From 33247aea6eb636f178055051a4f4c372ebe84643 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Mon, 11 Apr 2022 13:09:12 +0200 Subject: [PATCH 01/40] initial --- bugs.md | 2 ++ readme.md | 17 +++++++++++++++++ slides.md | 8 ++++++++ 3 files changed, 27 insertions(+) create mode 100644 bugs.md create mode 100644 readme.md create mode 100644 slides.md diff --git a/bugs.md b/bugs.md new file mode 100644 index 0000000..008c4f3 --- /dev/null +++ b/bugs.md @@ -0,0 +1,2 @@ +# Fixes for possible bugs you may have + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..deff29f --- /dev/null +++ b/readme.md @@ -0,0 +1,17 @@ +# Professional JavaScript Course -- [See course here](https://bytegrad.com/professional-javascript) + +**You can find the final code for each video in the project folders here**. Use that if you get stuck. + +There is also a bugs file, a slides file and a readme file (you're reading it right now). + +## Something is not working! What should I do? +Follow these steps: +1. Check if you made any typos. Also, check your terminals (if we're using those in the videos) to see if there are errors, fix those and try again. This will fix your issue 99% of the time so make sure you've done this thoroughly. +2. Still not working? Check the file in this repo called 'bugs' and see if your problem is present and follow the steps there. +3. Still not working? Copy and paste the final code of the video you're stuck at (you can find the final code in the project folders here). If you change the **package.json** file, run the command `npm install` to make sure you have the correct dependencies installed. +4. Still not working? Please open an issue for this GitHub repository with a clear description of what's not working. Please also include copy or a screenshot of your code. This makes it faster for me to help you. + +I'm online throughout most of the day and will help you as quickly as I can! In the meantime, I apologize for the inconvenience. Sometimes things break after recording the videos, but I try to be quick with any bugs that come up. + +## How can I contact you? +You can always contact me by email: support@bytegrad.com, but please open an issue for this GitHub repository to submit problems with the course. diff --git a/slides.md b/slides.md new file mode 100644 index 0000000..1c4cd5d --- /dev/null +++ b/slides.md @@ -0,0 +1,8 @@ +![](https://bytegrad.com/course-assets/css/slides/1.png) +![](https://bytegrad.com/course-assets/css/slides/2.png) +![](https://bytegrad.com/course-assets/css/slides/3.png) +![](https://bytegrad.com/course-assets/css/slides/4.png) +![](https://bytegrad.com/course-assets/css/slides/5.png) +![](https://bytegrad.com/course-assets/css/slides/6.png) +![](https://bytegrad.com/course-assets/css/slides/7.png?v=2) +![](https://bytegrad.com/course-assets/css/slides/8.png) \ No newline at end of file From 19fcd9b530302e06ba41b38ce6909483cc2edfb3 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Mon, 11 Apr 2022 13:10:17 +0200 Subject: [PATCH 02/40] better formatting --- readme.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/readme.md b/readme.md index deff29f..176ff8c 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,6 @@ # Professional JavaScript Course -- [See course here](https://bytegrad.com/professional-javascript) -**You can find the final code for each video in the project folders here**. Use that if you get stuck. - -There is also a bugs file, a slides file and a readme file (you're reading it right now). +**You can find the final code for each video in the project folders here**. Use that if you get stuck. There is also a bugs file, a slides file and a readme file (you're reading it right now). ## Something is not working! What should I do? Follow these steps: From c0ce6e48061bda576bead19749474e72eec338f6 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Tue, 26 Apr 2022 19:38:28 +0200 Subject: [PATCH 03/40] added files for videos 3 - 5 --- CorpComment/video 4/index.html | 94 +++++ CorpComment/video 4/script.js | 0 CorpComment/video 4/style.css | 671 +++++++++++++++++++++++++++++++++ CorpComment/video 5/index.html | 94 +++++ CorpComment/video 5/script.js | 0 CorpComment/video 5/style.css | 671 +++++++++++++++++++++++++++++++++ 6 files changed, 1530 insertions(+) create mode 100644 CorpComment/video 4/index.html create mode 100644 CorpComment/video 4/script.js create mode 100644 CorpComment/video 4/style.css create mode 100644 CorpComment/video 5/index.html create mode 100644 CorpComment/video 5/script.js create mode 100644 CorpComment/video 5/style.css diff --git a/CorpComment/video 4/index.html b/CorpComment/video 4/index.html new file mode 100644 index 0000000..816df8b --- /dev/null +++ b/CorpComment/video 4/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + afdasdf CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    + +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 4/script.js b/CorpComment/video 4/script.js new file mode 100644 index 0000000..e69de29 diff --git a/CorpComment/video 4/style.css b/CorpComment/video 4/style.css new file mode 100644 index 0000000..cc43415 --- /dev/null +++ b/CorpComment/video 4/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + margin-bottom: 11px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file diff --git a/CorpComment/video 5/index.html b/CorpComment/video 5/index.html new file mode 100644 index 0000000..816df8b --- /dev/null +++ b/CorpComment/video 5/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + afdasdf CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    + +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 5/script.js b/CorpComment/video 5/script.js new file mode 100644 index 0000000..e69de29 diff --git a/CorpComment/video 5/style.css b/CorpComment/video 5/style.css new file mode 100644 index 0000000..cc43415 --- /dev/null +++ b/CorpComment/video 5/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + margin-bottom: 11px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file From 94bbafd3cb94c2d5ecfc2e822b73aea98a5dbbf3 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Wed, 8 Jun 2022 21:31:52 +0200 Subject: [PATCH 04/40] save --- CorpComment/video 10/index.html | 94 + CorpComment/video 10/script.js | 130 + CorpComment/video 10/style.css | 671 ++ CorpComment/video 11/index.html | 94 + CorpComment/video 11/script.js | 146 + CorpComment/video 11/style.css | 671 ++ CorpComment/video 12/index.html | 94 + CorpComment/video 12/script.js | 177 + CorpComment/video 12/style.css | 671 ++ CorpComment/video 13/index.html | 94 + CorpComment/video 13/script.js | 207 + CorpComment/video 13/style.css | 671 ++ CorpComment/video 14/index.html | 94 + CorpComment/video 14/script.js | 215 + CorpComment/video 14/style.css | 671 ++ CorpComment/video 4/style.css | 2 +- CorpComment/video 5/style.css | 2 +- CorpComment/video 6/index.html | 94 + CorpComment/video 6/script.js | 19 + CorpComment/video 6/style.css | 671 ++ CorpComment/video 7/index.html | 94 + CorpComment/video 7/script.js | 98 + CorpComment/video 7/style.css | 671 ++ CorpComment/video 8/index.html | 94 + CorpComment/video 8/script.js | 100 + CorpComment/video 8/style.css | 671 ++ CorpComment/video 9/index.html | 94 + CorpComment/video 9/script.js | 137 + CorpComment/video 9/style.css | 671 ++ Modern JS Fundamentals/index.html | 26 + Modern JS Fundamentals/script.js | 67 + Modern JS Fundamentals/style.css | 7 + rmtDev/video 16/index.css | 1130 +++ rmtDev/video 16/index.html | 121 + rmtDev/video 16/index.js | 0 rmtDev/video 17/index.css | 1130 +++ rmtDev/video 17/index.html | 121 + rmtDev/video 17/index.js | 99 + rmtDev/video 18/index.css | 1130 +++ rmtDev/video 18/index.html | 121 + rmtDev/video 18/index.js | 203 + rmtDev/video 19/index.css | 1130 +++ rmtDev/video 19/index.html | 121 + rmtDev/video 19/index.js | 4 + rmtDev/video 19/src/common.js | 20 + rmtDev/video 19/src/components/Error.js | 0 rmtDev/video 19/src/components/JobList.js | 105 + rmtDev/video 19/src/components/Search.js | 84 + rmtDev/video 19/src/components/Spinner.js | 0 rmtDev/video 20/index.css | 1130 +++ rmtDev/video 20/index.html | 121 + rmtDev/video 20/index.js | 4 + rmtDev/video 20/src/common.js | 20 + rmtDev/video 20/src/components/Error.js | 14 + rmtDev/video 20/src/components/JobList.js | 105 + rmtDev/video 20/src/components/Search.js | 82 + rmtDev/video 20/src/components/Spinner.js | 11 + rmtDev/video 21/index.css | 1130 +++ rmtDev/video 21/index.html | 121 + rmtDev/video 21/index.js | 4 + rmtDev/video 21/src/common.js | 25 + rmtDev/video 21/src/components/Error.js | 15 + rmtDev/video 21/src/components/JobList.js | 106 + rmtDev/video 21/src/components/Search.js | 83 + rmtDev/video 21/src/components/Spinner.js | 11 + rmtDev/video 22/index.css | 1130 +++ rmtDev/video 22/index.html | 121 + rmtDev/video 22/index.js | 5 + rmtDev/video 22/src/common.js | 25 + rmtDev/video 22/src/components/Error.js | 15 + rmtDev/video 22/src/components/JobDetails.js | 62 + rmtDev/video 22/src/components/JobList.js | 82 + rmtDev/video 22/src/components/Search.js | 62 + rmtDev/video 22/src/components/Spinner.js | 11 + rmtDev/video 23/index.css | 1130 +++ rmtDev/video 23/index.html | 121 + rmtDev/video 23/index.js | 5 + rmtDev/video 23/src/common.js | 25 + rmtDev/video 23/src/components/Error.js | 15 + rmtDev/video 23/src/components/JobDetails.js | 62 + rmtDev/video 23/src/components/JobList.js | 84 + rmtDev/video 23/src/components/Search.js | 64 + rmtDev/video 23/src/components/Spinner.js | 11 + rmtDev/video 24/index.css | 1130 +++ rmtDev/video 24/index.html | 121 + rmtDev/video 24/index.js | 5 + rmtDev/video 24/src/common.js | 25 + rmtDev/video 24/src/components/Error.js | 15 + rmtDev/video 24/src/components/JobDetails.js | 62 + rmtDev/video 24/src/components/JobList.js | 83 + rmtDev/video 24/src/components/Search.js | 62 + rmtDev/video 24/src/components/Spinner.js | 11 + rmtDev/video 25/index.css | 1130 +++ rmtDev/video 25/index.html | 121 + rmtDev/video 25/index.js | 5 + rmtDev/video 25/src/common.js | 37 + rmtDev/video 25/src/components/Error.js | 15 + rmtDev/video 25/src/components/JobDetails.js | 62 + rmtDev/video 25/src/components/JobList.js | 79 + rmtDev/video 25/src/components/Search.js | 58 + rmtDev/video 25/src/components/Spinner.js | 11 + rmtDev/video 26/index.css | 1130 +++ rmtDev/video 26/index.html | 121 + rmtDev/video 26/index.js | 6 + rmtDev/video 26/src/common.js | 37 + rmtDev/video 26/src/components/Error.js | 15 + rmtDev/video 26/src/components/JobDetails.js | 62 + rmtDev/video 26/src/components/JobList.js | 79 + rmtDev/video 26/src/components/Search.js | 58 + rmtDev/video 26/src/components/Sorting.js | 27 + rmtDev/video 26/src/components/Spinner.js | 11 + rmtDev/video 27/index.css | 1130 +++ rmtDev/video 27/index.html | 121 + rmtDev/video 27/index.js | 6 + rmtDev/video 27/src/common.js | 42 + rmtDev/video 27/src/components/Error.js | 15 + rmtDev/video 27/src/components/JobDetails.js | 62 + rmtDev/video 27/src/components/JobList.js | 80 + rmtDev/video 27/src/components/Search.js | 62 + rmtDev/video 27/src/components/Sorting.js | 25 + rmtDev/video 27/src/components/Spinner.js | 11 + rmtDev/video 28/index.css | 1130 +++ rmtDev/video 28/index.html | 121 + rmtDev/video 28/index.js | 6 + rmtDev/video 28/src/common.js | 42 + rmtDev/video 28/src/components/Error.js | 15 + rmtDev/video 28/src/components/JobDetails.js | 62 + rmtDev/video 28/src/components/JobList.js | 84 + rmtDev/video 28/src/components/Search.js | 62 + rmtDev/video 28/src/components/Sorting.js | 44 + rmtDev/video 28/src/components/Spinner.js | 11 + rmtDev/video 29/index.css | 1130 +++ rmtDev/video 29/index.html | 121 + rmtDev/video 29/index.js | 7 + rmtDev/video 29/src/common.js | 44 + rmtDev/video 29/src/components/Error.js | 15 + rmtDev/video 29/src/components/JobDetails.js | 62 + rmtDev/video 29/src/components/JobList.js | 85 + rmtDev/video 29/src/components/Pagination.js | 58 + rmtDev/video 29/src/components/Search.js | 73 + rmtDev/video 29/src/components/Sorting.js | 51 + rmtDev/video 29/src/components/Spinner.js | 11 + rmtDev/video 30/index.css | 1130 +++ rmtDev/video 30/index.html | 121 + rmtDev/video 30/index.js | 8 + rmtDev/video 30/src/common.js | 44 + rmtDev/video 30/src/components/Error.js | 15 + rmtDev/video 30/src/components/JobDetails.js | 62 + rmtDev/video 30/src/components/JobList.js | 88 + rmtDev/video 30/src/components/Pagination.js | 58 + rmtDev/video 30/src/components/Router.js | 40 + rmtDev/video 30/src/components/Search.js | 73 + rmtDev/video 30/src/components/Sorting.js | 51 + rmtDev/video 30/src/components/Spinner.js | 11 + rmtDev/video 31/index.css | 1130 +++ rmtDev/video 31/index.html | 121 + rmtDev/video 31/index.js | 8 + rmtDev/video 31/src/common.js | 45 + rmtDev/video 31/src/components/Error.js | 15 + rmtDev/video 31/src/components/JobDetails.js | 62 + rmtDev/video 31/src/components/JobList.js | 91 + rmtDev/video 31/src/components/Pagination.js | 58 + rmtDev/video 31/src/components/Router.js | 45 + rmtDev/video 31/src/components/Search.js | 73 + rmtDev/video 31/src/components/Sorting.js | 51 + rmtDev/video 31/src/components/Spinner.js | 11 + rmtDev/video 32/index.css | 1130 +++ rmtDev/video 32/index.html | 121 + rmtDev/video 32/index.js | 9 + rmtDev/video 32/src/common.js | 46 + rmtDev/video 32/src/components/Bookmarks.js | 45 + rmtDev/video 32/src/components/Error.js | 15 + rmtDev/video 32/src/components/JobDetails.js | 62 + rmtDev/video 32/src/components/JobList.js | 104 + rmtDev/video 32/src/components/Pagination.js | 58 + rmtDev/video 32/src/components/Router.js | 45 + rmtDev/video 32/src/components/Search.js | 73 + rmtDev/video 32/src/components/Sorting.js | 51 + rmtDev/video 32/src/components/Spinner.js | 11 + rmtDev/video 33/index.css | 1130 +++ rmtDev/video 33/index.html | 121 + rmtDev/video 33/index.js | 10 + rmtDev/video 33/src/common.js | 46 + rmtDev/video 33/src/components/Bookmarks.js | 48 + rmtDev/video 33/src/components/Error.js | 15 + rmtDev/video 33/src/components/JobDetails.js | 62 + rmtDev/video 33/src/components/JobList.js | 105 + rmtDev/video 33/src/components/Pagination.js | 58 + rmtDev/video 33/src/components/Router.js | 45 + rmtDev/video 33/src/components/Search.js | 73 + rmtDev/video 33/src/components/Sorting.js | 51 + rmtDev/video 33/src/components/Spinner.js | 11 + rmtDev/video 33/src/components/Storage.js | 8 + rmtDev/video 34/index.css | 1130 +++ rmtDev/video 34/index.html | 121 + rmtDev/video 34/index.js | 10 + rmtDev/video 34/src/common.js | 46 + rmtDev/video 34/src/components/Bookmarks.js | 51 + rmtDev/video 34/src/components/Error.js | 15 + rmtDev/video 34/src/components/JobDetails.js | 63 + rmtDev/video 34/src/components/JobList.js | 105 + rmtDev/video 34/src/components/Pagination.js | 58 + rmtDev/video 34/src/components/Router.js | 52 + rmtDev/video 34/src/components/Search.js | 73 + rmtDev/video 34/src/components/Sorting.js | 51 + rmtDev/video 34/src/components/Spinner.js | 11 + rmtDev/video 34/src/components/Storage.js | 8 + rmtDev/video 35/babel.config.js | 11 + rmtDev/video 35/dist/index.html | 121 + rmtDev/video 35/dist/main.css | 1 + rmtDev/video 35/dist/main.js | 2 + rmtDev/video 35/dist/main.js.LICENSE.txt | 1 + rmtDev/video 35/package-lock.json | 8346 ++++++++++++++++++ rmtDev/video 35/package.json | 28 + rmtDev/video 35/postcss.config.js | 7 + rmtDev/video 35/src/common.js | 46 + rmtDev/video 35/src/components/Bookmarks.js | 51 + rmtDev/video 35/src/components/Error.js | 15 + rmtDev/video 35/src/components/JobDetails.js | 63 + rmtDev/video 35/src/components/JobList.js | 105 + rmtDev/video 35/src/components/Pagination.js | 58 + rmtDev/video 35/src/components/Router.js | 52 + rmtDev/video 35/src/components/Search.js | 73 + rmtDev/video 35/src/components/Sorting.js | 51 + rmtDev/video 35/src/components/Spinner.js | 11 + rmtDev/video 35/src/components/Storage.js | 8 + rmtDev/video 35/src/index.css | 1130 +++ rmtDev/video 35/src/index.html | 121 + rmtDev/video 35/src/index.js | 12 + rmtDev/video 35/webpack.config.js | 27 + 230 files changed, 48211 insertions(+), 2 deletions(-) create mode 100644 CorpComment/video 10/index.html create mode 100644 CorpComment/video 10/script.js create mode 100644 CorpComment/video 10/style.css create mode 100644 CorpComment/video 11/index.html create mode 100644 CorpComment/video 11/script.js create mode 100644 CorpComment/video 11/style.css create mode 100644 CorpComment/video 12/index.html create mode 100644 CorpComment/video 12/script.js create mode 100644 CorpComment/video 12/style.css create mode 100644 CorpComment/video 13/index.html create mode 100644 CorpComment/video 13/script.js create mode 100644 CorpComment/video 13/style.css create mode 100644 CorpComment/video 14/index.html create mode 100644 CorpComment/video 14/script.js create mode 100644 CorpComment/video 14/style.css create mode 100644 CorpComment/video 6/index.html create mode 100644 CorpComment/video 6/script.js create mode 100644 CorpComment/video 6/style.css create mode 100644 CorpComment/video 7/index.html create mode 100644 CorpComment/video 7/script.js create mode 100644 CorpComment/video 7/style.css create mode 100644 CorpComment/video 8/index.html create mode 100644 CorpComment/video 8/script.js create mode 100644 CorpComment/video 8/style.css create mode 100644 CorpComment/video 9/index.html create mode 100644 CorpComment/video 9/script.js create mode 100644 CorpComment/video 9/style.css create mode 100644 Modern JS Fundamentals/index.html create mode 100644 Modern JS Fundamentals/script.js create mode 100644 Modern JS Fundamentals/style.css create mode 100644 rmtDev/video 16/index.css create mode 100644 rmtDev/video 16/index.html create mode 100644 rmtDev/video 16/index.js create mode 100644 rmtDev/video 17/index.css create mode 100644 rmtDev/video 17/index.html create mode 100644 rmtDev/video 17/index.js create mode 100644 rmtDev/video 18/index.css create mode 100644 rmtDev/video 18/index.html create mode 100644 rmtDev/video 18/index.js create mode 100644 rmtDev/video 19/index.css create mode 100644 rmtDev/video 19/index.html create mode 100644 rmtDev/video 19/index.js create mode 100644 rmtDev/video 19/src/common.js create mode 100644 rmtDev/video 19/src/components/Error.js create mode 100644 rmtDev/video 19/src/components/JobList.js create mode 100644 rmtDev/video 19/src/components/Search.js create mode 100644 rmtDev/video 19/src/components/Spinner.js create mode 100644 rmtDev/video 20/index.css create mode 100644 rmtDev/video 20/index.html create mode 100644 rmtDev/video 20/index.js create mode 100644 rmtDev/video 20/src/common.js create mode 100644 rmtDev/video 20/src/components/Error.js create mode 100644 rmtDev/video 20/src/components/JobList.js create mode 100644 rmtDev/video 20/src/components/Search.js create mode 100644 rmtDev/video 20/src/components/Spinner.js create mode 100644 rmtDev/video 21/index.css create mode 100644 rmtDev/video 21/index.html create mode 100644 rmtDev/video 21/index.js create mode 100644 rmtDev/video 21/src/common.js create mode 100644 rmtDev/video 21/src/components/Error.js create mode 100644 rmtDev/video 21/src/components/JobList.js create mode 100644 rmtDev/video 21/src/components/Search.js create mode 100644 rmtDev/video 21/src/components/Spinner.js create mode 100644 rmtDev/video 22/index.css create mode 100644 rmtDev/video 22/index.html create mode 100644 rmtDev/video 22/index.js create mode 100644 rmtDev/video 22/src/common.js create mode 100644 rmtDev/video 22/src/components/Error.js create mode 100644 rmtDev/video 22/src/components/JobDetails.js create mode 100644 rmtDev/video 22/src/components/JobList.js create mode 100644 rmtDev/video 22/src/components/Search.js create mode 100644 rmtDev/video 22/src/components/Spinner.js create mode 100644 rmtDev/video 23/index.css create mode 100644 rmtDev/video 23/index.html create mode 100644 rmtDev/video 23/index.js create mode 100644 rmtDev/video 23/src/common.js create mode 100644 rmtDev/video 23/src/components/Error.js create mode 100644 rmtDev/video 23/src/components/JobDetails.js create mode 100644 rmtDev/video 23/src/components/JobList.js create mode 100644 rmtDev/video 23/src/components/Search.js create mode 100644 rmtDev/video 23/src/components/Spinner.js create mode 100644 rmtDev/video 24/index.css create mode 100644 rmtDev/video 24/index.html create mode 100644 rmtDev/video 24/index.js create mode 100644 rmtDev/video 24/src/common.js create mode 100644 rmtDev/video 24/src/components/Error.js create mode 100644 rmtDev/video 24/src/components/JobDetails.js create mode 100644 rmtDev/video 24/src/components/JobList.js create mode 100644 rmtDev/video 24/src/components/Search.js create mode 100644 rmtDev/video 24/src/components/Spinner.js create mode 100644 rmtDev/video 25/index.css create mode 100644 rmtDev/video 25/index.html create mode 100644 rmtDev/video 25/index.js create mode 100644 rmtDev/video 25/src/common.js create mode 100644 rmtDev/video 25/src/components/Error.js create mode 100644 rmtDev/video 25/src/components/JobDetails.js create mode 100644 rmtDev/video 25/src/components/JobList.js create mode 100644 rmtDev/video 25/src/components/Search.js create mode 100644 rmtDev/video 25/src/components/Spinner.js create mode 100644 rmtDev/video 26/index.css create mode 100644 rmtDev/video 26/index.html create mode 100644 rmtDev/video 26/index.js create mode 100644 rmtDev/video 26/src/common.js create mode 100644 rmtDev/video 26/src/components/Error.js create mode 100644 rmtDev/video 26/src/components/JobDetails.js create mode 100644 rmtDev/video 26/src/components/JobList.js create mode 100644 rmtDev/video 26/src/components/Search.js create mode 100644 rmtDev/video 26/src/components/Sorting.js create mode 100644 rmtDev/video 26/src/components/Spinner.js create mode 100644 rmtDev/video 27/index.css create mode 100644 rmtDev/video 27/index.html create mode 100644 rmtDev/video 27/index.js create mode 100644 rmtDev/video 27/src/common.js create mode 100644 rmtDev/video 27/src/components/Error.js create mode 100644 rmtDev/video 27/src/components/JobDetails.js create mode 100644 rmtDev/video 27/src/components/JobList.js create mode 100644 rmtDev/video 27/src/components/Search.js create mode 100644 rmtDev/video 27/src/components/Sorting.js create mode 100644 rmtDev/video 27/src/components/Spinner.js create mode 100644 rmtDev/video 28/index.css create mode 100644 rmtDev/video 28/index.html create mode 100644 rmtDev/video 28/index.js create mode 100644 rmtDev/video 28/src/common.js create mode 100644 rmtDev/video 28/src/components/Error.js create mode 100644 rmtDev/video 28/src/components/JobDetails.js create mode 100644 rmtDev/video 28/src/components/JobList.js create mode 100644 rmtDev/video 28/src/components/Search.js create mode 100644 rmtDev/video 28/src/components/Sorting.js create mode 100644 rmtDev/video 28/src/components/Spinner.js create mode 100644 rmtDev/video 29/index.css create mode 100644 rmtDev/video 29/index.html create mode 100644 rmtDev/video 29/index.js create mode 100644 rmtDev/video 29/src/common.js create mode 100644 rmtDev/video 29/src/components/Error.js create mode 100644 rmtDev/video 29/src/components/JobDetails.js create mode 100644 rmtDev/video 29/src/components/JobList.js create mode 100644 rmtDev/video 29/src/components/Pagination.js create mode 100644 rmtDev/video 29/src/components/Search.js create mode 100644 rmtDev/video 29/src/components/Sorting.js create mode 100644 rmtDev/video 29/src/components/Spinner.js create mode 100644 rmtDev/video 30/index.css create mode 100644 rmtDev/video 30/index.html create mode 100644 rmtDev/video 30/index.js create mode 100644 rmtDev/video 30/src/common.js create mode 100644 rmtDev/video 30/src/components/Error.js create mode 100644 rmtDev/video 30/src/components/JobDetails.js create mode 100644 rmtDev/video 30/src/components/JobList.js create mode 100644 rmtDev/video 30/src/components/Pagination.js create mode 100644 rmtDev/video 30/src/components/Router.js create mode 100644 rmtDev/video 30/src/components/Search.js create mode 100644 rmtDev/video 30/src/components/Sorting.js create mode 100644 rmtDev/video 30/src/components/Spinner.js create mode 100644 rmtDev/video 31/index.css create mode 100644 rmtDev/video 31/index.html create mode 100644 rmtDev/video 31/index.js create mode 100644 rmtDev/video 31/src/common.js create mode 100644 rmtDev/video 31/src/components/Error.js create mode 100644 rmtDev/video 31/src/components/JobDetails.js create mode 100644 rmtDev/video 31/src/components/JobList.js create mode 100644 rmtDev/video 31/src/components/Pagination.js create mode 100644 rmtDev/video 31/src/components/Router.js create mode 100644 rmtDev/video 31/src/components/Search.js create mode 100644 rmtDev/video 31/src/components/Sorting.js create mode 100644 rmtDev/video 31/src/components/Spinner.js create mode 100644 rmtDev/video 32/index.css create mode 100644 rmtDev/video 32/index.html create mode 100644 rmtDev/video 32/index.js create mode 100644 rmtDev/video 32/src/common.js create mode 100644 rmtDev/video 32/src/components/Bookmarks.js create mode 100644 rmtDev/video 32/src/components/Error.js create mode 100644 rmtDev/video 32/src/components/JobDetails.js create mode 100644 rmtDev/video 32/src/components/JobList.js create mode 100644 rmtDev/video 32/src/components/Pagination.js create mode 100644 rmtDev/video 32/src/components/Router.js create mode 100644 rmtDev/video 32/src/components/Search.js create mode 100644 rmtDev/video 32/src/components/Sorting.js create mode 100644 rmtDev/video 32/src/components/Spinner.js create mode 100644 rmtDev/video 33/index.css create mode 100644 rmtDev/video 33/index.html create mode 100644 rmtDev/video 33/index.js create mode 100644 rmtDev/video 33/src/common.js create mode 100644 rmtDev/video 33/src/components/Bookmarks.js create mode 100644 rmtDev/video 33/src/components/Error.js create mode 100644 rmtDev/video 33/src/components/JobDetails.js create mode 100644 rmtDev/video 33/src/components/JobList.js create mode 100644 rmtDev/video 33/src/components/Pagination.js create mode 100644 rmtDev/video 33/src/components/Router.js create mode 100644 rmtDev/video 33/src/components/Search.js create mode 100644 rmtDev/video 33/src/components/Sorting.js create mode 100644 rmtDev/video 33/src/components/Spinner.js create mode 100644 rmtDev/video 33/src/components/Storage.js create mode 100644 rmtDev/video 34/index.css create mode 100644 rmtDev/video 34/index.html create mode 100644 rmtDev/video 34/index.js create mode 100644 rmtDev/video 34/src/common.js create mode 100644 rmtDev/video 34/src/components/Bookmarks.js create mode 100644 rmtDev/video 34/src/components/Error.js create mode 100644 rmtDev/video 34/src/components/JobDetails.js create mode 100644 rmtDev/video 34/src/components/JobList.js create mode 100644 rmtDev/video 34/src/components/Pagination.js create mode 100644 rmtDev/video 34/src/components/Router.js create mode 100644 rmtDev/video 34/src/components/Search.js create mode 100644 rmtDev/video 34/src/components/Sorting.js create mode 100644 rmtDev/video 34/src/components/Spinner.js create mode 100644 rmtDev/video 34/src/components/Storage.js create mode 100644 rmtDev/video 35/babel.config.js create mode 100644 rmtDev/video 35/dist/index.html create mode 100644 rmtDev/video 35/dist/main.css create mode 100644 rmtDev/video 35/dist/main.js create mode 100644 rmtDev/video 35/dist/main.js.LICENSE.txt create mode 100644 rmtDev/video 35/package-lock.json create mode 100644 rmtDev/video 35/package.json create mode 100644 rmtDev/video 35/postcss.config.js create mode 100644 rmtDev/video 35/src/common.js create mode 100644 rmtDev/video 35/src/components/Bookmarks.js create mode 100644 rmtDev/video 35/src/components/Error.js create mode 100644 rmtDev/video 35/src/components/JobDetails.js create mode 100644 rmtDev/video 35/src/components/JobList.js create mode 100644 rmtDev/video 35/src/components/Pagination.js create mode 100644 rmtDev/video 35/src/components/Router.js create mode 100644 rmtDev/video 35/src/components/Search.js create mode 100644 rmtDev/video 35/src/components/Sorting.js create mode 100644 rmtDev/video 35/src/components/Spinner.js create mode 100644 rmtDev/video 35/src/components/Storage.js create mode 100644 rmtDev/video 35/src/index.css create mode 100644 rmtDev/video 35/src/index.html create mode 100644 rmtDev/video 35/src/index.js create mode 100644 rmtDev/video 35/webpack.config.js diff --git a/CorpComment/video 10/index.html b/CorpComment/video 10/index.html new file mode 100644 index 0000000..b4a3c9b --- /dev/null +++ b/CorpComment/video 10/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    +
    +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 10/script.js b/CorpComment/video 10/script.js new file mode 100644 index 0000000..8a2320b --- /dev/null +++ b/CorpComment/video 10/script.js @@ -0,0 +1,130 @@ +// -- GLOBAL -- +const MAX_CHARS = 150; + +const textareaEl = document.querySelector('.form__textarea'); +const counterEl = document.querySelector('.counter'); +const formEl = document.querySelector('.form'); +const feedbackListEl = document.querySelector('.feedbacks'); +const submitBtnEl = document.querySelector('.submit-btn'); +const spinnerEl = document.querySelector('.spinner'); + +const renderFeedbackItem = feedbackItem => { + // new feedback item HTML + const feedbackItemHTML = ` + + `; + + // insert new feedback item in list + feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); +}; + + +// -- COUNTER COMPONENT -- +const inputHandler = () => { + // determine maximum number of characters + const maxNrChars = MAX_CHARS; + + // determine number of characters currently typed + const nrCharsTyped = textareaEl.value.length; + + // calculate number of characters left (maximum minus currently typed) + const charsLeft = maxNrChars - nrCharsTyped; + + // show number of characters left + counterEl.textContent = charsLeft; +}; + +textareaEl.addEventListener('input', inputHandler); + + +// -- FORM COMPONENT -- +const showVisualIndicator = textCheck => { + const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; + + // show valid indicator + formEl.classList.add(className); + + // remove visual indicator + setTimeout(() => { + formEl.classList.remove(className); + }, 2000); +}; + +const submitHandler = event => { + // prevent default browser action (submitting form data to 'action'-address and refreshing page) + event.preventDefault(); + + // get text from textarea + const text = textareaEl.value; + + // validate text (e.g. check if #hashtag is present and text is long enough) + if (text.includes('#') && text.length >= 5) { + showVisualIndicator('valid'); + } else { + showVisualIndicator('invalid'); + + // focus textarea + textareaEl.focus(); + + // stop this function execution + return; + } + + // we have text, now extract other info from text + const hashtag = text.split(' ').find(word => word.includes('#')); + const company = hashtag.substring(1); + const badgeLetter = company.substring(0, 1).toUpperCase(); + const upvoteCount = 0; + const daysAgo = 0; + + // create feedback item object + const feedbackItem = { + upvoteCount: upvoteCount, + company: company, + badgeLetter: badgeLetter, + daysAgo: daysAgo, + text: text + }; + + // render feedback item + renderFeedbackItem(feedbackItem); + + // clear textarea + textareaEl.value = ''; + + // blur submit button + submitBtnEl.blur(); + + // reset counter + counterEl.textContent = MAX_CHARS; +}; + +formEl.addEventListener('submit', submitHandler); + + +// -- FEEDBACK LIST COMPONENT -- +fetch('https://bytegrad.com/course-assets/js/1/api/feedbacks') + .then(response => response.json()) + .then(data => { + // remove spinner + spinnerEl.remove(); + + // iterate over each element in feedbacks array and render it in list + data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); + }) + .catch(error => { + feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; + }); diff --git a/CorpComment/video 10/style.css b/CorpComment/video 10/style.css new file mode 100644 index 0000000..fd4b37f --- /dev/null +++ b/CorpComment/video 10/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; + margin-bottom: 11px; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file diff --git a/CorpComment/video 11/index.html b/CorpComment/video 11/index.html new file mode 100644 index 0000000..b4a3c9b --- /dev/null +++ b/CorpComment/video 11/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    +
    +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 11/script.js b/CorpComment/video 11/script.js new file mode 100644 index 0000000..3d1bf44 --- /dev/null +++ b/CorpComment/video 11/script.js @@ -0,0 +1,146 @@ +// -- GLOBAL -- +const MAX_CHARS = 150; +const BASE_API_URL = 'https://bytegrad.com/course-assets/js/1/api'; + +const textareaEl = document.querySelector('.form__textarea'); +const counterEl = document.querySelector('.counter'); +const formEl = document.querySelector('.form'); +const feedbackListEl = document.querySelector('.feedbacks'); +const submitBtnEl = document.querySelector('.submit-btn'); +const spinnerEl = document.querySelector('.spinner'); + +const renderFeedbackItem = feedbackItem => { + // new feedback item HTML + const feedbackItemHTML = ` + + `; + + // insert new feedback item in list + feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); +}; + + +// -- COUNTER COMPONENT -- +const inputHandler = () => { + // determine maximum number of characters + const maxNrChars = MAX_CHARS; + + // determine number of characters currently typed + const nrCharsTyped = textareaEl.value.length; + + // calculate number of characters left (maximum minus currently typed) + const charsLeft = maxNrChars - nrCharsTyped; + + // show number of characters left + counterEl.textContent = charsLeft; +}; + +textareaEl.addEventListener('input', inputHandler); + + +// -- FORM COMPONENT -- +const showVisualIndicator = textCheck => { + const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; + + // show valid indicator + formEl.classList.add(className); + + // remove visual indicator + setTimeout(() => { + formEl.classList.remove(className); + }, 2000); +}; + +const submitHandler = event => { + // prevent default browser action (submitting form data to 'action'-address and refreshing page) + event.preventDefault(); + + // get text from textarea + const text = textareaEl.value; + + // validate text (e.g. check if #hashtag is present and text is long enough) + if (text.includes('#') && text.length >= 5) { + showVisualIndicator('valid'); + } else { + showVisualIndicator('invalid'); + + // focus textarea + textareaEl.focus(); + + // stop this function execution + return; + } + + // we have text, now extract other info from text + const hashtag = text.split(' ').find(word => word.includes('#')); + const company = hashtag.substring(1); + const badgeLetter = company.substring(0, 1).toUpperCase(); + const upvoteCount = 0; + const daysAgo = 0; + + // render feedback item in list + const feedbackItem = { + upvoteCount: upvoteCount, + company: company, + badgeLetter: badgeLetter, + daysAgo: daysAgo, + text: text + }; + renderFeedbackItem(feedbackItem); + + // send feedback item to server + fetch(`${BASE_API_URL}/feedbacks`, { + method: 'POST', + body: JSON.stringify(feedbackItem), + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + }).then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + console.log('Successfully submitted'); + }).catch(error => console.log(error)); + + // clear textarea + textareaEl.value = ''; + + // blur submit button + submitBtnEl.blur(); + + // reset counter + counterEl.textContent = MAX_CHARS; +}; + +formEl.addEventListener('submit', submitHandler); + + +// -- FEEDBACK LIST COMPONENT -- +fetch(`${BASE_API_URL}/feedbacks`) + .then(response => response.json()) + .then(data => { + // remove spinner + spinnerEl.remove(); + + // iterate over each element in feedbacks array and render it in list + data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); + }) + .catch(error => { + feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; + }); diff --git a/CorpComment/video 11/style.css b/CorpComment/video 11/style.css new file mode 100644 index 0000000..5746913 --- /dev/null +++ b/CorpComment/video 11/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + margin-bottom: 11px; + max-width: max-content; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file diff --git a/CorpComment/video 12/index.html b/CorpComment/video 12/index.html new file mode 100644 index 0000000..b4a3c9b --- /dev/null +++ b/CorpComment/video 12/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    +
    +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 12/script.js b/CorpComment/video 12/script.js new file mode 100644 index 0000000..83d0ecf --- /dev/null +++ b/CorpComment/video 12/script.js @@ -0,0 +1,177 @@ +// -- GLOBAL -- +const MAX_CHARS = 150; +const BASE_API_URL = 'https://bytegrad.com/course-assets/js/1/api'; + +const textareaEl = document.querySelector('.form__textarea'); +const counterEl = document.querySelector('.counter'); +const formEl = document.querySelector('.form'); +const feedbackListEl = document.querySelector('.feedbacks'); +const submitBtnEl = document.querySelector('.submit-btn'); +const spinnerEl = document.querySelector('.spinner'); + +const renderFeedbackItem = feedbackItem => { + // new feedback item HTML + const feedbackItemHTML = ` + + `; + + // insert new feedback item in list + feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); +}; + + +// -- COUNTER COMPONENT -- +const inputHandler = () => { + // determine maximum number of characters + const maxNrChars = MAX_CHARS; + + // determine number of characters currently typed + const nrCharsTyped = textareaEl.value.length; + + // calculate number of characters left (maximum minus currently typed) + const charsLeft = maxNrChars - nrCharsTyped; + + // show number of characters left + counterEl.textContent = charsLeft; +}; + +textareaEl.addEventListener('input', inputHandler); + + +// -- FORM COMPONENT -- +const showVisualIndicator = textCheck => { + const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; + + // show valid indicator + formEl.classList.add(className); + + // remove visual indicator + setTimeout(() => { + formEl.classList.remove(className); + }, 2000); +}; + +const submitHandler = event => { + // prevent default browser action (submitting form data to 'action'-address and refreshing page) + event.preventDefault(); + + // get text from textarea + const text = textareaEl.value; + + // validate text (e.g. check if #hashtag is present and text is long enough) + if (text.includes('#') && text.length >= 5) { + showVisualIndicator('valid'); + } else { + showVisualIndicator('invalid'); + + // focus textarea + textareaEl.focus(); + + // stop this function execution + return; + } + + // we have text, now extract other info from text + const hashtag = text.split(' ').find(word => word.includes('#')); + const company = hashtag.substring(1); + const badgeLetter = company.substring(0, 1).toUpperCase(); + const upvoteCount = 0; + const daysAgo = 0; + + // render feedback item in list + const feedbackItem = { + upvoteCount: upvoteCount, + company: company, + badgeLetter: badgeLetter, + daysAgo: daysAgo, + text: text + }; + renderFeedbackItem(feedbackItem); + + // send feedback item to server + fetch(`${BASE_API_URL}/feedbacks`, { + method: 'POST', + body: JSON.stringify(feedbackItem), + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + }).then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + console.log('Successfully submitted'); + }).catch(error => console.log(error)); + + // clear textarea + textareaEl.value = ''; + + // blur submit button + submitBtnEl.blur(); + + // reset counter + counterEl.textContent = MAX_CHARS; +}; + +formEl.addEventListener('submit', submitHandler); + + +// -- FEEDBACK LIST COMPONENT -- +const clickHandler = event => { + // get clicked HTML-element + const clickedEl = event.target; + + // determine if user intended to upvote or expand + const upvoteIntention = clickedEl.className.includes('upvote'); + + // run the appropriate logic + if (upvoteIntention) { + // get the closest upvote button + const upvoteBtnEl = clickedEl.closest('.upvote'); + + // disable upvote button (prevent double-clicks, spam) + upvoteBtnEl.disabled = true; + + // select the upvote count element within the upvote button + const upvoteCountEl = upvoteBtnEl.querySelector('.upvote__count'); + + // get currently displayed upvote count as number (+) + let upvoteCount = +upvoteCountEl.textContent; + + // set upvote count incremented by 1 + upvoteCountEl.textContent = ++upvoteCount; + } else { + // expand the clicked feedback item + clickedEl.closest('.feedback').classList.toggle('feedback--expand'); + } +}; + +feedbackListEl.addEventListener('click', clickHandler); + +fetch(`${BASE_API_URL}/feedbacks`) + .then(response => response.json()) + .then(data => { + // remove spinner + spinnerEl.remove(); + + // iterate over each element in feedbacks array and render it in list + data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); + }) + .catch(error => { + feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; + }); diff --git a/CorpComment/video 12/style.css b/CorpComment/video 12/style.css new file mode 100644 index 0000000..fd4b37f --- /dev/null +++ b/CorpComment/video 12/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; + margin-bottom: 11px; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file diff --git a/CorpComment/video 13/index.html b/CorpComment/video 13/index.html new file mode 100644 index 0000000..b4a3c9b --- /dev/null +++ b/CorpComment/video 13/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    +
    +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 13/script.js b/CorpComment/video 13/script.js new file mode 100644 index 0000000..4436179 --- /dev/null +++ b/CorpComment/video 13/script.js @@ -0,0 +1,207 @@ +// -- GLOBAL -- +const MAX_CHARS = 150; +const BASE_API_URL = 'https://bytegrad.com/course-assets/js/1/api'; + +const textareaEl = document.querySelector('.form__textarea'); +const counterEl = document.querySelector('.counter'); +const formEl = document.querySelector('.form'); +const feedbackListEl = document.querySelector('.feedbacks'); +const submitBtnEl = document.querySelector('.submit-btn'); +const spinnerEl = document.querySelector('.spinner'); +const hashtagListEl = document.querySelector('.hashtags'); + +const renderFeedbackItem = feedbackItem => { + // new feedback item HTML + const feedbackItemHTML = ` + + `; + + // insert new feedback item in list + feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); +}; + + +// -- COUNTER COMPONENT -- +const inputHandler = () => { + // determine maximum number of characters + const maxNrChars = MAX_CHARS; + + // determine number of characters currently typed + const nrCharsTyped = textareaEl.value.length; + + // calculate number of characters left (maximum minus currently typed) + const charsLeft = maxNrChars - nrCharsTyped; + + // show number of characters left + counterEl.textContent = charsLeft; +}; + +textareaEl.addEventListener('input', inputHandler); + + +// -- FORM COMPONENT -- +const showVisualIndicator = textCheck => { + const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; + + // show valid indicator + formEl.classList.add(className); + + // remove visual indicator + setTimeout(() => { + formEl.classList.remove(className); + }, 2000); +}; + +const submitHandler = event => { + // prevent default browser action (submitting form data to 'action'-address and refreshing page) + event.preventDefault(); + + // get text from textarea + const text = textareaEl.value; + + // validate text (e.g. check if #hashtag is present and text is long enough) + if (text.includes('#') && text.length >= 5) { + showVisualIndicator('valid'); + } else { + showVisualIndicator('invalid'); + + // focus textarea + textareaEl.focus(); + + // stop this function execution + return; + } + + // we have text, now extract other info from text + const hashtag = text.split(' ').find(word => word.includes('#')); + const company = hashtag.substring(1); + const badgeLetter = company.substring(0, 1).toUpperCase(); + const upvoteCount = 0; + const daysAgo = 0; + + // render feedback item in list + const feedbackItem = { + upvoteCount: upvoteCount, + company: company, + badgeLetter: badgeLetter, + daysAgo: daysAgo, + text: text + }; + renderFeedbackItem(feedbackItem); + + // send feedback item to server + fetch(`${BASE_API_URL}/feedbacks`, { + method: 'POST', + body: JSON.stringify(feedbackItem), + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + }).then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + console.log('Successfully submitted'); + }).catch(error => console.log(error)); + + // clear textarea + textareaEl.value = ''; + + // blur submit button + submitBtnEl.blur(); + + // reset counter + counterEl.textContent = MAX_CHARS; +}; + +formEl.addEventListener('submit', submitHandler); + + +// -- FEEDBACK LIST COMPONENT -- +const clickHandler = event => { + // get clicked HTML-element + const clickedEl = event.target; + + // determine if user intended to upvote or expand + const upvoteIntention = clickedEl.className.includes('upvote'); + + // run the appropriate logic + if (upvoteIntention) { + // get the closest upvote button + const upvoteBtnEl = clickedEl.closest('.upvote'); + + // disable upvote button (prevent double-clicks, spam) + upvoteBtnEl.disabled = true; + + // select the upvote count element within the upvote button + const upvoteCountEl = upvoteBtnEl.querySelector('.upvote__count'); + + // get currently displayed upvote count as number (+) + let upvoteCount = +upvoteCountEl.textContent; + + // set upvote count incremented by 1 + upvoteCountEl.textContent = ++upvoteCount; + } else { + // expand the clicked feedback item + clickedEl.closest('.feedback').classList.toggle('feedback--expand'); + } +}; + +feedbackListEl.addEventListener('click', clickHandler); + +fetch(`${BASE_API_URL}/feedbacks`) + .then(response => response.json()) + .then(data => { + // remove spinner + spinnerEl.remove(); + + // iterate over each element in feedbacks array and render it in list + data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); + }) + .catch(error => { + feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; + }); + + +// -- HASHTAG LIST COMPONENT -- +const clickHandler2 = event => { + // get the clicked element + const clickedEl = event.target; + + // stop function if click happened in list, but outside buttons + if (clickedEl.className === 'hashtags') return; + + // extract company name + const companyNameFromHashtag = clickedEl.textContent.substring(1).toLowerCase().trim(); + + // iterate over each feedback item in the list + feedbackListEl.childNodes.forEach(childNode => { + // stop this iteration if it's a text node + if (childNode.nodeType === 3) return; + + // extract company name + const companyNameFromFeedbackItem = childNode.querySelector('.feedback__company').textContent.toLowerCase().trim(); + + // remove feedback item from list if company names are not equal + if (companyNameFromHashtag !== companyNameFromFeedbackItem) { + childNode.remove(); + } + }); +}; + +hashtagListEl.addEventListener('click', clickHandler2); \ No newline at end of file diff --git a/CorpComment/video 13/style.css b/CorpComment/video 13/style.css new file mode 100644 index 0000000..fd4b37f --- /dev/null +++ b/CorpComment/video 13/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; + margin-bottom: 11px; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file diff --git a/CorpComment/video 14/index.html b/CorpComment/video 14/index.html new file mode 100644 index 0000000..b4a3c9b --- /dev/null +++ b/CorpComment/video 14/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    +
    +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 14/script.js b/CorpComment/video 14/script.js new file mode 100644 index 0000000..809fa3e --- /dev/null +++ b/CorpComment/video 14/script.js @@ -0,0 +1,215 @@ +// -- GLOBAL -- +const MAX_CHARS = 150; +const BASE_API_URL = 'https://bytegrad.com/course-assets/js/1/api'; + +const textareaEl = document.querySelector('.form__textarea'); +const counterEl = document.querySelector('.counter'); +const formEl = document.querySelector('.form'); +const feedbackListEl = document.querySelector('.feedbacks'); +const submitBtnEl = document.querySelector('.submit-btn'); +const spinnerEl = document.querySelector('.spinner'); +const hashtagListEl = document.querySelector('.hashtags'); + +const renderFeedbackItem = feedbackItem => { + // new feedback item HTML + const feedbackItemHTML = ` + + `; + + // insert new feedback item in list + feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); +}; + + +// -- COUNTER COMPONENT -- +(() => { + const inputHandler = () => { + // determine maximum number of characters + const maxNrChars = MAX_CHARS; + + // determine number of characters currently typed + const nrCharsTyped = textareaEl.value.length; + + // calculate number of characters left (maximum minus currently typed) + const charsLeft = maxNrChars - nrCharsTyped; + + // show number of characters left + counterEl.textContent = charsLeft; + }; + + textareaEl.addEventListener('input', inputHandler); +})(); + + +// -- FORM COMPONENT -- +(() => { + const showVisualIndicator = textCheck => { + const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; + + // show valid indicator + formEl.classList.add(className); + + // remove visual indicator + setTimeout(() => { + formEl.classList.remove(className); + }, 2000); + }; + + const submitHandler = event => { + // prevent default browser action (submitting form data to 'action'-address and refreshing page) + event.preventDefault(); + + // get text from textarea + const text = textareaEl.value; + + // validate text (e.g. check if #hashtag is present and text is long enough) + if (text.includes('#') && text.length >= 5) { + showVisualIndicator('valid'); + } else { + showVisualIndicator('invalid'); + + // focus textarea + textareaEl.focus(); + + // stop this function execution + return; + } + + // we have text, now extract other info from text + const hashtag = text.split(' ').find(word => word.includes('#')); + const company = hashtag.substring(1); + const badgeLetter = company.substring(0, 1).toUpperCase(); + const upvoteCount = 0; + const daysAgo = 0; + + // render feedback item in list + const feedbackItem = { + upvoteCount: upvoteCount, + company: company, + badgeLetter: badgeLetter, + daysAgo: daysAgo, + text: text + }; + renderFeedbackItem(feedbackItem); + + // send feedback item to server + fetch(`${BASE_API_URL}/feedbacks`, { + method: 'POST', + body: JSON.stringify(feedbackItem), + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + }).then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + console.log('Successfully submitted'); + }).catch(error => console.log(error)); + + // clear textarea + textareaEl.value = ''; + + // blur submit button + submitBtnEl.blur(); + + // reset counter + counterEl.textContent = MAX_CHARS; + }; + + formEl.addEventListener('submit', submitHandler); +})(); + + +// -- FEEDBACK LIST COMPONENT -- +(() => { + const clickHandler = event => { + // get clicked HTML-element + const clickedEl = event.target; + + // determine if user intended to upvote or expand + const upvoteIntention = clickedEl.className.includes('upvote'); + + // run the appropriate logic + if (upvoteIntention) { + // get the closest upvote button + const upvoteBtnEl = clickedEl.closest('.upvote'); + + // disable upvote button (prevent double-clicks, spam) + upvoteBtnEl.disabled = true; + + // select the upvote count element within the upvote button + const upvoteCountEl = upvoteBtnEl.querySelector('.upvote__count'); + + // get currently displayed upvote count as number (+) + let upvoteCount = +upvoteCountEl.textContent; + + // set upvote count incremented by 1 + upvoteCountEl.textContent = ++upvoteCount; + } else { + // expand the clicked feedback item + clickedEl.closest('.feedback').classList.toggle('feedback--expand'); + } + }; + + feedbackListEl.addEventListener('click', clickHandler); + + fetch(`${BASE_API_URL}/feedbacks`) + .then(response => response.json()) + .then(data => { + // remove spinner + spinnerEl.remove(); + + // iterate over each element in feedbacks array and render it in list + data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); + }) + .catch(error => { + feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; + }); +})(); + + +// -- HASHTAG LIST COMPONENT -- +(() => { + const clickHandler = event => { + // get the clicked element + const clickedEl = event.target; + + // stop function if click happened in list, but outside buttons + if (clickedEl.className === 'hashtags') return; + + // extract company name + const companyNameFromHashtag = clickedEl.textContent.substring(1).toLowerCase().trim(); + + // iterate over each feedback item in the list + feedbackListEl.childNodes.forEach(childNode => { + // stop this iteration if it's a text node + if (childNode.nodeType === 3) return; + + // extract company name + const companyNameFromFeedbackItem = childNode.querySelector('.feedback__company').textContent.toLowerCase().trim(); + + // remove feedback item from list if company names are not equal + if (companyNameFromHashtag !== companyNameFromFeedbackItem) { + childNode.remove(); + } + }); + }; + + hashtagListEl.addEventListener('click', clickHandler); +})(); diff --git a/CorpComment/video 14/style.css b/CorpComment/video 14/style.css new file mode 100644 index 0000000..fd4b37f --- /dev/null +++ b/CorpComment/video 14/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; + margin-bottom: 11px; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file diff --git a/CorpComment/video 4/style.css b/CorpComment/video 4/style.css index cc43415..fd4b37f 100644 --- a/CorpComment/video 4/style.css +++ b/CorpComment/video 4/style.css @@ -494,6 +494,7 @@ button { .hashtags__item { max-width: max-content; + margin-bottom: 11px; } /* HASHTAG */ @@ -505,7 +506,6 @@ button { padding: 7px 14px 9px; background-color: rgba(255, 255, 255, 0.06); border-radius: 500px; - margin-bottom: 11px; cursor: pointer; transition: all 0.2s; } diff --git a/CorpComment/video 5/style.css b/CorpComment/video 5/style.css index cc43415..fd4b37f 100644 --- a/CorpComment/video 5/style.css +++ b/CorpComment/video 5/style.css @@ -494,6 +494,7 @@ button { .hashtags__item { max-width: max-content; + margin-bottom: 11px; } /* HASHTAG */ @@ -505,7 +506,6 @@ button { padding: 7px 14px 9px; background-color: rgba(255, 255, 255, 0.06); border-radius: 500px; - margin-bottom: 11px; cursor: pointer; transition: all 0.2s; } diff --git a/CorpComment/video 6/index.html b/CorpComment/video 6/index.html new file mode 100644 index 0000000..816df8b --- /dev/null +++ b/CorpComment/video 6/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + afdasdf CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    + +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 6/script.js b/CorpComment/video 6/script.js new file mode 100644 index 0000000..6d694c9 --- /dev/null +++ b/CorpComment/video 6/script.js @@ -0,0 +1,19 @@ +// -- COUNTER COMPONENT -- +const textareaEl = document.querySelector('.form__textarea'); +const counterEl = document.querySelector('.counter'); + +const inputHandler = () => { + // determine maximum number of characters + const maxNrChars = 150; + + // determine number of characters currently typed + const nrCharsTyped = textareaEl.value.length; + + // calculate number of characters left (maximum minus currently typed) + const charsLeft = maxNrChars - nrCharsTyped; + + // show number of characters left + counterEl.textContent = charsLeft; +}; + +textareaEl.addEventListener('input', inputHandler); diff --git a/CorpComment/video 6/style.css b/CorpComment/video 6/style.css new file mode 100644 index 0000000..fd4b37f --- /dev/null +++ b/CorpComment/video 6/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; + margin-bottom: 11px; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file diff --git a/CorpComment/video 7/index.html b/CorpComment/video 7/index.html new file mode 100644 index 0000000..a7a9d00 --- /dev/null +++ b/CorpComment/video 7/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    + +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 7/script.js b/CorpComment/video 7/script.js new file mode 100644 index 0000000..40454ab --- /dev/null +++ b/CorpComment/video 7/script.js @@ -0,0 +1,98 @@ +// -- GLOBAL -- +const textareaEl = document.querySelector('.form__textarea'); +const counterEl = document.querySelector('.counter'); +const formEl = document.querySelector('.form'); +const feedbackListEl = document.querySelector('.feedbacks'); +const submitBtnEl = document.querySelector('.submit-btn'); + + +// -- COUNTER COMPONENT -- +const inputHandler = () => { + // determine maximum number of characters + const maxNrChars = 150; + + // determine number of characters currently typed + const nrCharsTyped = textareaEl.value.length; + + // calculate number of characters left (maximum minus currently typed) + const charsLeft = maxNrChars - nrCharsTyped; + + // show number of characters left + counterEl.textContent = charsLeft; +}; + +textareaEl.addEventListener('input', inputHandler); + + +// -- SUBMIT COMPONENT -- +const submitHandler = event => { + // prevent default browser action (submitting form data to 'action'-address and refreshing page) + event.preventDefault(); + + // get text from textarea + const text = textareaEl.value; + + // validate text (e.g. check if #hashtag is present and text is long enough) + if (text.includes('#') && text.length >= 5) { + // show valid indicator + formEl.classList.add('form--valid'); + + // remove visual indicator + setTimeout(() => { + formEl.classList.remove('form--valid'); + }, 2000); + } else { + // show invalid indicator + formEl.classList.add('form--invalid'); + + // remove visual indicator + setTimeout(() => { + formEl.classList.remove('form--invalid'); + }, 2000); + + // focus textarea + textareaEl.focus(); + + // stop this function execution + return; + } + + // we have text, now extract other info from text + const hashtag = text.split(' ').find(word => word.includes('#')); + const company = hashtag.substring(1); + const badgeLetter = company.substring(0, 1).toUpperCase(); + const upvoteCount = 0; + const daysAgo = 0; + + // new feedback item HTML + const feedbackItemHTML = ` + + `; + + // insert new feedback item in list + feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); + + // clear textarea + textareaEl.value = ''; + + // blur submit button + submitBtnEl.blur(); + + // reset counter + counterEl.textContent = '150'; +}; + +formEl.addEventListener('submit', submitHandler); diff --git a/CorpComment/video 7/style.css b/CorpComment/video 7/style.css new file mode 100644 index 0000000..fd4b37f --- /dev/null +++ b/CorpComment/video 7/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; + margin-bottom: 11px; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file diff --git a/CorpComment/video 8/index.html b/CorpComment/video 8/index.html new file mode 100644 index 0000000..a7a9d00 --- /dev/null +++ b/CorpComment/video 8/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    + +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 8/script.js b/CorpComment/video 8/script.js new file mode 100644 index 0000000..1ca56ec --- /dev/null +++ b/CorpComment/video 8/script.js @@ -0,0 +1,100 @@ +// -- GLOBAL -- +const MAX_CHARS = 150; + +const textareaEl = document.querySelector('.form__textarea'); +const counterEl = document.querySelector('.counter'); +const formEl = document.querySelector('.form'); +const feedbackListEl = document.querySelector('.feedbacks'); +const submitBtnEl = document.querySelector('.submit-btn'); + + +// -- COUNTER COMPONENT -- +const inputHandler = () => { + // determine maximum number of characters + const maxNrChars = MAX_CHARS; + + // determine number of characters currently typed + const nrCharsTyped = textareaEl.value.length; + + // calculate number of characters left (maximum minus currently typed) + const charsLeft = maxNrChars - nrCharsTyped; + + // show number of characters left + counterEl.textContent = charsLeft; +}; + +textareaEl.addEventListener('input', inputHandler); + + +// -- FORM COMPONENT -- +const showVisualIndicator = textCheck => { + const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; + + // show valid indicator + formEl.classList.add(className); + + // remove visual indicator + setTimeout(() => { + formEl.classList.remove(className); + }, 2000); +}; + +const submitHandler = event => { + // prevent default browser action (submitting form data to 'action'-address and refreshing page) + event.preventDefault(); + + // get text from textarea + const text = textareaEl.value; + + // validate text (e.g. check if #hashtag is present and text is long enough) + if (text.includes('#') && text.length >= 5) { + showVisualIndicator('valid'); + } else { + showVisualIndicator('invalid'); + + // focus textarea + textareaEl.focus(); + + // stop this function execution + return; + } + + // we have text, now extract other info from text + const hashtag = text.split(' ').find(word => word.includes('#')); + const company = hashtag.substring(1); + const badgeLetter = company.substring(0, 1).toUpperCase(); + const upvoteCount = 0; + const daysAgo = 0; + + // new feedback item HTML + const feedbackItemHTML = ` + + `; + + // insert new feedback item in list + feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); + + // clear textarea + textareaEl.value = ''; + + // blur submit button + submitBtnEl.blur(); + + // reset counter + counterEl.textContent = MAX_CHARS; +}; + +formEl.addEventListener('submit', submitHandler); diff --git a/CorpComment/video 8/style.css b/CorpComment/video 8/style.css new file mode 100644 index 0000000..fd4b37f --- /dev/null +++ b/CorpComment/video 8/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; + margin-bottom: 11px; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file diff --git a/CorpComment/video 9/index.html b/CorpComment/video 9/index.html new file mode 100644 index 0000000..b4a3c9b --- /dev/null +++ b/CorpComment/video 9/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + CorpComment -- Give Feedback, Publicly + + + + +
+
+ + + + +
+ +
+
+ pattern + +

Give Feedback. Publicly.

+
+ + +
+

150

+ +
+
+
+ +
    +
    +
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/CorpComment/video 9/script.js b/CorpComment/video 9/script.js new file mode 100644 index 0000000..e919ed2 --- /dev/null +++ b/CorpComment/video 9/script.js @@ -0,0 +1,137 @@ +// -- GLOBAL -- +const MAX_CHARS = 150; + +const textareaEl = document.querySelector('.form__textarea'); +const counterEl = document.querySelector('.counter'); +const formEl = document.querySelector('.form'); +const feedbackListEl = document.querySelector('.feedbacks'); +const submitBtnEl = document.querySelector('.submit-btn'); +const spinnerEl = document.querySelector('.spinner'); + + +// -- COUNTER COMPONENT -- +const inputHandler = () => { + // determine maximum number of characters + const maxNrChars = MAX_CHARS; + + // determine number of characters currently typed + const nrCharsTyped = textareaEl.value.length; + + // calculate number of characters left (maximum minus currently typed) + const charsLeft = maxNrChars - nrCharsTyped; + + // show number of characters left + counterEl.textContent = charsLeft; +}; + +textareaEl.addEventListener('input', inputHandler); + + +// -- FORM COMPONENT -- +const showVisualIndicator = textCheck => { + const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; + + // show valid indicator + formEl.classList.add(className); + + // remove visual indicator + setTimeout(() => { + formEl.classList.remove(className); + }, 2000); +}; + +const submitHandler = event => { + // prevent default browser action (submitting form data to 'action'-address and refreshing page) + event.preventDefault(); + + // get text from textarea + const text = textareaEl.value; + + // validate text (e.g. check if #hashtag is present and text is long enough) + if (text.includes('#') && text.length >= 5) { + showVisualIndicator('valid'); + } else { + showVisualIndicator('invalid'); + + // focus textarea + textareaEl.focus(); + + // stop this function execution + return; + } + + // we have text, now extract other info from text + const hashtag = text.split(' ').find(word => word.includes('#')); + const company = hashtag.substring(1); + const badgeLetter = company.substring(0, 1).toUpperCase(); + const upvoteCount = 0; + const daysAgo = 0; + + // new feedback item HTML + const feedbackItemHTML = ` + + `; + + // insert new feedback item in list + feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); + + // clear textarea + textareaEl.value = ''; + + // blur submit button + submitBtnEl.blur(); + + // reset counter + counterEl.textContent = MAX_CHARS; +}; + +formEl.addEventListener('submit', submitHandler); + + +// -- FEEDBACK LIST COMPONENT -- +fetch('https://bytegrad.com/course-assets/js/1/api/feedbacks') + .then(response => response.json()) + .then(data => { + // remove spinner + spinnerEl.remove(); + + // iterate over each element in feedbacks array and render it in list + data.feedbacks.forEach(feedbackItem => { + // new feedback item HTML + const feedbackItemHTML = ` + + `; + + // insert new feedback item in list + feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); + }); + }) + .catch(error => { + feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; + }); diff --git a/CorpComment/video 9/style.css b/CorpComment/video 9/style.css new file mode 100644 index 0000000..fd4b37f --- /dev/null +++ b/CorpComment/video 9/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; + margin-bottom: 11px; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file diff --git a/Modern JS Fundamentals/index.html b/Modern JS Fundamentals/index.html new file mode 100644 index 0000000..b84e1e5 --- /dev/null +++ b/Modern JS Fundamentals/index.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + Overview of Modern JS Fundamentals + + + +

This is a quick overview of modern JS

+ + + \ No newline at end of file diff --git a/Modern JS Fundamentals/script.js b/Modern JS Fundamentals/script.js new file mode 100644 index 0000000..0382f0c --- /dev/null +++ b/Modern JS Fundamentals/script.js @@ -0,0 +1,67 @@ +// -- variables (var/let/const) & data types/structures (strings/numbers/booleans/arrays/objects) -- +// const description = 'We need a new floor.'; +// const squareMeters = 100; +// const specialCoating = true; +// const floorOptions = ['carpet', 'hardwood', 'tiles']; +// const renovationJob = { +// ownerName: 'John', +// maximumPrice: 5000, +// category: 'bathroom', +// newShower: true +// }; + + +// -- traditional functions vs arrow functions -- +// function calculatePrice(sqMeters) { +// return 1000 + sqMeters; +// } + +// var calculatePrice = function(sqMeters) { +// return 1000 + sqMeters; +// }; + +// const calculatePrice = (sqMeters) => { +// return 1000 + sqMeters; +// }; + +// const calculatePrice = sqMeters => 1000 + sqMeters; + + +// -- string concatenation vs template literals -- +// const price = 5000; +// const result = 'The total cost will be: ' + price; +// const result = `The total cost will be: ${price}`; + + +// -- if-else vs ternary operator -- +// const price = 5000; + +// if (price) { +// console.log('hello'); +// } else { +// console.log('blabla'); +// } + +// price > 3000 ? console.log('hello') : console.log('blabla'); + + +// -- manipulating HTML and CSS -- +// const headingEl = document.querySelector('.heading'); + +// headingEl.textContent = 'Hello everyone!'; +// headingEl.innerHTML = 'Hello everyone!'; +// headingEl.insertAdjacentHTML('beforebegin', 'Hello everyone!'); + +// headingEl.style.fontSize = '55px'; +// headingEl.classList.add('heading--big'); + + +// -- events and functions for handling events (also called "event handlers") -- +// const headingEl = document.querySelector('.heading'); + +// const clickHandler = () => { +// headingEl.style.color = 'red'; +// console.log('changed color'); +// }; + +// headingEl.addEventListener('click', clickHandler); \ No newline at end of file diff --git a/Modern JS Fundamentals/style.css b/Modern JS Fundamentals/style.css new file mode 100644 index 0000000..a7721c6 --- /dev/null +++ b/Modern JS Fundamentals/style.css @@ -0,0 +1,7 @@ +.heading { + font-size: 15px; +} + +.heading--big { + font-size: 55px; +} \ No newline at end of file diff --git a/rmtDev/video 16/index.css b/rmtDev/video 16/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 16/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 16/index.html b/rmtDev/video 16/index.html new file mode 100644 index 0000000..93ddb40 --- /dev/null +++ b/rmtDev/video 16/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
+ Background pattern +
+ +
+
+ + +
    + +
+
+
+ +
+
+

Find your remote developer job

+ +
+ +
+
+ + +
+

+ 0 results +

+ +
+ + + +
+
+ + + +
+ + +
+
+ +
+
+ +
+ +
+

What are you looking for?

+

Start by searching for any technology + your ideal job is working with

+
+ +
+
+
+
+ +
+ + + + +

109573 total jobs available

+
+ +
+ +
+

Something went wrong

+

Lorem, ipsum dolor sit amet consectetur adipisicing elit.

+
+
+ + + + \ No newline at end of file diff --git a/rmtDev/video 16/index.js b/rmtDev/video 16/index.js new file mode 100644 index 0000000..e69de29 diff --git a/rmtDev/video 17/index.css b/rmtDev/video 17/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 17/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 17/index.html b/rmtDev/video 17/index.html new file mode 100644 index 0000000..93ddb40 --- /dev/null +++ b/rmtDev/video 17/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
+ Background pattern +
+ +
+
+ + +
    + +
+
+
+ +
+
+

Find your remote developer job

+ +
+ +
+
+ + +
+

+ 0 results +

+ +
+ + + +
+
+ + + +
+ + +
+
+ +
+
+ +
+ +
+

What are you looking for?

+

Start by searching for any technology + your ideal job is working with

+
+ +
+
+
+
+ +
+ + + + +

109573 total jobs available

+
+ +
+ +
+

Something went wrong

+

Lorem, ipsum dolor sit amet consectetur adipisicing elit.

+
+
+ + + + \ No newline at end of file diff --git a/rmtDev/video 17/index.js b/rmtDev/video 17/index.js new file mode 100644 index 0000000..e22d115 --- /dev/null +++ b/rmtDev/video 17/index.js @@ -0,0 +1,99 @@ +// -- GLOBAL -- +const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +const errorEl = document.querySelector('.error'); +const errorTextEl = document.querySelector('.error__text'); +const jobDetailsEl = document.querySelector('.job-details'); +const jobDetailsContentEl = document.querySelector(".job-details__content"); +const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +const jobListSearchEl = document.querySelector(".job-list--search"); +const numberEl = document.querySelector(".count__number"); +const paginationEl = document.querySelector(".pagination"); +const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +const searchFormEl = document.querySelector(".search"); +const searchInputEl = document.querySelector(".search__input"); +const sortingEl = document.querySelector(".sorting"); +const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +const spinnerSearchEl = document.querySelector(".spinner--search"); +const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// -- SEARCH COMPONENT -- +const submitHandler = event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + errorTextEl.textContent = 'Your search may not contain numbers'; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, 3500); + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + spinnerSearchEl.classList.add('spinner--visible'); + + // fetch search results + fetch(`https://bytegrad.com/course-assets/js/2/api/jobs?search=${searchText}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job items + const { jobItems } = data; + + // remove spinner + spinnerSearchEl.classList.remove('spinner--visible'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + jobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); + }) + .catch(error => console.log(error)); +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 18/index.css b/rmtDev/video 18/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 18/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 18/index.html b/rmtDev/video 18/index.html new file mode 100644 index 0000000..93ddb40 --- /dev/null +++ b/rmtDev/video 18/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 18/index.js b/rmtDev/video 18/index.js new file mode 100644 index 0000000..bed83f8 --- /dev/null +++ b/rmtDev/video 18/index.js @@ -0,0 +1,203 @@ +// -- GLOBAL -- +const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +const errorEl = document.querySelector('.error'); +const errorTextEl = document.querySelector('.error__text'); +const jobDetailsEl = document.querySelector('.job-details'); +const jobDetailsContentEl = document.querySelector(".job-details__content"); +const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +const jobListSearchEl = document.querySelector(".job-list--search"); +const numberEl = document.querySelector(".count__number"); +const paginationEl = document.querySelector(".pagination"); +const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +const searchFormEl = document.querySelector(".search"); +const searchInputEl = document.querySelector(".search__input"); +const sortingEl = document.querySelector(".sorting"); +const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +const spinnerSearchEl = document.querySelector(".spinner--search"); +const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// -- SEARCH COMPONENT -- +const submitHandler = event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + errorTextEl.textContent = 'Your search may not contain numbers'; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, 3500); + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + spinnerSearchEl.classList.add('spinner--visible'); + + // fetch search results + fetch(`https://bytegrad.com/course-assets/js/2/api/jobs?search=${searchText}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job items + const { jobItems } = data; + + // remove spinner + spinnerSearchEl.classList.remove('spinner--visible'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + jobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); + }) + .catch(error => console.log(error)); +}; + +searchFormEl.addEventListener('submit', submitHandler); + +// -- JOB LIST COMPONENT -- +const clickHandler = event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + spinnerJobDetailsEl.classList.add('spinner--visible'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // fetch job item data + fetch(`https://bytegrad.com/course-assets/js/2/api/jobs/${id}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job item + const { jobItem } = data; + + // remove spinner + spinnerJobDetailsEl.classList.remove('spinner--visible'); + + // render job details + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; + }) + .catch(error => console.log(error)); +}; + +jobListSearchEl.addEventListener('click', clickHandler); + + + diff --git a/rmtDev/video 19/index.css b/rmtDev/video 19/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 19/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 19/index.html b/rmtDev/video 19/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 19/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 19/index.js b/rmtDev/video 19/index.js new file mode 100644 index 0000000..1842071 --- /dev/null +++ b/rmtDev/video 19/index.js @@ -0,0 +1,4 @@ +import './src/components/Error.js'; +import './src/components/JobList.js'; +import './src/components/Search.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 19/src/common.js b/rmtDev/video 19/src/common.js new file mode 100644 index 0000000..8494ea4 --- /dev/null +++ b/rmtDev/video 19/src/common.js @@ -0,0 +1,20 @@ +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/rmtDev/video 19/src/components/Error.js b/rmtDev/video 19/src/components/Error.js new file mode 100644 index 0000000..e69de29 diff --git a/rmtDev/video 19/src/components/JobList.js b/rmtDev/video 19/src/components/JobList.js new file mode 100644 index 0000000..85985cf --- /dev/null +++ b/rmtDev/video 19/src/components/JobList.js @@ -0,0 +1,105 @@ +import { + jobListSearchEl, + jobDetailsContentEl, + spinnerJobDetailsEl +} from '../common.js'; + +const clickHandler = event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + spinnerJobDetailsEl.classList.add('spinner--visible'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // fetch job item data + fetch(`https://bytegrad.com/course-assets/js/2/api/jobs/${id}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job item + const { jobItem } = data; + + // remove spinner + spinnerJobDetailsEl.classList.remove('spinner--visible'); + + // render job details + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; + }) + .catch(error => console.log(error)); +}; + +jobListSearchEl.addEventListener('click', clickHandler); \ No newline at end of file diff --git a/rmtDev/video 19/src/components/Search.js b/rmtDev/video 19/src/components/Search.js new file mode 100644 index 0000000..21f06d5 --- /dev/null +++ b/rmtDev/video 19/src/components/Search.js @@ -0,0 +1,84 @@ +import { + searchInputEl, + searchFormEl, + spinnerSearchEl, + jobListSearchEl, + numberEl +} from '../common.js'; + +const submitHandler = event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + errorTextEl.textContent = 'Your search may not contain numbers'; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, 3500); + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + spinnerSearchEl.classList.add('spinner--visible'); + + // fetch search results + fetch(`https://bytegrad.com/course-assets/js/2/api/jobs?search=${searchText}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job items + const { jobItems } = data; + + // remove spinner + spinnerSearchEl.classList.remove('spinner--visible'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + jobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); + }) + .catch(error => console.log(error)); +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 19/src/components/Spinner.js b/rmtDev/video 19/src/components/Spinner.js new file mode 100644 index 0000000..e69de29 diff --git a/rmtDev/video 20/index.css b/rmtDev/video 20/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 20/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 20/index.html b/rmtDev/video 20/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 20/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 20/index.js b/rmtDev/video 20/index.js new file mode 100644 index 0000000..1842071 --- /dev/null +++ b/rmtDev/video 20/index.js @@ -0,0 +1,4 @@ +import './src/components/Error.js'; +import './src/components/JobList.js'; +import './src/components/Search.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 20/src/common.js b/rmtDev/video 20/src/common.js new file mode 100644 index 0000000..8494ea4 --- /dev/null +++ b/rmtDev/video 20/src/common.js @@ -0,0 +1,20 @@ +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/rmtDev/video 20/src/components/Error.js b/rmtDev/video 20/src/components/Error.js new file mode 100644 index 0000000..ac0e9b2 --- /dev/null +++ b/rmtDev/video 20/src/components/Error.js @@ -0,0 +1,14 @@ +import { + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, 3500); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 20/src/components/JobList.js b/rmtDev/video 20/src/components/JobList.js new file mode 100644 index 0000000..1bace65 --- /dev/null +++ b/rmtDev/video 20/src/components/JobList.js @@ -0,0 +1,105 @@ +import { + jobListSearchEl, + jobDetailsContentEl +} from '../common.js'; +import renderSpinner from './Spinner.js'; + +const clickHandler = event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // fetch job item data + fetch(`https://bytegrad.com/course-assets/js/2/api/jobs/${id}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; + }) + .catch(error => console.log(error)); +}; + +jobListSearchEl.addEventListener('click', clickHandler); \ No newline at end of file diff --git a/rmtDev/video 20/src/components/Search.js b/rmtDev/video 20/src/components/Search.js new file mode 100644 index 0000000..8c30f60 --- /dev/null +++ b/rmtDev/video 20/src/components/Search.js @@ -0,0 +1,82 @@ +import { + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; + +const submitHandler = event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + renderSpinner('search'); + + // fetch search results + fetch(`https://bytegrad.com/course-assets/js/2/api/jobs?search=${searchText}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job items + const { jobItems } = data; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + jobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); + }) + .catch(error => console.log(error)); +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 20/src/components/Spinner.js b/rmtDev/video 20/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 20/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 21/index.css b/rmtDev/video 21/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 21/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 21/index.html b/rmtDev/video 21/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 21/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 21/index.js b/rmtDev/video 21/index.js new file mode 100644 index 0000000..1842071 --- /dev/null +++ b/rmtDev/video 21/index.js @@ -0,0 +1,4 @@ +import './src/components/Error.js'; +import './src/components/JobList.js'; +import './src/components/Search.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 21/src/common.js b/rmtDev/video 21/src/common.js new file mode 100644 index 0000000..39a7caf --- /dev/null +++ b/rmtDev/video 21/src/common.js @@ -0,0 +1,25 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/rmtDev/video 21/src/components/Error.js b/rmtDev/video 21/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 21/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 21/src/components/JobList.js b/rmtDev/video 21/src/components/JobList.js new file mode 100644 index 0000000..3bdf95c --- /dev/null +++ b/rmtDev/video 21/src/components/JobList.js @@ -0,0 +1,106 @@ +import { + BASE_API_URL, + jobListSearchEl, + jobDetailsContentEl +} from '../common.js'; +import renderSpinner from './Spinner.js'; + +const clickHandler = event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // fetch job item data + fetch(`${BASE_API_URL}/jobs/${id}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; + }) + .catch(error => console.log(error)); +}; + +jobListSearchEl.addEventListener('click', clickHandler); \ No newline at end of file diff --git a/rmtDev/video 21/src/components/Search.js b/rmtDev/video 21/src/components/Search.js new file mode 100644 index 0000000..b6159cd --- /dev/null +++ b/rmtDev/video 21/src/components/Search.js @@ -0,0 +1,83 @@ +import { + BASE_API_URL, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; + +const submitHandler = event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + renderSpinner('search'); + + // fetch search results + fetch(`${BASE_API_URL}/jobs?search=${searchText}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job items + const { jobItems } = data; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + jobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); + }) + .catch(error => console.log(error)); +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 21/src/components/Spinner.js b/rmtDev/video 21/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 21/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 22/index.css b/rmtDev/video 22/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 22/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 22/index.html b/rmtDev/video 22/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 22/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 22/index.js b/rmtDev/video 22/index.js new file mode 100644 index 0000000..65ddab7 --- /dev/null +++ b/rmtDev/video 22/index.js @@ -0,0 +1,5 @@ +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Search.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 22/src/common.js b/rmtDev/video 22/src/common.js new file mode 100644 index 0000000..39a7caf --- /dev/null +++ b/rmtDev/video 22/src/common.js @@ -0,0 +1,25 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/rmtDev/video 22/src/components/Error.js b/rmtDev/video 22/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 22/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 22/src/components/JobDetails.js b/rmtDev/video 22/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 22/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 22/src/components/JobList.js b/rmtDev/video 22/src/components/JobList.js new file mode 100644 index 0000000..d386a35 --- /dev/null +++ b/rmtDev/video 22/src/components/JobList.js @@ -0,0 +1,82 @@ +import { + BASE_API_URL, + jobListSearchEl, + jobDetailsContentEl +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; + +const renderJobList = jobItems => { + jobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // fetch job item data + fetch(`${BASE_API_URL}/jobs/${id}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + }) + .catch(error => console.log(error)); +}; + +jobListSearchEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 22/src/components/Search.js b/rmtDev/video 22/src/components/Search.js new file mode 100644 index 0000000..a4bd91a --- /dev/null +++ b/rmtDev/video 22/src/components/Search.js @@ -0,0 +1,62 @@ +import { + BASE_API_URL, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; + +const submitHandler = event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + renderSpinner('search'); + + // fetch search results + fetch(`${BASE_API_URL}/jobs?search=${searchText}`) + .then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + return response.json(); + }) + .then(data => { + // extract job items + const { jobItems } = data; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + renderJobList(jobItems); + }) + .catch(error => console.log(error)); +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 22/src/components/Spinner.js b/rmtDev/video 22/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 22/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 23/index.css b/rmtDev/video 23/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 23/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 23/index.html b/rmtDev/video 23/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 23/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 23/index.js b/rmtDev/video 23/index.js new file mode 100644 index 0000000..65ddab7 --- /dev/null +++ b/rmtDev/video 23/index.js @@ -0,0 +1,5 @@ +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Search.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 23/src/common.js b/rmtDev/video 23/src/common.js new file mode 100644 index 0000000..39a7caf --- /dev/null +++ b/rmtDev/video 23/src/common.js @@ -0,0 +1,25 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/rmtDev/video 23/src/components/Error.js b/rmtDev/video 23/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 23/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 23/src/components/JobDetails.js b/rmtDev/video 23/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 23/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 23/src/components/JobList.js b/rmtDev/video 23/src/components/JobList.js new file mode 100644 index 0000000..5dfee66 --- /dev/null +++ b/rmtDev/video 23/src/components/JobList.js @@ -0,0 +1,84 @@ +import { + BASE_API_URL, + jobListSearchEl, + jobDetailsContentEl +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; + +const renderJobList = jobItems => { + jobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // fetch job item data + fetch(`${BASE_API_URL}/jobs/${id}`) + .then(response => { + if (!response.ok) { // 4xx, 5xx status code + throw new Error('Resource issue (e.g. resource doesn\'t exist) or server issue'); + } + + return response.json(); + }) + .then(data => { + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + }) + .catch(error => { // network problem or other errors (e.g. trying to parse something not JSON as JSON) + renderSpinner('job-details'); + renderError(error.message); + }); +}; + +jobListSearchEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 23/src/components/Search.js b/rmtDev/video 23/src/components/Search.js new file mode 100644 index 0000000..259c3a8 --- /dev/null +++ b/rmtDev/video 23/src/components/Search.js @@ -0,0 +1,64 @@ +import { + BASE_API_URL, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; + +const submitHandler = event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + renderSpinner('search'); + + // fetch search results + fetch(`${BASE_API_URL}/jobs?search=${searchText}`) + .then(response => { + if (!response.ok) { // 4xx, 5xx status code + throw new Error('Resource issue (e.g. resource doesn\'t exist) or server issue'); + } + + return response.json(); + }) + .then(data => { + // extract job items + const { jobItems } = data; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + renderJobList(jobItems); + }) + .catch(error => { // network problem or other errors (e.g. trying to parse something not JSON as JSON) + renderSpinner('search'); + renderError(error.message); + }); +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 23/src/components/Spinner.js b/rmtDev/video 23/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 23/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 24/index.css b/rmtDev/video 24/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 24/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 24/index.html b/rmtDev/video 24/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 24/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 24/index.js b/rmtDev/video 24/index.js new file mode 100644 index 0000000..65ddab7 --- /dev/null +++ b/rmtDev/video 24/index.js @@ -0,0 +1,5 @@ +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Search.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 24/src/common.js b/rmtDev/video 24/src/common.js new file mode 100644 index 0000000..39a7caf --- /dev/null +++ b/rmtDev/video 24/src/common.js @@ -0,0 +1,25 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/rmtDev/video 24/src/components/Error.js b/rmtDev/video 24/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 24/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 24/src/components/JobDetails.js b/rmtDev/video 24/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 24/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 24/src/components/JobList.js b/rmtDev/video 24/src/components/JobList.js new file mode 100644 index 0000000..221b7b2 --- /dev/null +++ b/rmtDev/video 24/src/components/JobList.js @@ -0,0 +1,83 @@ +import { + BASE_API_URL, + jobListSearchEl, + jobDetailsContentEl +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = jobItems => { + jobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // fetch job item data + try { + const response = await fetch(`${BASE_API_URL}/jobs/${id}`); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 24/src/components/Search.js b/rmtDev/video 24/src/components/Search.js new file mode 100644 index 0000000..7bb49b3 --- /dev/null +++ b/rmtDev/video 24/src/components/Search.js @@ -0,0 +1,62 @@ +import { + BASE_API_URL, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + renderSpinner('search'); + + // fetch search results + try { + const response = await fetch(`${BASE_API_URL}/jobs?search=${searchText}`); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + // extract job items + const { jobItems } = data; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + renderJobList(jobItems); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 24/src/components/Spinner.js b/rmtDev/video 24/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 24/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 25/index.css b/rmtDev/video 25/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 25/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 25/index.html b/rmtDev/video 25/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 25/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 25/index.js b/rmtDev/video 25/index.js new file mode 100644 index 0000000..65ddab7 --- /dev/null +++ b/rmtDev/video 25/index.js @@ -0,0 +1,5 @@ +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Search.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 25/src/common.js b/rmtDev/video 25/src/common.js new file mode 100644 index 0000000..b433421 --- /dev/null +++ b/rmtDev/video 25/src/common.js @@ -0,0 +1,37 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// HELPER / UTILITY FUNCTIONS +export const getData = async completeURL => { + const response = await fetch(completeURL); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + return data; +}; \ No newline at end of file diff --git a/rmtDev/video 25/src/components/Error.js b/rmtDev/video 25/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 25/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 25/src/components/JobDetails.js b/rmtDev/video 25/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 25/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 25/src/components/JobList.js b/rmtDev/video 25/src/components/JobList.js new file mode 100644 index 0000000..40cfa32 --- /dev/null +++ b/rmtDev/video 25/src/components/JobList.js @@ -0,0 +1,79 @@ +import { + BASE_API_URL, + jobListSearchEl, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = jobItems => { + jobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 25/src/components/Search.js b/rmtDev/video 25/src/components/Search.js new file mode 100644 index 0000000..9874b96 --- /dev/null +++ b/rmtDev/video 25/src/components/Search.js @@ -0,0 +1,58 @@ +import { + BASE_API_URL, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl, + getData +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + renderSpinner('search'); + + try { + // fetch search results + const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); + + // extract job items + const { jobItems } = data; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + renderJobList(jobItems); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 25/src/components/Spinner.js b/rmtDev/video 25/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 25/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 26/index.css b/rmtDev/video 26/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 26/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 26/index.html b/rmtDev/video 26/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 26/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 26/index.js b/rmtDev/video 26/index.js new file mode 100644 index 0000000..b81c001 --- /dev/null +++ b/rmtDev/video 26/index.js @@ -0,0 +1,6 @@ +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Search.js'; +import './src/components/Sorting.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 26/src/common.js b/rmtDev/video 26/src/common.js new file mode 100644 index 0000000..b433421 --- /dev/null +++ b/rmtDev/video 26/src/common.js @@ -0,0 +1,37 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// HELPER / UTILITY FUNCTIONS +export const getData = async completeURL => { + const response = await fetch(completeURL); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + return data; +}; \ No newline at end of file diff --git a/rmtDev/video 26/src/components/Error.js b/rmtDev/video 26/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 26/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 26/src/components/JobDetails.js b/rmtDev/video 26/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 26/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 26/src/components/JobList.js b/rmtDev/video 26/src/components/JobList.js new file mode 100644 index 0000000..40cfa32 --- /dev/null +++ b/rmtDev/video 26/src/components/JobList.js @@ -0,0 +1,79 @@ +import { + BASE_API_URL, + jobListSearchEl, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = jobItems => { + jobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 26/src/components/Search.js b/rmtDev/video 26/src/components/Search.js new file mode 100644 index 0000000..9874b96 --- /dev/null +++ b/rmtDev/video 26/src/components/Search.js @@ -0,0 +1,58 @@ +import { + BASE_API_URL, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl, + getData +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + renderSpinner('search'); + + try { + // fetch search results + const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); + + // extract job items + const { jobItems } = data; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + renderJobList(jobItems); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 26/src/components/Sorting.js b/rmtDev/video 26/src/components/Sorting.js new file mode 100644 index 0000000..69cf900 --- /dev/null +++ b/rmtDev/video 26/src/components/Sorting.js @@ -0,0 +1,27 @@ +import { + state, + sortingEl, + sortingBtnRecentEl, + sortingBtnRelevantEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.sorting__button'); + + // stop function if no clicked button element + if (!clickedButtonEl) return; + + // check if intention is recent or relevant sorting + const recent = clickedButtonEl.className.includes('--recent') ? true : false; + + // sort job items + if (recent) { + + } else { + + } +}; + +sortingEl.addEventListener('click', clickHandler); diff --git a/rmtDev/video 26/src/components/Spinner.js b/rmtDev/video 26/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 26/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 27/index.css b/rmtDev/video 27/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 27/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 27/index.html b/rmtDev/video 27/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 27/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 27/index.js b/rmtDev/video 27/index.js new file mode 100644 index 0000000..b81c001 --- /dev/null +++ b/rmtDev/video 27/index.js @@ -0,0 +1,6 @@ +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Search.js'; +import './src/components/Sorting.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 27/src/common.js b/rmtDev/video 27/src/common.js new file mode 100644 index 0000000..e1a8785 --- /dev/null +++ b/rmtDev/video 27/src/common.js @@ -0,0 +1,42 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; + +// STATE +export const state = { + searchJobItems: [] +}; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// HELPER / UTILITY FUNCTIONS +export const getData = async completeURL => { + const response = await fetch(completeURL); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + return data; +}; \ No newline at end of file diff --git a/rmtDev/video 27/src/components/Error.js b/rmtDev/video 27/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 27/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 27/src/components/JobDetails.js b/rmtDev/video 27/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 27/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 27/src/components/JobList.js b/rmtDev/video 27/src/components/JobList.js new file mode 100644 index 0000000..8bf12b8 --- /dev/null +++ b/rmtDev/video 27/src/components/JobList.js @@ -0,0 +1,80 @@ +import { + BASE_API_URL, + state, + jobListSearchEl, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = () => { + state.searchJobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 27/src/components/Search.js b/rmtDev/video 27/src/components/Search.js new file mode 100644 index 0000000..133a652 --- /dev/null +++ b/rmtDev/video 27/src/components/Search.js @@ -0,0 +1,62 @@ +import { + BASE_API_URL, + state, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl, + getData +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + renderSpinner('search'); + + try { + // fetch search results + const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); + + // extract job items + const { jobItems } = data; + + // update state + state.searchJobItems = jobItems; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + renderJobList(); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 27/src/components/Sorting.js b/rmtDev/video 27/src/components/Sorting.js new file mode 100644 index 0000000..61c8a83 --- /dev/null +++ b/rmtDev/video 27/src/components/Sorting.js @@ -0,0 +1,25 @@ +import { + sortingEl, + sortingBtnRecentEl, + sortingBtnRelevantEl +} from '../common.js'; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.sorting__button'); + + // stop function if no clicked button element + if (!clickedButtonEl) return; + + // check if intention is recent or relevant sorting + const recent = clickedButtonEl.className.includes('--recent') ? true : false; + + // sort job items + if (recent) { + + } else { + + } +}; + +sortingEl.addEventListener('click', clickHandler); diff --git a/rmtDev/video 27/src/components/Spinner.js b/rmtDev/video 27/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 27/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 28/index.css b/rmtDev/video 28/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 28/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 28/index.html b/rmtDev/video 28/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 28/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 28/index.js b/rmtDev/video 28/index.js new file mode 100644 index 0000000..b81c001 --- /dev/null +++ b/rmtDev/video 28/index.js @@ -0,0 +1,6 @@ +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Search.js'; +import './src/components/Sorting.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 28/src/common.js b/rmtDev/video 28/src/common.js new file mode 100644 index 0000000..e1a8785 --- /dev/null +++ b/rmtDev/video 28/src/common.js @@ -0,0 +1,42 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; + +// STATE +export const state = { + searchJobItems: [] +}; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// HELPER / UTILITY FUNCTIONS +export const getData = async completeURL => { + const response = await fetch(completeURL); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + return data; +}; \ No newline at end of file diff --git a/rmtDev/video 28/src/components/Error.js b/rmtDev/video 28/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 28/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 28/src/components/JobDetails.js b/rmtDev/video 28/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 28/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 28/src/components/JobList.js b/rmtDev/video 28/src/components/JobList.js new file mode 100644 index 0000000..6bb7252 --- /dev/null +++ b/rmtDev/video 28/src/components/JobList.js @@ -0,0 +1,84 @@ +import { + BASE_API_URL, + state, + jobListSearchEl, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = () => { + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // display job items + state.searchJobItems.slice(0, 7).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 28/src/components/Search.js b/rmtDev/video 28/src/components/Search.js new file mode 100644 index 0000000..133a652 --- /dev/null +++ b/rmtDev/video 28/src/components/Search.js @@ -0,0 +1,62 @@ +import { + BASE_API_URL, + state, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl, + getData +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // render spinner + renderSpinner('search'); + + try { + // fetch search results + const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); + + // extract job items + const { jobItems } = data; + + // update state + state.searchJobItems = jobItems; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // render job items in search job list + renderJobList(); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 28/src/components/Sorting.js b/rmtDev/video 28/src/components/Sorting.js new file mode 100644 index 0000000..a93ed06 --- /dev/null +++ b/rmtDev/video 28/src/components/Sorting.js @@ -0,0 +1,44 @@ +import { + state, + sortingEl, + sortingBtnRecentEl, + sortingBtnRelevantEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.sorting__button'); + + // stop function if no clicked button element + if (!clickedButtonEl) return; + + // check if intention is recent or relevant sorting + const recent = clickedButtonEl.className.includes('--recent') ? true : false; + + // make sorting button look (in)active + if (recent) { + sortingBtnRecentEl.classList.add('sorting__button--active'); + sortingBtnRelevantEl.classList.remove('sorting__button--active'); + } else { + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + } + + // sort job items + // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same + if (recent) { + state.searchJobItems.sort((a, b) => { + return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. + }); + } else { + state.searchJobItems.sort((a, b) => { + return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. + }); + } + + // render job items in list + renderJobList(); +}; + +sortingEl.addEventListener('click', clickHandler); diff --git a/rmtDev/video 28/src/components/Spinner.js b/rmtDev/video 28/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 28/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 29/index.css b/rmtDev/video 29/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 29/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 29/index.html b/rmtDev/video 29/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 29/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 29/index.js b/rmtDev/video 29/index.js new file mode 100644 index 0000000..d2d52d4 --- /dev/null +++ b/rmtDev/video 29/index.js @@ -0,0 +1,7 @@ +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Pagination.js'; +import './src/components/Search.js'; +import './src/components/Sorting.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 29/src/common.js b/rmtDev/video 29/src/common.js new file mode 100644 index 0000000..696b860 --- /dev/null +++ b/rmtDev/video 29/src/common.js @@ -0,0 +1,44 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; +export const RESULTS_PER_PAGE = 7; + +// STATE +export const state = { + searchJobItems: [], + currentPage: 1 +}; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// HELPER / UTILITY FUNCTIONS +export const getData = async completeURL => { + const response = await fetch(completeURL); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + return data; +}; \ No newline at end of file diff --git a/rmtDev/video 29/src/components/Error.js b/rmtDev/video 29/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 29/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 29/src/components/JobDetails.js b/rmtDev/video 29/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 29/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 29/src/components/JobList.js b/rmtDev/video 29/src/components/JobList.js new file mode 100644 index 0000000..fd55aaf --- /dev/null +++ b/rmtDev/video 29/src/components/JobList.js @@ -0,0 +1,85 @@ +import { + BASE_API_URL, + RESULTS_PER_PAGE, + state, + jobListSearchEl, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = () => { + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // display job items + state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 29/src/components/Pagination.js b/rmtDev/video 29/src/components/Pagination.js new file mode 100644 index 0000000..720f1c7 --- /dev/null +++ b/rmtDev/video 29/src/components/Pagination.js @@ -0,0 +1,58 @@ +import { + RESULTS_PER_PAGE, + state, + paginationEl, + paginationNumberNextEl, + paginationNumberBackEl, + paginationBtnNextEl, + paginationBtnBackEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const renderPaginationButtons = () => { + // display back button if we are on page 2 or further + if (state.currentPage >= 2) { + paginationBtnBackEl.classList.remove('pagination__button--hidden'); + } else { + paginationBtnBackEl.classList.add('pagination__button--hidden'); + } + + // display next button if there are more job items on next page + if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { + paginationBtnNextEl.classList.add('pagination__button--hidden'); + } else { + paginationBtnNextEl.classList.remove('pagination__button--hidden'); + } + + // update page numbers + paginationNumberNextEl.textContent = state.currentPage + 1; + paginationNumberBackEl.textContent = state.currentPage - 1; + + // unfocus ('blur') buttons + paginationBtnNextEl.blur(); + paginationBtnBackEl.blur(); +}; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.pagination__button'); + + // stop function if null + if (!clickedButtonEl) return; + + // check if intention is next or back + const nextPage = clickedButtonEl.className.includes('--next') ? true : false; + + // update state + nextPage ? state.currentPage++ : state.currentPage--; + + // render pagination buttons + renderPaginationButtons(); + + // render job items for that page + renderJobList(); +}; + +paginationEl.addEventListener('click', clickHandler); + +export default renderPaginationButtons; \ No newline at end of file diff --git a/rmtDev/video 29/src/components/Search.js b/rmtDev/video 29/src/components/Search.js new file mode 100644 index 0000000..e672606 --- /dev/null +++ b/rmtDev/video 29/src/components/Search.js @@ -0,0 +1,73 @@ +import { + BASE_API_URL, + state, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl, + sortingBtnRecentEl, + sortingBtnRelevantEl, + getData +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // reset sorting buttons + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + + // render spinner + renderSpinner('search'); + + try { + // fetch search results + const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); + + // extract job items + const { jobItems } = data; + + // update state + state.searchJobItems = jobItems; + state.currentPage = 1; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in search job list + renderJobList(); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 29/src/components/Sorting.js b/rmtDev/video 29/src/components/Sorting.js new file mode 100644 index 0000000..cd08f17 --- /dev/null +++ b/rmtDev/video 29/src/components/Sorting.js @@ -0,0 +1,51 @@ +import { + state, + sortingEl, + sortingBtnRecentEl, + sortingBtnRelevantEl +} from '../common.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.sorting__button'); + + // stop function if no clicked button element + if (!clickedButtonEl) return; + + // update state (reset to page 1) + state.currentPage = 1; + + // check if intention is recent or relevant sorting + const recent = clickedButtonEl.className.includes('--recent') ? true : false; + + // make sorting button look (in)active + if (recent) { + sortingBtnRecentEl.classList.add('sorting__button--active'); + sortingBtnRelevantEl.classList.remove('sorting__button--active'); + } else { + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + } + + // sort job items + // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same + if (recent) { + state.searchJobItems.sort((a, b) => { + return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. + }); + } else { + state.searchJobItems.sort((a, b) => { + return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. + }); + } + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in list + renderJobList(); +}; + +sortingEl.addEventListener('click', clickHandler); diff --git a/rmtDev/video 29/src/components/Spinner.js b/rmtDev/video 29/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 29/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 30/index.css b/rmtDev/video 30/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 30/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 30/index.html b/rmtDev/video 30/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 30/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 30/index.js b/rmtDev/video 30/index.js new file mode 100644 index 0000000..e88260f --- /dev/null +++ b/rmtDev/video 30/index.js @@ -0,0 +1,8 @@ +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Pagination.js'; +import './src/components/Router.js'; +import './src/components/Search.js'; +import './src/components/Sorting.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 30/src/common.js b/rmtDev/video 30/src/common.js new file mode 100644 index 0000000..696b860 --- /dev/null +++ b/rmtDev/video 30/src/common.js @@ -0,0 +1,44 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; +export const RESULTS_PER_PAGE = 7; + +// STATE +export const state = { + searchJobItems: [], + currentPage: 1 +}; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// HELPER / UTILITY FUNCTIONS +export const getData = async completeURL => { + const response = await fetch(completeURL); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + return data; +}; \ No newline at end of file diff --git a/rmtDev/video 30/src/components/Error.js b/rmtDev/video 30/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 30/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 30/src/components/JobDetails.js b/rmtDev/video 30/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 30/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 30/src/components/JobList.js b/rmtDev/video 30/src/components/JobList.js new file mode 100644 index 0000000..83c625e --- /dev/null +++ b/rmtDev/video 30/src/components/JobList.js @@ -0,0 +1,88 @@ +import { + BASE_API_URL, + RESULTS_PER_PAGE, + state, + jobListSearchEl, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = () => { + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // display job items + state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // add id to url + history.pushState(null, '', `/#${id}`); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 30/src/components/Pagination.js b/rmtDev/video 30/src/components/Pagination.js new file mode 100644 index 0000000..720f1c7 --- /dev/null +++ b/rmtDev/video 30/src/components/Pagination.js @@ -0,0 +1,58 @@ +import { + RESULTS_PER_PAGE, + state, + paginationEl, + paginationNumberNextEl, + paginationNumberBackEl, + paginationBtnNextEl, + paginationBtnBackEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const renderPaginationButtons = () => { + // display back button if we are on page 2 or further + if (state.currentPage >= 2) { + paginationBtnBackEl.classList.remove('pagination__button--hidden'); + } else { + paginationBtnBackEl.classList.add('pagination__button--hidden'); + } + + // display next button if there are more job items on next page + if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { + paginationBtnNextEl.classList.add('pagination__button--hidden'); + } else { + paginationBtnNextEl.classList.remove('pagination__button--hidden'); + } + + // update page numbers + paginationNumberNextEl.textContent = state.currentPage + 1; + paginationNumberBackEl.textContent = state.currentPage - 1; + + // unfocus ('blur') buttons + paginationBtnNextEl.blur(); + paginationBtnBackEl.blur(); +}; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.pagination__button'); + + // stop function if null + if (!clickedButtonEl) return; + + // check if intention is next or back + const nextPage = clickedButtonEl.className.includes('--next') ? true : false; + + // update state + nextPage ? state.currentPage++ : state.currentPage--; + + // render pagination buttons + renderPaginationButtons(); + + // render job items for that page + renderJobList(); +}; + +paginationEl.addEventListener('click', clickHandler); + +export default renderPaginationButtons; \ No newline at end of file diff --git a/rmtDev/video 30/src/components/Router.js b/rmtDev/video 30/src/components/Router.js new file mode 100644 index 0000000..1900378 --- /dev/null +++ b/rmtDev/video 30/src/components/Router.js @@ -0,0 +1,40 @@ +import { + BASE_API_URL, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; + +const loadHashChangeHandler = async () => { + // get id from url + const id = window.location.hash.substring(1); + + if (id) { + // remove previous job details content + jobDetailsContentEl.innerHTML = ''; + + // add spinner + renderSpinner('job-details'); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } + } +}; + +window.addEventListener('DOMContentLoaded', loadHashChangeHandler); +window.addEventListener('hashchange', loadHashChangeHandler); \ No newline at end of file diff --git a/rmtDev/video 30/src/components/Search.js b/rmtDev/video 30/src/components/Search.js new file mode 100644 index 0000000..e672606 --- /dev/null +++ b/rmtDev/video 30/src/components/Search.js @@ -0,0 +1,73 @@ +import { + BASE_API_URL, + state, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl, + sortingBtnRecentEl, + sortingBtnRelevantEl, + getData +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // reset sorting buttons + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + + // render spinner + renderSpinner('search'); + + try { + // fetch search results + const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); + + // extract job items + const { jobItems } = data; + + // update state + state.searchJobItems = jobItems; + state.currentPage = 1; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in search job list + renderJobList(); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 30/src/components/Sorting.js b/rmtDev/video 30/src/components/Sorting.js new file mode 100644 index 0000000..cd08f17 --- /dev/null +++ b/rmtDev/video 30/src/components/Sorting.js @@ -0,0 +1,51 @@ +import { + state, + sortingEl, + sortingBtnRecentEl, + sortingBtnRelevantEl +} from '../common.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.sorting__button'); + + // stop function if no clicked button element + if (!clickedButtonEl) return; + + // update state (reset to page 1) + state.currentPage = 1; + + // check if intention is recent or relevant sorting + const recent = clickedButtonEl.className.includes('--recent') ? true : false; + + // make sorting button look (in)active + if (recent) { + sortingBtnRecentEl.classList.add('sorting__button--active'); + sortingBtnRelevantEl.classList.remove('sorting__button--active'); + } else { + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + } + + // sort job items + // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same + if (recent) { + state.searchJobItems.sort((a, b) => { + return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. + }); + } else { + state.searchJobItems.sort((a, b) => { + return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. + }); + } + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in list + renderJobList(); +}; + +sortingEl.addEventListener('click', clickHandler); diff --git a/rmtDev/video 30/src/components/Spinner.js b/rmtDev/video 30/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 30/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 31/index.css b/rmtDev/video 31/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 31/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 31/index.html b/rmtDev/video 31/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 31/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 31/index.js b/rmtDev/video 31/index.js new file mode 100644 index 0000000..e88260f --- /dev/null +++ b/rmtDev/video 31/index.js @@ -0,0 +1,8 @@ +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Pagination.js'; +import './src/components/Router.js'; +import './src/components/Search.js'; +import './src/components/Sorting.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 31/src/common.js b/rmtDev/video 31/src/common.js new file mode 100644 index 0000000..6015300 --- /dev/null +++ b/rmtDev/video 31/src/common.js @@ -0,0 +1,45 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; +export const RESULTS_PER_PAGE = 7; + +// STATE +export const state = { + searchJobItems: [], + activeJobItem: {}, + currentPage: 1 +}; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// HELPER / UTILITY FUNCTIONS +export const getData = async completeURL => { + const response = await fetch(completeURL); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + return data; +}; \ No newline at end of file diff --git a/rmtDev/video 31/src/components/Error.js b/rmtDev/video 31/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 31/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 31/src/components/JobDetails.js b/rmtDev/video 31/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 31/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 31/src/components/JobList.js b/rmtDev/video 31/src/components/JobList.js new file mode 100644 index 0000000..3cf5983 --- /dev/null +++ b/rmtDev/video 31/src/components/JobList.js @@ -0,0 +1,91 @@ +import { + BASE_API_URL, + RESULTS_PER_PAGE, + state, + jobListSearchEl, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = () => { + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // display job items + state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE).forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job item + document.querySelector('.job-item--active')?.classList.remove('job-item--active'); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // update state + state.activeJobItem = state.searchJobItems.find(jobItem => jobItem.id === +id); + + // add id to url + history.pushState(null, '', `/#${id}`); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 31/src/components/Pagination.js b/rmtDev/video 31/src/components/Pagination.js new file mode 100644 index 0000000..720f1c7 --- /dev/null +++ b/rmtDev/video 31/src/components/Pagination.js @@ -0,0 +1,58 @@ +import { + RESULTS_PER_PAGE, + state, + paginationEl, + paginationNumberNextEl, + paginationNumberBackEl, + paginationBtnNextEl, + paginationBtnBackEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const renderPaginationButtons = () => { + // display back button if we are on page 2 or further + if (state.currentPage >= 2) { + paginationBtnBackEl.classList.remove('pagination__button--hidden'); + } else { + paginationBtnBackEl.classList.add('pagination__button--hidden'); + } + + // display next button if there are more job items on next page + if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { + paginationBtnNextEl.classList.add('pagination__button--hidden'); + } else { + paginationBtnNextEl.classList.remove('pagination__button--hidden'); + } + + // update page numbers + paginationNumberNextEl.textContent = state.currentPage + 1; + paginationNumberBackEl.textContent = state.currentPage - 1; + + // unfocus ('blur') buttons + paginationBtnNextEl.blur(); + paginationBtnBackEl.blur(); +}; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.pagination__button'); + + // stop function if null + if (!clickedButtonEl) return; + + // check if intention is next or back + const nextPage = clickedButtonEl.className.includes('--next') ? true : false; + + // update state + nextPage ? state.currentPage++ : state.currentPage--; + + // render pagination buttons + renderPaginationButtons(); + + // render job items for that page + renderJobList(); +}; + +paginationEl.addEventListener('click', clickHandler); + +export default renderPaginationButtons; \ No newline at end of file diff --git a/rmtDev/video 31/src/components/Router.js b/rmtDev/video 31/src/components/Router.js new file mode 100644 index 0000000..b1d0be8 --- /dev/null +++ b/rmtDev/video 31/src/components/Router.js @@ -0,0 +1,45 @@ +import { + BASE_API_URL, + state, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const loadHashChangeHandler = async () => { + // get id from url + const id = window.location.hash.substring(1); + + if (id) { + // remove previous job details content + jobDetailsContentEl.innerHTML = ''; + + // add spinner + renderSpinner('job-details'); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // update state + state.activeJobItem = jobItem; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } + } +}; + +window.addEventListener('DOMContentLoaded', loadHashChangeHandler); +window.addEventListener('hashchange', loadHashChangeHandler); \ No newline at end of file diff --git a/rmtDev/video 31/src/components/Search.js b/rmtDev/video 31/src/components/Search.js new file mode 100644 index 0000000..e672606 --- /dev/null +++ b/rmtDev/video 31/src/components/Search.js @@ -0,0 +1,73 @@ +import { + BASE_API_URL, + state, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl, + sortingBtnRecentEl, + sortingBtnRelevantEl, + getData +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // reset sorting buttons + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + + // render spinner + renderSpinner('search'); + + try { + // fetch search results + const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); + + // extract job items + const { jobItems } = data; + + // update state + state.searchJobItems = jobItems; + state.currentPage = 1; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in search job list + renderJobList(); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 31/src/components/Sorting.js b/rmtDev/video 31/src/components/Sorting.js new file mode 100644 index 0000000..cd08f17 --- /dev/null +++ b/rmtDev/video 31/src/components/Sorting.js @@ -0,0 +1,51 @@ +import { + state, + sortingEl, + sortingBtnRecentEl, + sortingBtnRelevantEl +} from '../common.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.sorting__button'); + + // stop function if no clicked button element + if (!clickedButtonEl) return; + + // update state (reset to page 1) + state.currentPage = 1; + + // check if intention is recent or relevant sorting + const recent = clickedButtonEl.className.includes('--recent') ? true : false; + + // make sorting button look (in)active + if (recent) { + sortingBtnRecentEl.classList.add('sorting__button--active'); + sortingBtnRelevantEl.classList.remove('sorting__button--active'); + } else { + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + } + + // sort job items + // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same + if (recent) { + state.searchJobItems.sort((a, b) => { + return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. + }); + } else { + state.searchJobItems.sort((a, b) => { + return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. + }); + } + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in list + renderJobList(); +}; + +sortingEl.addEventListener('click', clickHandler); diff --git a/rmtDev/video 31/src/components/Spinner.js b/rmtDev/video 31/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 31/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 32/index.css b/rmtDev/video 32/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 32/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 32/index.html b/rmtDev/video 32/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 32/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 32/index.js b/rmtDev/video 32/index.js new file mode 100644 index 0000000..fe6eede --- /dev/null +++ b/rmtDev/video 32/index.js @@ -0,0 +1,9 @@ +import './src/components/Bookmarks.js'; +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Pagination.js'; +import './src/components/Router.js'; +import './src/components/Search.js'; +import './src/components/Sorting.js'; +import './src/components/Spinner.js'; \ No newline at end of file diff --git a/rmtDev/video 32/src/common.js b/rmtDev/video 32/src/common.js new file mode 100644 index 0000000..34e739a --- /dev/null +++ b/rmtDev/video 32/src/common.js @@ -0,0 +1,46 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; +export const RESULTS_PER_PAGE = 7; + +// STATE +export const state = { + searchJobItems: [], + bookmarkJobItems: [], + activeJobItem: {}, + currentPage: 1 +}; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// HELPER / UTILITY FUNCTIONS +export const getData = async completeURL => { + const response = await fetch(completeURL); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + return data; +}; \ No newline at end of file diff --git a/rmtDev/video 32/src/components/Bookmarks.js b/rmtDev/video 32/src/components/Bookmarks.js new file mode 100644 index 0000000..674ce47 --- /dev/null +++ b/rmtDev/video 32/src/components/Bookmarks.js @@ -0,0 +1,45 @@ +import { + state, + bookmarksBtnEl, + jobDetailsEl, + jobListBookmarksEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const clickHandler = event => { + // don't continue if click was outside bookmark button + if (!event.target.className.includes('bookmark')) return; + + // update state + if (state.bookmarkJobItems.some(bookmarkJobItem => bookmarkJobItem.id === state.activeJobItem.id)) { + state.bookmarkJobItems = state.bookmarkJobItems.filter(bookmarkJobItem => bookmarkJobItem.id !== state.activeJobItem.id); + } else { + state.bookmarkJobItems.push(state.activeJobItem); + } + + // update bookmark icon + document.querySelector('.job-info__bookmark-icon').classList.toggle('job-info__bookmark-icon--bookmarked'); +}; + +const mouseEnterHandler = () => { + // make bookmarks button look active + bookmarksBtnEl.classList.add('bookmarks-btn--active'); + + // make job list visible + jobListBookmarksEl.classList.add('job-list--visible'); + + // render bookmarks job list + renderJobList('bookmarks'); +}; + +const mouseLeaveHandler = () => { + // make bookmarks button look inactive + bookmarksBtnEl.classList.remove('bookmarks-btn--active'); + + // make job list invisible + jobListBookmarksEl.classList.remove('job-list--visible'); +}; + +jobDetailsEl.addEventListener('click', clickHandler); +bookmarksBtnEl.addEventListener('mouseenter', mouseEnterHandler); +jobListBookmarksEl.addEventListener('mouseleave', mouseLeaveHandler); \ No newline at end of file diff --git a/rmtDev/video 32/src/components/Error.js b/rmtDev/video 32/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 32/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 32/src/components/JobDetails.js b/rmtDev/video 32/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 32/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 32/src/components/JobList.js b/rmtDev/video 32/src/components/JobList.js new file mode 100644 index 0000000..980e04c --- /dev/null +++ b/rmtDev/video 32/src/components/JobList.js @@ -0,0 +1,104 @@ +import { + BASE_API_URL, + RESULTS_PER_PAGE, + state, + jobListSearchEl, + jobListBookmarksEl, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = (whichJobList = 'search') => { + // determine correct selector for job list (search results list or bookmarks list) + const jobListEl = whichJobList === 'search' ? jobListSearchEl : jobListBookmarksEl; + + // remove previous job items + jobListEl.innerHTML = ''; + + // determine the job items that should be rendered + let jobItems; + if (whichJobList === 'search') { + jobItems = state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE); + } else if (whichJobList === 'bookmarks') { + jobItems = state.bookmarkJobItems; + } + + // display job items + jobItems.forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job items + document.querySelectorAll('.job-item--active').forEach(jobItemWithActiveClass => jobItemWithActiveClass.classList.remove('job-item--active')); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // update state + state.activeJobItem = state.searchJobItems.find(jobItem => jobItem.id === +id); + + // add id to url + history.pushState(null, '', `/#${id}`); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); +jobListBookmarksEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 32/src/components/Pagination.js b/rmtDev/video 32/src/components/Pagination.js new file mode 100644 index 0000000..720f1c7 --- /dev/null +++ b/rmtDev/video 32/src/components/Pagination.js @@ -0,0 +1,58 @@ +import { + RESULTS_PER_PAGE, + state, + paginationEl, + paginationNumberNextEl, + paginationNumberBackEl, + paginationBtnNextEl, + paginationBtnBackEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const renderPaginationButtons = () => { + // display back button if we are on page 2 or further + if (state.currentPage >= 2) { + paginationBtnBackEl.classList.remove('pagination__button--hidden'); + } else { + paginationBtnBackEl.classList.add('pagination__button--hidden'); + } + + // display next button if there are more job items on next page + if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { + paginationBtnNextEl.classList.add('pagination__button--hidden'); + } else { + paginationBtnNextEl.classList.remove('pagination__button--hidden'); + } + + // update page numbers + paginationNumberNextEl.textContent = state.currentPage + 1; + paginationNumberBackEl.textContent = state.currentPage - 1; + + // unfocus ('blur') buttons + paginationBtnNextEl.blur(); + paginationBtnBackEl.blur(); +}; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.pagination__button'); + + // stop function if null + if (!clickedButtonEl) return; + + // check if intention is next or back + const nextPage = clickedButtonEl.className.includes('--next') ? true : false; + + // update state + nextPage ? state.currentPage++ : state.currentPage--; + + // render pagination buttons + renderPaginationButtons(); + + // render job items for that page + renderJobList(); +}; + +paginationEl.addEventListener('click', clickHandler); + +export default renderPaginationButtons; \ No newline at end of file diff --git a/rmtDev/video 32/src/components/Router.js b/rmtDev/video 32/src/components/Router.js new file mode 100644 index 0000000..b1d0be8 --- /dev/null +++ b/rmtDev/video 32/src/components/Router.js @@ -0,0 +1,45 @@ +import { + BASE_API_URL, + state, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const loadHashChangeHandler = async () => { + // get id from url + const id = window.location.hash.substring(1); + + if (id) { + // remove previous job details content + jobDetailsContentEl.innerHTML = ''; + + // add spinner + renderSpinner('job-details'); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // update state + state.activeJobItem = jobItem; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } + } +}; + +window.addEventListener('DOMContentLoaded', loadHashChangeHandler); +window.addEventListener('hashchange', loadHashChangeHandler); \ No newline at end of file diff --git a/rmtDev/video 32/src/components/Search.js b/rmtDev/video 32/src/components/Search.js new file mode 100644 index 0000000..e672606 --- /dev/null +++ b/rmtDev/video 32/src/components/Search.js @@ -0,0 +1,73 @@ +import { + BASE_API_URL, + state, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl, + sortingBtnRecentEl, + sortingBtnRelevantEl, + getData +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // reset sorting buttons + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + + // render spinner + renderSpinner('search'); + + try { + // fetch search results + const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); + + // extract job items + const { jobItems } = data; + + // update state + state.searchJobItems = jobItems; + state.currentPage = 1; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in search job list + renderJobList(); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 32/src/components/Sorting.js b/rmtDev/video 32/src/components/Sorting.js new file mode 100644 index 0000000..cd08f17 --- /dev/null +++ b/rmtDev/video 32/src/components/Sorting.js @@ -0,0 +1,51 @@ +import { + state, + sortingEl, + sortingBtnRecentEl, + sortingBtnRelevantEl +} from '../common.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.sorting__button'); + + // stop function if no clicked button element + if (!clickedButtonEl) return; + + // update state (reset to page 1) + state.currentPage = 1; + + // check if intention is recent or relevant sorting + const recent = clickedButtonEl.className.includes('--recent') ? true : false; + + // make sorting button look (in)active + if (recent) { + sortingBtnRecentEl.classList.add('sorting__button--active'); + sortingBtnRelevantEl.classList.remove('sorting__button--active'); + } else { + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + } + + // sort job items + // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same + if (recent) { + state.searchJobItems.sort((a, b) => { + return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. + }); + } else { + state.searchJobItems.sort((a, b) => { + return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. + }); + } + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in list + renderJobList(); +}; + +sortingEl.addEventListener('click', clickHandler); diff --git a/rmtDev/video 32/src/components/Spinner.js b/rmtDev/video 32/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 32/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 33/index.css b/rmtDev/video 33/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 33/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 33/index.html b/rmtDev/video 33/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 33/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 33/index.js b/rmtDev/video 33/index.js new file mode 100644 index 0000000..e51a258 --- /dev/null +++ b/rmtDev/video 33/index.js @@ -0,0 +1,10 @@ +import './src/components/Bookmarks.js'; +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Pagination.js'; +import './src/components/Router.js'; +import './src/components/Search.js'; +import './src/components/Sorting.js'; +import './src/components/Spinner.js'; +import './src/components/Storage.js'; \ No newline at end of file diff --git a/rmtDev/video 33/src/common.js b/rmtDev/video 33/src/common.js new file mode 100644 index 0000000..34e739a --- /dev/null +++ b/rmtDev/video 33/src/common.js @@ -0,0 +1,46 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; +export const RESULTS_PER_PAGE = 7; + +// STATE +export const state = { + searchJobItems: [], + bookmarkJobItems: [], + activeJobItem: {}, + currentPage: 1 +}; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// HELPER / UTILITY FUNCTIONS +export const getData = async completeURL => { + const response = await fetch(completeURL); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + return data; +}; \ No newline at end of file diff --git a/rmtDev/video 33/src/components/Bookmarks.js b/rmtDev/video 33/src/components/Bookmarks.js new file mode 100644 index 0000000..6e21073 --- /dev/null +++ b/rmtDev/video 33/src/components/Bookmarks.js @@ -0,0 +1,48 @@ +import { + state, + bookmarksBtnEl, + jobDetailsEl, + jobListBookmarksEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const clickHandler = event => { + // don't continue if click was outside bookmark button + if (!event.target.className.includes('bookmark')) return; + + // update state + if (state.bookmarkJobItems.some(bookmarkJobItem => bookmarkJobItem.id === state.activeJobItem.id)) { + state.bookmarkJobItems = state.bookmarkJobItems.filter(bookmarkJobItem => bookmarkJobItem.id !== state.activeJobItem.id); + } else { + state.bookmarkJobItems.push(state.activeJobItem); + } + + // persist data with localstorage + localStorage.setItem('bookmarkJobItems', JSON.stringify(state.bookmarkJobItems)); + + // update bookmark icon + document.querySelector('.job-info__bookmark-icon').classList.toggle('job-info__bookmark-icon--bookmarked'); +}; + +const mouseEnterHandler = () => { + // make bookmarks button look active + bookmarksBtnEl.classList.add('bookmarks-btn--active'); + + // make job list visible + jobListBookmarksEl.classList.add('job-list--visible'); + + // render bookmarks job list + renderJobList('bookmarks'); +}; + +const mouseLeaveHandler = () => { + // make bookmarks button look inactive + bookmarksBtnEl.classList.remove('bookmarks-btn--active'); + + // make job list invisible + jobListBookmarksEl.classList.remove('job-list--visible'); +}; + +jobDetailsEl.addEventListener('click', clickHandler); +bookmarksBtnEl.addEventListener('mouseenter', mouseEnterHandler); +jobListBookmarksEl.addEventListener('mouseleave', mouseLeaveHandler); \ No newline at end of file diff --git a/rmtDev/video 33/src/components/Error.js b/rmtDev/video 33/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 33/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 33/src/components/JobDetails.js b/rmtDev/video 33/src/components/JobDetails.js new file mode 100644 index 0000000..e66277d --- /dev/null +++ b/rmtDev/video 33/src/components/JobDetails.js @@ -0,0 +1,62 @@ +import { + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 33/src/components/JobList.js b/rmtDev/video 33/src/components/JobList.js new file mode 100644 index 0000000..58cdfe9 --- /dev/null +++ b/rmtDev/video 33/src/components/JobList.js @@ -0,0 +1,105 @@ +import { + BASE_API_URL, + RESULTS_PER_PAGE, + state, + jobListSearchEl, + jobListBookmarksEl, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = (whichJobList = 'search') => { + // determine correct selector for job list (search results list or bookmarks list) + const jobListEl = whichJobList === 'search' ? jobListSearchEl : jobListBookmarksEl; + + // remove previous job items + jobListEl.innerHTML = ''; + + // determine the job items that should be rendered + let jobItems; + if (whichJobList === 'search') { + jobItems = state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE); + } else if (whichJobList === 'bookmarks') { + jobItems = state.bookmarkJobItems; + } + + // display job items + jobItems.forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job items + document.querySelectorAll('.job-item--active').forEach(jobItemWithActiveClass => jobItemWithActiveClass.classList.remove('job-item--active')); + + // add active class + jobItemEl.classList.add('job-item--active'); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // update state + const allJobItems = [...state.searchJobItems, ...state.bookmarkJobItems]; + state.activeJobItem = allJobItems.find(jobItem => jobItem.id === +id); + + // add id to url + history.pushState(null, '', `/#${id}`); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); +jobListBookmarksEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 33/src/components/Pagination.js b/rmtDev/video 33/src/components/Pagination.js new file mode 100644 index 0000000..720f1c7 --- /dev/null +++ b/rmtDev/video 33/src/components/Pagination.js @@ -0,0 +1,58 @@ +import { + RESULTS_PER_PAGE, + state, + paginationEl, + paginationNumberNextEl, + paginationNumberBackEl, + paginationBtnNextEl, + paginationBtnBackEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const renderPaginationButtons = () => { + // display back button if we are on page 2 or further + if (state.currentPage >= 2) { + paginationBtnBackEl.classList.remove('pagination__button--hidden'); + } else { + paginationBtnBackEl.classList.add('pagination__button--hidden'); + } + + // display next button if there are more job items on next page + if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { + paginationBtnNextEl.classList.add('pagination__button--hidden'); + } else { + paginationBtnNextEl.classList.remove('pagination__button--hidden'); + } + + // update page numbers + paginationNumberNextEl.textContent = state.currentPage + 1; + paginationNumberBackEl.textContent = state.currentPage - 1; + + // unfocus ('blur') buttons + paginationBtnNextEl.blur(); + paginationBtnBackEl.blur(); +}; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.pagination__button'); + + // stop function if null + if (!clickedButtonEl) return; + + // check if intention is next or back + const nextPage = clickedButtonEl.className.includes('--next') ? true : false; + + // update state + nextPage ? state.currentPage++ : state.currentPage--; + + // render pagination buttons + renderPaginationButtons(); + + // render job items for that page + renderJobList(); +}; + +paginationEl.addEventListener('click', clickHandler); + +export default renderPaginationButtons; \ No newline at end of file diff --git a/rmtDev/video 33/src/components/Router.js b/rmtDev/video 33/src/components/Router.js new file mode 100644 index 0000000..b1d0be8 --- /dev/null +++ b/rmtDev/video 33/src/components/Router.js @@ -0,0 +1,45 @@ +import { + BASE_API_URL, + state, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const loadHashChangeHandler = async () => { + // get id from url + const id = window.location.hash.substring(1); + + if (id) { + // remove previous job details content + jobDetailsContentEl.innerHTML = ''; + + // add spinner + renderSpinner('job-details'); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // update state + state.activeJobItem = jobItem; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } + } +}; + +window.addEventListener('DOMContentLoaded', loadHashChangeHandler); +window.addEventListener('hashchange', loadHashChangeHandler); \ No newline at end of file diff --git a/rmtDev/video 33/src/components/Search.js b/rmtDev/video 33/src/components/Search.js new file mode 100644 index 0000000..e672606 --- /dev/null +++ b/rmtDev/video 33/src/components/Search.js @@ -0,0 +1,73 @@ +import { + BASE_API_URL, + state, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl, + sortingBtnRecentEl, + sortingBtnRelevantEl, + getData +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // reset sorting buttons + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + + // render spinner + renderSpinner('search'); + + try { + // fetch search results + const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); + + // extract job items + const { jobItems } = data; + + // update state + state.searchJobItems = jobItems; + state.currentPage = 1; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in search job list + renderJobList(); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 33/src/components/Sorting.js b/rmtDev/video 33/src/components/Sorting.js new file mode 100644 index 0000000..cd08f17 --- /dev/null +++ b/rmtDev/video 33/src/components/Sorting.js @@ -0,0 +1,51 @@ +import { + state, + sortingEl, + sortingBtnRecentEl, + sortingBtnRelevantEl +} from '../common.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.sorting__button'); + + // stop function if no clicked button element + if (!clickedButtonEl) return; + + // update state (reset to page 1) + state.currentPage = 1; + + // check if intention is recent or relevant sorting + const recent = clickedButtonEl.className.includes('--recent') ? true : false; + + // make sorting button look (in)active + if (recent) { + sortingBtnRecentEl.classList.add('sorting__button--active'); + sortingBtnRelevantEl.classList.remove('sorting__button--active'); + } else { + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + } + + // sort job items + // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same + if (recent) { + state.searchJobItems.sort((a, b) => { + return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. + }); + } else { + state.searchJobItems.sort((a, b) => { + return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. + }); + } + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in list + renderJobList(); +}; + +sortingEl.addEventListener('click', clickHandler); diff --git a/rmtDev/video 33/src/components/Spinner.js b/rmtDev/video 33/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 33/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 33/src/components/Storage.js b/rmtDev/video 33/src/components/Storage.js new file mode 100644 index 0000000..2bb6fbe --- /dev/null +++ b/rmtDev/video 33/src/components/Storage.js @@ -0,0 +1,8 @@ +import { + state +} from '../common.js'; + +const storedJobItems = localStorage.getItem('bookmarkJobItems'); +if (storedJobItems) { + state.bookmarkJobItems = JSON.parse(storedJobItems); +} \ No newline at end of file diff --git a/rmtDev/video 34/index.css b/rmtDev/video 34/index.css new file mode 100644 index 0000000..b624c33 --- /dev/null +++ b/rmtDev/video 34/index.css @@ -0,0 +1,1130 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +.body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 13px; + font-weight: 600; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background__pattern { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { +} + +.job-list--bookmarks { + visibility: hidden; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + opacity: 0; + transform: scale(0.9) translateX(-50%); + transform-origin: left; + transition: all 0.2s; + pointer-events: none; + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--bookmarks::before { + content: 'Nothing bookmarked yet...'; + position: absolute; + top: 49%; + left: 50%; + transform: translate(-50%, -50%); + z-index: -1; + font-size: 13px; + color: rgb(97, 98, 104); +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + padding: 14px 20px; + cursor: pointer; + transition: all 0.2s; + background-color: #fff; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + background-color: #8dd335; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +.job-item:nth-child(4n+2) .job-item__badge { + background-color: #3D87F1; +} + +.job-item:nth-child(4n+3) .job-item__badge { + background-color: #D2D631; +} + +.job-item:nth-child(4n+4) .job-item__badge { + background-color: #D96A46; +} + +.job-item__middle { +} + +.job-item__company { + font-size: 12px; + margin-bottom: 2px; + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.job-item__bookmark-icon { + font-size: 14px; + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 10px; + margin-top: 4px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; +} + +.search__submit-btn { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search__icon { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search__input { + height: 56px; + width: 610px; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search__input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search__input:hover, +.search__input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search__input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search__input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.search-results__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +.count__number { +} + +/* SORTING */ +.sorting { +} + +.sorting__icon { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--back { +} + +.pagination__button--next { +} + +.pagination__number { + font-weight: 500; +} + +.pagination__number--back { +} + +.pagination__number--next { +} + +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-text { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details__start-text--big { + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__content { + height: 100%; +} + +.job-details__cover-img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.job-info__bookmark-btn { + cursor: pointer; +} + +.job-info__bookmark-btn:hover .job-info__bookmark-icon { + color: #2671dd; +} + +.job-info__bookmark-icon { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.job-info__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: '“'; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.copyright { + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + line-height: 1.2; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.jobs-available { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + visibility: hidden; +} + +.spinner--search { + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .copyright { + text-align: center; + } + + .jobs-available { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtDev/video 34/index.html b/rmtDev/video 34/index.html new file mode 100644 index 0000000..d930bd5 --- /dev/null +++ b/rmtDev/video 34/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 34/index.js b/rmtDev/video 34/index.js new file mode 100644 index 0000000..e51a258 --- /dev/null +++ b/rmtDev/video 34/index.js @@ -0,0 +1,10 @@ +import './src/components/Bookmarks.js'; +import './src/components/Error.js'; +import './src/components/JobDetails.js'; +import './src/components/JobList.js'; +import './src/components/Pagination.js'; +import './src/components/Router.js'; +import './src/components/Search.js'; +import './src/components/Sorting.js'; +import './src/components/Spinner.js'; +import './src/components/Storage.js'; \ No newline at end of file diff --git a/rmtDev/video 34/src/common.js b/rmtDev/video 34/src/common.js new file mode 100644 index 0000000..34e739a --- /dev/null +++ b/rmtDev/video 34/src/common.js @@ -0,0 +1,46 @@ +// CONSTANTS +export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; +export const DEFAULT_DISPLAY_TIME = 3500; +export const RESULTS_PER_PAGE = 7; + +// STATE +export const state = { + searchJobItems: [], + bookmarkJobItems: [], + activeJobItem: {}, + currentPage: 1 +}; + +// SELECTORS +export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +export const errorEl = document.querySelector('.error'); +export const errorTextEl = document.querySelector('.error__text'); +export const jobDetailsEl = document.querySelector('.job-details'); +export const jobDetailsContentEl = document.querySelector(".job-details__content"); +export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +export const jobListSearchEl = document.querySelector(".job-list--search"); +export const numberEl = document.querySelector(".count__number"); +export const paginationEl = document.querySelector(".pagination"); +export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +export const searchFormEl = document.querySelector(".search"); +export const searchInputEl = document.querySelector(".search__input"); +export const sortingEl = document.querySelector(".sorting"); +export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +export const spinnerSearchEl = document.querySelector(".spinner--search"); +export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +// HELPER / UTILITY FUNCTIONS +export const getData = async completeURL => { + const response = await fetch(completeURL); + const data = await response.json(); + + if (!response.ok) { // 4xx, 5xx status code + throw new Error(data.description); + } + + return data; +}; \ No newline at end of file diff --git a/rmtDev/video 34/src/components/Bookmarks.js b/rmtDev/video 34/src/components/Bookmarks.js new file mode 100644 index 0000000..0e4dfa4 --- /dev/null +++ b/rmtDev/video 34/src/components/Bookmarks.js @@ -0,0 +1,51 @@ +import { + state, + bookmarksBtnEl, + jobDetailsEl, + jobListBookmarksEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const clickHandler = event => { + // don't continue if click was outside bookmark button + if (!event.target.className.includes('bookmark')) return; + + // update state + if (state.bookmarkJobItems.some(bookmarkJobItem => bookmarkJobItem.id === state.activeJobItem.id)) { + state.bookmarkJobItems = state.bookmarkJobItems.filter(bookmarkJobItem => bookmarkJobItem.id !== state.activeJobItem.id); + } else { + state.bookmarkJobItems.push(state.activeJobItem); + } + + // persist data with localstorage + localStorage.setItem('bookmarkJobItems', JSON.stringify(state.bookmarkJobItems)); + + // update bookmark icon + document.querySelector('.job-info__bookmark-icon').classList.toggle('job-info__bookmark-icon--bookmarked'); + + // render search job list + renderJobList(); +}; + +const mouseEnterHandler = () => { + // make bookmarks button look active + bookmarksBtnEl.classList.add('bookmarks-btn--active'); + + // make job list visible + jobListBookmarksEl.classList.add('job-list--visible'); + + // render bookmarks job list + renderJobList('bookmarks'); +}; + +const mouseLeaveHandler = () => { + // make bookmarks button look inactive + bookmarksBtnEl.classList.remove('bookmarks-btn--active'); + + // make job list invisible + jobListBookmarksEl.classList.remove('job-list--visible'); +}; + +jobDetailsEl.addEventListener('click', clickHandler); +bookmarksBtnEl.addEventListener('mouseenter', mouseEnterHandler); +jobListBookmarksEl.addEventListener('mouseleave', mouseLeaveHandler); \ No newline at end of file diff --git a/rmtDev/video 34/src/components/Error.js b/rmtDev/video 34/src/components/Error.js new file mode 100644 index 0000000..80177ed --- /dev/null +++ b/rmtDev/video 34/src/components/Error.js @@ -0,0 +1,15 @@ +import { + DEFAULT_DISPLAY_TIME, + errorTextEl, + errorEl +} from '../common.js'; + +const renderError = (message = 'Something went wrong') => { + errorTextEl.textContent = message; + errorEl.classList.add('error--visible'); + setTimeout(() => { + errorEl.classList.remove('error--visible'); + }, DEFAULT_DISPLAY_TIME); +}; + +export default renderError; \ No newline at end of file diff --git a/rmtDev/video 34/src/components/JobDetails.js b/rmtDev/video 34/src/components/JobDetails.js new file mode 100644 index 0000000..9c66267 --- /dev/null +++ b/rmtDev/video 34/src/components/JobDetails.js @@ -0,0 +1,63 @@ +import { + state, + jobDetailsContentEl +} from '../common.js'; + +const renderJobDetails = jobItem => { + const jobDetailsHTML = ` + # + + Apply + +
    +
    +
    ${jobItem.badgeLetters}
    +
    + + +
    +
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +

    ${jobItem.description}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      + ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      + ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} +
    +
    +
    + +
    + +
    + `; + jobDetailsContentEl.innerHTML = jobDetailsHTML; +}; + +export default renderJobDetails; \ No newline at end of file diff --git a/rmtDev/video 34/src/components/JobList.js b/rmtDev/video 34/src/components/JobList.js new file mode 100644 index 0000000..afd0376 --- /dev/null +++ b/rmtDev/video 34/src/components/JobList.js @@ -0,0 +1,105 @@ +import { + BASE_API_URL, + RESULTS_PER_PAGE, + state, + jobListSearchEl, + jobListBookmarksEl, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; + +const renderJobList = (whichJobList = 'search') => { + // determine correct selector for job list (search results list or bookmarks list) + const jobListEl = whichJobList === 'search' ? jobListSearchEl : jobListBookmarksEl; + + // remove previous job items + jobListEl.innerHTML = ''; + + // determine the job items that should be rendered + let jobItems; + if (whichJobList === 'search') { + jobItems = state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE); + } else if (whichJobList === 'bookmarks') { + jobItems = state.bookmarkJobItems; + } + + // display job items + jobItems.forEach(jobItem => { + const newJobItemHTML = ` +
  • + +
    ${jobItem.badgeLetters}
    +
    +

    ${jobItem.title}

    +

    ${jobItem.company}

    +
    +

    ${jobItem.duration}

    +

    ${jobItem.salary}

    +

    ${jobItem.location}

    +
    +
    +
    + + +
    +
    +
  • + `; + jobListEl.insertAdjacentHTML('beforeend', newJobItemHTML); + }); +}; + +const clickHandler = async event => { + // prevent default behavior (navigation) + event.preventDefault(); + + // get clicked job item element + const jobItemEl = event.target.closest('.job-item'); + + // remove the active class from previously active job items + document.querySelectorAll('.job-item--active').forEach(jobItemWithActiveClass => jobItemWithActiveClass.classList.remove('job-item--active')); + + // empty the job details section + jobDetailsContentEl.innerHTML = ''; + + // render spinner + renderSpinner('job-details'); + + // get the id + const id = jobItemEl.children[0].getAttribute('href'); + + // update state + const allJobItems = [...state.searchJobItems, ...state.bookmarkJobItems]; + state.activeJobItem = allJobItems.find(jobItem => jobItem.id === +id); + + // render search job list + renderJobList(); + + // add id to url + history.pushState(null, '', `/#${id}`); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } +}; + +jobListSearchEl.addEventListener('click', clickHandler); +jobListBookmarksEl.addEventListener('click', clickHandler); + +export default renderJobList; \ No newline at end of file diff --git a/rmtDev/video 34/src/components/Pagination.js b/rmtDev/video 34/src/components/Pagination.js new file mode 100644 index 0000000..720f1c7 --- /dev/null +++ b/rmtDev/video 34/src/components/Pagination.js @@ -0,0 +1,58 @@ +import { + RESULTS_PER_PAGE, + state, + paginationEl, + paginationNumberNextEl, + paginationNumberBackEl, + paginationBtnNextEl, + paginationBtnBackEl +} from '../common.js'; +import renderJobList from './JobList.js'; + +const renderPaginationButtons = () => { + // display back button if we are on page 2 or further + if (state.currentPage >= 2) { + paginationBtnBackEl.classList.remove('pagination__button--hidden'); + } else { + paginationBtnBackEl.classList.add('pagination__button--hidden'); + } + + // display next button if there are more job items on next page + if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { + paginationBtnNextEl.classList.add('pagination__button--hidden'); + } else { + paginationBtnNextEl.classList.remove('pagination__button--hidden'); + } + + // update page numbers + paginationNumberNextEl.textContent = state.currentPage + 1; + paginationNumberBackEl.textContent = state.currentPage - 1; + + // unfocus ('blur') buttons + paginationBtnNextEl.blur(); + paginationBtnBackEl.blur(); +}; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.pagination__button'); + + // stop function if null + if (!clickedButtonEl) return; + + // check if intention is next or back + const nextPage = clickedButtonEl.className.includes('--next') ? true : false; + + // update state + nextPage ? state.currentPage++ : state.currentPage--; + + // render pagination buttons + renderPaginationButtons(); + + // render job items for that page + renderJobList(); +}; + +paginationEl.addEventListener('click', clickHandler); + +export default renderPaginationButtons; \ No newline at end of file diff --git a/rmtDev/video 34/src/components/Router.js b/rmtDev/video 34/src/components/Router.js new file mode 100644 index 0000000..62bd46e --- /dev/null +++ b/rmtDev/video 34/src/components/Router.js @@ -0,0 +1,52 @@ +import { + BASE_API_URL, + state, + jobDetailsContentEl, + getData +} from '../common.js'; +import renderSpinner from './Spinner.js'; +import renderJobDetails from './JobDetails.js'; +import renderError from './Error.js'; +import renderJobList from './JobList.js'; + +const loadHashChangeHandler = async () => { + // get id from url + const id = window.location.hash.substring(1); + + if (id) { + // remove the active class from previously active job items + document.querySelectorAll('.job-item--active').forEach(jobItemWithActiveClass => jobItemWithActiveClass.classList.remove('job-item--active')); + + // remove previous job details content + jobDetailsContentEl.innerHTML = ''; + + // add spinner + renderSpinner('job-details'); + + try { + // fetch job item data + const data = await getData(`${BASE_API_URL}/jobs/${id}`); + + // extract job item + const { jobItem } = data; + + // update state + state.activeJobItem = jobItem; + + // render search job list + renderJobList(); + + // remove spinner + renderSpinner('job-details'); + + // render job details + renderJobDetails(jobItem); + } catch (error) { + renderSpinner('job-details'); + renderError(error.message); + } + } +}; + +window.addEventListener('DOMContentLoaded', loadHashChangeHandler); +window.addEventListener('hashchange', loadHashChangeHandler); \ No newline at end of file diff --git a/rmtDev/video 34/src/components/Search.js b/rmtDev/video 34/src/components/Search.js new file mode 100644 index 0000000..e672606 --- /dev/null +++ b/rmtDev/video 34/src/components/Search.js @@ -0,0 +1,73 @@ +import { + BASE_API_URL, + state, + searchInputEl, + searchFormEl, + jobListSearchEl, + numberEl, + sortingBtnRecentEl, + sortingBtnRelevantEl, + getData +} from '../common.js'; +import renderError from './Error.js'; +import renderSpinner from './Spinner.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const submitHandler = async event => { + // prevent default behavior + event.preventDefault(); + + // get search text + const searchText = searchInputEl.value; + + // validation (regular expression example) + const forbiddenPattern = /[0-9]/; + const patternMatch = forbiddenPattern.test(searchText); + if (patternMatch) { + renderError('Your search may not contain numbers'); + return; + } + + // blur input + searchInputEl.blur(); + + // remove previous job items + jobListSearchEl.innerHTML = ''; + + // reset sorting buttons + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + + // render spinner + renderSpinner('search'); + + try { + // fetch search results + const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); + + // extract job items + const { jobItems } = data; + + // update state + state.searchJobItems = jobItems; + state.currentPage = 1; + + // remove spinner + renderSpinner('search'); + + // render number of results + numberEl.textContent = jobItems.length; + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in search job list + renderJobList(); + } catch (error) { + renderSpinner('search'); + renderError(error.message); + } +}; + +searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/rmtDev/video 34/src/components/Sorting.js b/rmtDev/video 34/src/components/Sorting.js new file mode 100644 index 0000000..cd08f17 --- /dev/null +++ b/rmtDev/video 34/src/components/Sorting.js @@ -0,0 +1,51 @@ +import { + state, + sortingEl, + sortingBtnRecentEl, + sortingBtnRelevantEl +} from '../common.js'; +import renderJobList from './JobList.js'; +import renderPaginationButtons from './Pagination.js'; + +const clickHandler = event => { + // get clicked button element + const clickedButtonEl = event.target.closest('.sorting__button'); + + // stop function if no clicked button element + if (!clickedButtonEl) return; + + // update state (reset to page 1) + state.currentPage = 1; + + // check if intention is recent or relevant sorting + const recent = clickedButtonEl.className.includes('--recent') ? true : false; + + // make sorting button look (in)active + if (recent) { + sortingBtnRecentEl.classList.add('sorting__button--active'); + sortingBtnRelevantEl.classList.remove('sorting__button--active'); + } else { + sortingBtnRecentEl.classList.remove('sorting__button--active'); + sortingBtnRelevantEl.classList.add('sorting__button--active'); + } + + // sort job items + // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same + if (recent) { + state.searchJobItems.sort((a, b) => { + return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. + }); + } else { + state.searchJobItems.sort((a, b) => { + return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. + }); + } + + // reset pagination buttons + renderPaginationButtons(); + + // render job items in list + renderJobList(); +}; + +sortingEl.addEventListener('click', clickHandler); diff --git a/rmtDev/video 34/src/components/Spinner.js b/rmtDev/video 34/src/components/Spinner.js new file mode 100644 index 0000000..cb25d4f --- /dev/null +++ b/rmtDev/video 34/src/components/Spinner.js @@ -0,0 +1,11 @@ +import { + spinnerSearchEl, + spinnerJobDetailsEl +} from '../common.js'; + +const renderSpinner = whichSpinner => { + const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; + spinnerEl.classList.toggle('spinner--visible'); +}; + +export default renderSpinner; \ No newline at end of file diff --git a/rmtDev/video 34/src/components/Storage.js b/rmtDev/video 34/src/components/Storage.js new file mode 100644 index 0000000..2bb6fbe --- /dev/null +++ b/rmtDev/video 34/src/components/Storage.js @@ -0,0 +1,8 @@ +import { + state +} from '../common.js'; + +const storedJobItems = localStorage.getItem('bookmarkJobItems'); +if (storedJobItems) { + state.bookmarkJobItems = JSON.parse(storedJobItems); +} \ No newline at end of file diff --git a/rmtDev/video 35/babel.config.js b/rmtDev/video 35/babel.config.js new file mode 100644 index 0000000..9c22988 --- /dev/null +++ b/rmtDev/video 35/babel.config.js @@ -0,0 +1,11 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + useBuiltIns: 'usage', + corejs: '3.0' + } + ] + ] +}; \ No newline at end of file diff --git a/rmtDev/video 35/dist/index.html b/rmtDev/video 35/dist/index.html new file mode 100644 index 0000000..217a364 --- /dev/null +++ b/rmtDev/video 35/dist/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 35/dist/main.css b/rmtDev/video 35/dist/main.css new file mode 100644 index 0000000..56d663d --- /dev/null +++ b/rmtDev/video 35/dist/main.css @@ -0,0 +1 @@ +*,:after,:before{box-sizing:border-box;margin:0;padding:0}ol,ul{list-style:none}a{color:inherit;text-decoration:initial}button{background-color:initial}button,input{border:initial;font:inherit;outline:initial}.u-bold{font-weight:700}@-webkit-keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@-webkit-keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}@keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}.body{background-color:#dee3e9;color:#16181c;font-family:Inter,sans-serif;min-height:100vh;position:relative;scrollbar-width:none}.body::-webkit-scrollbar{display:none}.first-heading{color:#fff;display:none;font-size:1.9375rem;font-size:1.6875rem;font-weight:400}.second-heading{color:#fff;font-size:1.4375rem;font-weight:500}.third-heading{font-size:.8125rem;font-weight:600}.fourth-heading{font-size:1rem;font-weight:600;text-transform:capitalize}.background{background-image:linear-gradient(125deg,#1f74f1 -10%,#0850b9);box-shadow:0 .125rem .1875rem rgba(0,0,0,.1);height:13.125rem;left:0;overflow:hidden;position:absolute;top:0;width:100%;z-index:-2}.background:before{background-image:linear-gradient(-180deg,rgba(0,0,0,.025),rgba(0,0,0,.075) 99%);bottom:0;content:"";left:0;position:absolute;right:0;top:0}.background__pattern{left:0;position:absolute;top:-1.5625rem;-webkit-transform:scale(1.1);transform:scale(1.1);-webkit-user-select:none;-ms-user-select:none;user-select:none;z-index:-1}.header{margin-bottom:0;position:relative}.header__top{align-items:center;-webkit-animation:intro .3s;animation:intro .3s;display:flex;justify-content:center;margin:0 auto;max-width:62.5rem;padding:2.5rem .75rem 0;position:relative}.header__submit-job{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.75rem;margin-right:0;padding-left:.625rem;text-transform:lowercase;-webkit-transform:translateY(-.125rem);transform:translateY(-.125rem)}.header__submit-job:before{background-color:hsla(0,0%,100%,.25);content:"";display:inline-block;height:.8125rem;margin-right:.5rem;-webkit-transform:translateY(.1875rem);transform:translateY(.1875rem);width:.125rem}.logo{margin-left:-.5rem;-webkit-user-select:none;-ms-user-select:none;user-select:none}.logo__img{margin-bottom:-.3125rem}.bookmarks-btn{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.8125rem;height:2rem;margin-left:.8125rem;padding-left:.8125rem;position:relative;text-transform:lowercase;transition:all .2s}.bookmarks-btn--active,.bookmarks-btn:focus,.bookmarks-btn:hover{color:#fff}.bookmarks-btn--active .bookmarks-btn__icon,.bookmarks-btn:focus .bookmarks-btn__icon,.bookmarks-btn:hover .bookmarks-btn__icon{color:hsla(0,0%,100%,.8)}.bookmarks-btn:before{background-color:hsla(0,0%,100%,.3);content:"";display:block;height:.9375rem;left:0;position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:.125rem}.bookmarks-btn__icon{color:hsla(0,0%,100%,.6);font-size:.625rem;margin-left:.125rem;-webkit-transform:translateY(-.0625rem);transform:translateY(-.0625rem);transition:all .2s}.job-list{background-color:#fff;scrollbar-color:#cacdd0 #fff;scrollbar-width:thin}.job-list::-webkit-scrollbar{width:.25rem}.job-list::-webkit-scrollbar-track{background-color:#fff}.job-list::-webkit-scrollbar-thumb{background-color:#cacdd0;-webkit-transition:all .2s;transition:all .2s}.job-list::-webkit-scrollbar-thumb:hover{background-color:#b1b4b8}.job-list--bookmarks{border-radius:.25rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);left:50%;min-height:4.75rem;min-width:21.25rem;opacity:0;overflow:hidden;pointer-events:none;position:fixed;top:5.125rem;-webkit-transform:scale(.9) translateX(-50%);transform:scale(.9) translateX(-50%);-webkit-transform-origin:left;transform-origin:left;transition:all .2s;visibility:hidden;width:21.25rem;z-index:10}.job-list--bookmarks:hover{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-list--bookmarks:before{color:#616268;content:"Nothing bookmarked yet...";font-size:.8125rem;left:50%;position:absolute;top:49%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);z-index:-1}.job-list--visible{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-item{background-color:#fff;cursor:pointer;padding:.875rem 1.25rem;transition:all .2s}.job-item:not(:nth-child(7)){border-bottom:.0625rem solid #ebeff1}.job-item--active,.job-item:hover{background-color:#f4f5f7}.job-item__link{display:flex;height:100%;width:100%}.job-item__badge{align-items:center;background-color:#8dd335;border-radius:.3125rem;display:flex;font-size:.8125rem;font-weight:600;height:2.875rem;justify-content:center;margin-right:.8125rem;width:2.375rem}.job-item:nth-child(4n+2) .job-item__badge{background-color:#3d87f1}.job-item:nth-child(4n+3) .job-item__badge{background-color:#d2d631}.job-item:nth-child(4n+4) .job-item__badge{background-color:#d96a46}.job-item__company{font-size:.75rem;font-style:italic;margin-bottom:.125rem}.job-item__extras{-webkit-column-gap:.625rem;column-gap:.625rem;display:grid;grid-template-columns:4.0625rem 4.5rem 4.0625rem}.job-item__extra{color:#4d5054;font-size:.6875rem}.job-item__extra-icon{color:#bec5ce;font-size:.625rem;margin-right:.0625rem}.job-item__right{align-items:flex-end;display:flex;flex-direction:column;margin-left:auto}.job-item__bookmark-icon{color:#d7dbe0;cursor:pointer;font-size:.875rem;transition:all .2s}.job-item__bookmark-icon--bookmarked{color:#2671dd}.job-item__time{color:#515459;font-size:.625rem;margin-top:.25rem}.main{justify-content:center}.intro,.main{align-items:center;display:flex;flex-direction:column}.intro{margin-top:1.25rem;row-gap:2.0625rem;row-gap:1.25rem}.search{-webkit-animation:intro .3s .1s backwards;animation:intro .3s .1s backwards;position:relative}.search__submit-btn{cursor:pointer;left:1.5625rem;position:absolute;top:1.0625rem}.search__icon{color:rgba(0,0,0,.73);transition:all .2s}.search__icon:focus,.search__icon:hover{color:rgba(0,0,0,.93)}.search__input{background-color:hsla(0,0%,100%,.9);border-radius:.25rem;caret-color:rgba(0,0,0,.5);color:rgba(0,0,0,.9);height:3.5rem;padding-bottom:.125rem;padding-left:3.4375rem;padding-right:.9375rem;transition:all .2s,box-shadow .1s;width:38.125rem}.search__input::selection{background-color:rgba(0,0,0,.25)}.search__input:focus,.search__input:hover{background-color:#fff}.search__input:focus{box-shadow:0 0 0 .25rem hsla(0,0%,100%,.4)}.search__input::-webkit-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input:-ms-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input::placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input--invalid{box-shadow:0 0 0 .25rem rgba(47,19,19,.729)}.container{-webkit-animation:intro .3s .2s backwards;animation:intro .3s .2s backwards;background-color:#fff;border-radius:.5rem;border-top-right-radius:.5625rem;box-shadow:0 .1875rem .3125rem rgba(0,0,0,.07);display:flex;height:38.5rem;margin:2.5rem .75rem 0;width:61rem}.search-results{cursor:default;display:flex;flex-direction:column;position:relative;width:21.25rem}.search-results__top{align-items:center;border-bottom:.0625rem solid #e8edf0;display:flex;justify-content:space-between;padding:.625rem 1.25rem}.count{font-size:.75rem}.sorting__icon{color:#4c4f50;font-size:.6875rem;margin-right:.3125rem}.sorting__button{background-color:#e8edf0;border-radius:.1875rem;cursor:pointer;font-size:.625rem;margin-left:.125rem;padding:.375rem .5rem;text-transform:uppercase;transition:all .2s}.sorting__button:focus,.sorting__button:hover{background-color:#d0d5d8}.sorting__button--active,.sorting__button--active:focus,.sorting__button--active:hover{background-color:#3c4041;color:#fff}.pagination{align-items:center;border-top:.0625rem solid #e8edf0;display:flex;height:2.5rem;justify-content:space-between;margin-top:auto;padding:0 1.25rem .0625rem}.pagination__button{background-color:#eceff2;border-radius:31.25rem;color:#747c82;cursor:pointer;font-size:.625rem;padding:.25rem .625rem;transition:all .2s,visibility 0s}.pagination__button:focus,.pagination__button:hover{background-color:#dde2e6}.pagination__button--hidden{visibility:hidden}.pagination__number{font-weight:500}.pagination__icon{color:#9fa6b0;font-size:.5rem}.job-details{background-color:#eff2f5;border-bottom-right-radius:.5rem;border-top-right-radius:.75rem;flex:1;position:relative}.job-details__start-view{left:50%;position:absolute;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.job-details__start-text{color:#24292d;font-size:.875rem;opacity:.55;text-align:center;width:17.1875rem}.job-details__start-text--big{color:#0d1114;font-size:1.125rem;font-weight:600;margin-bottom:.625rem}.job-details__content{height:100%}.job-details__cover-img{border-top-right-radius:.5rem;height:10.875rem;-o-object-fit:cover;object-fit:cover;position:absolute;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none;width:100%;z-index:0}.job-details__footer{border-top:.0625rem solid #dce2e8;margin-left:2.625rem;margin-right:2.625rem;margin-top:2.0625rem;padding-top:.8125rem}.job-details__footer-text{color:#858b8f;font-size:.625rem}.apply-btn{align-items:center;background-color:#2671dd;border-radius:.1875rem;color:hsla(0,0%,100%,.92);cursor:pointer;display:flex;font-size:.6875rem;padding:.3125rem .625rem .375rem;position:absolute;right:.75rem;text-transform:uppercase;top:.75rem;transition:all .2s;z-index:2}.apply-btn:focus,.apply-btn:hover{background-color:#1d60bd;color:#fff}.apply-btn__icon{color:hsla(0,0%,100%,.65);font-size:.5rem;margin-left:.25rem;margin-top:-.0625rem}.job-info{-webkit-column-gap:1rem;column-gap:1rem;display:flex;margin-bottom:2.5rem;padding-top:7.5rem;position:relative;z-index:1}.job-info:before{background-image:linear-gradient(0deg,rgba(0,0,0,.7),rgba(0,0,0,.15));border-top-right-radius:.5rem;content:"";height:10.875rem;left:0;position:absolute;top:0;width:100%;z-index:-1}.job-info__left{padding-left:2.625rem}.job-info__right{padding-right:2.625rem}.job-info__badge{align-items:center;background-color:#d0d335;border-radius:.3125rem;display:flex;font-size:1.375rem;font-weight:600;height:4.375rem;justify-content:center;margin-bottom:.8125rem;width:3.4375rem}.job-info__below-badge{display:flex;justify-content:space-between}.job-info__time{color:#494d4f;font-size:.75rem;-webkit-transform:translateY(.0625rem);transform:translateY(.0625rem)}.job-info__bookmark-btn{cursor:pointer}.job-info__bookmark-btn:hover .job-info__bookmark-icon{color:#2671dd}.job-info__bookmark-icon{color:#d7dbe0;font-size:1.125rem;transition:all .2s}.job-info__bookmark-icon--bookmarked{color:#2671dd}.job-info__company{color:hsla(0,0%,100%,.8);font-size:.875rem;font-style:italic}.job-info__description{font-size:.875rem;line-height:1.4;margin-bottom:.75rem;margin-top:1.125rem}.job-info__extras{-webkit-column-gap:2.1875rem;column-gap:2.1875rem;display:flex}.job-info__extra{align-items:center;display:flex;font-size:.75rem}.job-info__extra-icon{align-items:center;background-color:#e4e9ed;border-radius:50%;color:#a1a8b0;display:flex;font-size:.5625rem;height:1.4375rem;justify-content:center;margin-right:.5rem;width:1.4375rem}.qualifications{display:flex;margin-bottom:1.875rem;padding-left:2.625rem;padding-right:2.625rem}.qualifications__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.qualifications__left{margin-right:2.1875rem}.qualifications__list{display:flex;flex-wrap:wrap;gap:.375rem}.qualifications__item{background-color:#e6ebee;border-radius:.125rem;color:#494d4f;font-size:.8125rem;padding:.375rem .625rem}.reviews{display:flex;padding-left:2.625rem;padding-right:2.625rem}.reviews__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.reviews__left{margin-right:2.1875rem}.reviews__list{-webkit-column-gap:1.25rem;column-gap:1.25rem;display:grid;flex:1;grid-template-columns:1fr 1fr;grid-template-rows:auto auto;row-gap:1.25rem}.reviews__item{color:#494d4f;font-size:.8125rem;font-style:italic;position:relative;-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.reviews__item:before{color:#d2d7db;content:"“";font-size:3.125rem;left:-.625rem;position:absolute;top:-.9375rem;-webkit-transform:translateZ(-.0625rem);transform:translateZ(-.0625rem)}.footer{align-items:center;color:#a4a9ac;display:flex;justify-content:space-between;margin:.9375rem auto 0;max-width:62.5rem;padding:0 .75rem}.copyright{font-size:.6875rem}.copyright ::selection{background-color:hsla(0,0%,100%,.1)}.copyright__text{line-height:1.2}.copyright__link{text-decoration:underline}.copyright__icon{color:#aeb3b6;font-size:.625rem;margin-left:.125rem;margin-right:.25rem}.jobs-available{align-self:flex-start;font-size:.6875rem}.spinner{-webkit-animation:spinner 1s linear infinite;animation:spinner 1s linear infinite;border-radius:50%;position:absolute;visibility:hidden}.spinner--search{border:.3125rem solid #e2e7e9;border-left-color:#ccd1d3;height:5.3125rem;left:50%;top:18%;width:5.3125rem}.spinner--job-details{border:.375rem solid #d5d9db;border-left-color:#bbc0c2;height:6.5625rem;left:50%;top:40%;width:6.5625rem}.spinner--visible{visibility:visible}.error{background:#fff;border-radius:.1875rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);display:flex;min-height:2.875rem;opacity:0;padding:.875rem 1.25rem;position:absolute;right:.9375rem;top:.9375rem;-webkit-transform:translateY(-7.5rem);transform:translateY(-7.5rem);transition:all .3s;visibility:hidden;width:17.5rem}.error--visible{opacity:1;-webkit-transform:initial;transform:none;visibility:initial}.error__icon{color:#7b4040;font-size:1rem;margin-top:.125rem}.error__right{margin-left:.625rem}.error__title{font-size:.75rem;font-weight:500;margin-bottom:.0625rem;text-transform:uppercase}.error__text{color:#616268;font-size:.8125rem}@media (max-height:925px) and (min-width:1010px){.body{padding-bottom:3.125rem}}@media (max-width:1179px){.job-list--bookmarks{right:0}}@media (max-width:1009px){.body{padding:0 .75rem 3.125rem}.header__top{max-width:50rem;padding-left:0;padding-right:0}.container{border-radius:.5rem;flex-direction:column;height:auto;max-width:50rem;overflow:hidden;width:100%}.search-results{width:100%}.job-details{display:none}.footer{max-width:50rem;padding-left:0;padding-right:0}}@media (max-width:660px){.intro,.search,.search__input{width:100%}.footer{justify-content:center}.copyright{text-align:center}.jobs-available{display:none;margin-left:.9375rem;text-align:right}.intro{row-gap:1.5625rem}.first-heading{max-width:25rem;text-align:center}}@media (max-width:370px){.job-list--bookmarks{min-width:auto;width:93vw}.job-item{width:100%}.job-item__badge{display:none}.error{left:50%;right:auto;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:93vw}} diff --git a/rmtDev/video 35/dist/main.js b/rmtDev/video 35/dist/main.js new file mode 100644 index 0000000..eba8c55 --- /dev/null +++ b/rmtDev/video 35/dist/main.js @@ -0,0 +1,2 @@ +/*! For license information please see main.js.LICENSE.txt */ +!function(){var t={9662:function(t,r,e){var n=e(614),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a function")}},9483:function(t,r,e){var n=e(4411),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a constructor")}},6077:function(t,r,e){var n=e(614),o=String,i=TypeError;t.exports=function(t){if("object"==typeof t||n(t))return t;throw i("Can't set "+o(t)+" as a prototype")}},1223:function(t,r,e){var n=e(5112),o=e(30),i=e(3070).f,a=n("unscopables"),c=Array.prototype;null==c[a]&&i(c,a,{configurable:!0,value:o(null)}),t.exports=function(t){c[a][t]=!0}},5787:function(t,r,e){var n=e(7976),o=TypeError;t.exports=function(t,r){if(n(r,t))return t;throw o("Incorrect invocation")}},9670:function(t,r,e){var n=e(111),o=String,i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not an object")}},8533:function(t,r,e){"use strict";var n=e(2092).forEach,o=e(2133)("forEach");t.exports=o?[].forEach:function(t){return n(this,t,arguments.length>1?arguments[1]:void 0)}},8457:function(t,r,e){"use strict";var n=e(9974),o=e(6916),i=e(7908),a=e(3411),c=e(7659),u=e(4411),s=e(6244),f=e(6135),l=e(8554),p=e(1246),h=Array;t.exports=function(t){var r=i(t),e=u(this),v=arguments.length,d=v>1?arguments[1]:void 0,y=void 0!==d;y&&(d=n(d,v>2?arguments[2]:void 0));var m,g,b,x,w,_,S=p(r),j=0;if(!S||this===h&&c(S))for(m=s(r),g=e?new this(m):h(m);m>j;j++)_=y?d(r[j],j):r[j],f(g,j,_);else for(w=(x=l(r,S)).next,g=e?new this:[];!(b=o(w,x)).done;j++)_=y?a(x,d,[b.value,j],!0):b.value,f(g,j,_);return g.length=j,g}},1318:function(t,r,e){var n=e(5656),o=e(1400),i=e(6244),a=function(t){return function(r,e,a){var c,u=n(r),s=i(u),f=o(a,s);if(t&&e!=e){for(;s>f;)if((c=u[f++])!=c)return!0}else for(;s>f;f++)if((t||f in u)&&u[f]===e)return t||f||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},2092:function(t,r,e){var n=e(9974),o=e(1702),i=e(8361),a=e(7908),c=e(6244),u=e(5417),s=o([].push),f=function(t){var r=1==t,e=2==t,o=3==t,f=4==t,l=6==t,p=7==t,h=5==t||l;return function(v,d,y,m){for(var g,b,x=a(v),w=i(x),_=n(d,y),S=c(w),j=0,L=m||u,E=r?L(v,S):e||p?L(v,0):void 0;S>j;j++)if((h||j in w)&&(b=_(g=w[j],j,x),t))if(r)E[j]=b;else if(b)switch(t){case 3:return!0;case 5:return g;case 6:return j;case 2:s(E,g)}else switch(t){case 4:return!1;case 7:s(E,g)}return l?-1:o||f?f:E}};t.exports={forEach:f(0),map:f(1),filter:f(2),some:f(3),every:f(4),find:f(5),findIndex:f(6),filterReject:f(7)}},1194:function(t,r,e){var n=e(7293),o=e(5112),i=e(7392),a=o("species");t.exports=function(t){return i>=51||!n((function(){var r=[];return(r.constructor={})[a]=function(){return{foo:1}},1!==r[t](Boolean).foo}))}},2133:function(t,r,e){"use strict";var n=e(7293);t.exports=function(t,r){var e=[][t];return!!e&&n((function(){e.call(null,r||function(){return 1},1)}))}},1589:function(t,r,e){var n=e(1400),o=e(6244),i=e(6135),a=Array,c=Math.max;t.exports=function(t,r,e){for(var u=o(t),s=n(r,u),f=n(void 0===e?u:e,u),l=a(c(f-s,0)),p=0;s0;)t[n]=t[--n];n!==i++&&(t[n]=e)}return t},c=function(t,r,e,n){for(var o=r.length,i=e.length,a=0,c=0;a9007199254740991)throw r("Maximum allowed index exceeded");return t}},8324:function(t){t.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},8509:function(t,r,e){var n=e(317)("span").classList,o=n&&n.constructor&&n.constructor.prototype;t.exports=o===Object.prototype?void 0:o},8886:function(t,r,e){var n=e(8113).match(/firefox\/(\d+)/i);t.exports=!!n&&+n[1]},7871:function(t){t.exports="object"==typeof window&&"object"!=typeof Deno},256:function(t,r,e){var n=e(8113);t.exports=/MSIE|Trident/.test(n)},1528:function(t,r,e){var n=e(8113),o=e(7854);t.exports=/ipad|iphone|ipod/i.test(n)&&void 0!==o.Pebble},6833:function(t,r,e){var n=e(8113);t.exports=/(?:ipad|iphone|ipod).*applewebkit/i.test(n)},5268:function(t,r,e){var n=e(4326),o=e(7854);t.exports="process"==n(o.process)},1036:function(t,r,e){var n=e(8113);t.exports=/web0s(?!.*chrome)/i.test(n)},8113:function(t,r,e){var n=e(5005);t.exports=n("navigator","userAgent")||""},7392:function(t,r,e){var n,o,i=e(7854),a=e(8113),c=i.process,u=i.Deno,s=c&&c.versions||u&&u.version,f=s&&s.v8;f&&(o=(n=f.split("."))[0]>0&&n[0]<4?1:+(n[0]+n[1])),!o&&a&&(!(n=a.match(/Edge\/(\d+)/))||n[1]>=74)&&(n=a.match(/Chrome\/(\d+)/))&&(o=+n[1]),t.exports=o},8008:function(t,r,e){var n=e(8113).match(/AppleWebKit\/(\d+)\./);t.exports=!!n&&+n[1]},748:function(t){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(t,r,e){var n=e(7854),o=e(1236).f,i=e(8880),a=e(8052),c=e(3072),u=e(9920),s=e(4705);t.exports=function(t,r){var e,f,l,p,h,v=t.target,d=t.global,y=t.stat;if(e=d?n:y?n[v]||c(v,{}):(n[v]||{}).prototype)for(f in r){if(p=r[f],l=t.dontCallGetSet?(h=o(e,f))&&h.value:e[f],!s(d?f:v+(y?".":"#")+f,t.forced)&&void 0!==l){if(typeof p==typeof l)continue;u(p,l)}(t.sham||l&&l.sham)&&i(p,"sham",!0),a(e,f,p,t)}}},7293:function(t){t.exports=function(t){try{return!!t()}catch(t){return!0}}},2104:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.apply,a=o.call;t.exports="object"==typeof Reflect&&Reflect.apply||(n?a.bind(i):function(){return a.apply(i,arguments)})},9974:function(t,r,e){var n=e(1702),o=e(9662),i=e(4374),a=n(n.bind);t.exports=function(t,r){return o(t),void 0===r?t:i?a(t,r):function(){return t.apply(r,arguments)}}},4374:function(t,r,e){var n=e(7293);t.exports=!n((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))},6916:function(t,r,e){var n=e(4374),o=Function.prototype.call;t.exports=n?o.bind(o):function(){return o.apply(o,arguments)}},6530:function(t,r,e){var n=e(9781),o=e(2597),i=Function.prototype,a=n&&Object.getOwnPropertyDescriptor,c=o(i,"name"),u=c&&"something"===function(){}.name,s=c&&(!n||n&&a(i,"name").configurable);t.exports={EXISTS:c,PROPER:u,CONFIGURABLE:s}},1702:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.bind,a=o.call,c=n&&i.bind(a,a);t.exports=n?function(t){return t&&c(t)}:function(t){return t&&function(){return a.apply(t,arguments)}}},5005:function(t,r,e){var n=e(7854),o=e(614),i=function(t){return o(t)?t:void 0};t.exports=function(t,r){return arguments.length<2?i(n[t]):n[t]&&n[t][r]}},1246:function(t,r,e){var n=e(648),o=e(8173),i=e(7497),a=e(5112)("iterator");t.exports=function(t){if(null!=t)return o(t,a)||o(t,"@@iterator")||i[n(t)]}},8554:function(t,r,e){var n=e(6916),o=e(9662),i=e(9670),a=e(6330),c=e(1246),u=TypeError;t.exports=function(t,r){var e=arguments.length<2?c(t):r;if(o(e))return i(n(e,t));throw u(a(t)+" is not iterable")}},8173:function(t,r,e){var n=e(9662);t.exports=function(t,r){var e=t[r];return null==e?void 0:n(e)}},7854:function(t,r,e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e.g&&e.g)||function(){return this}()||Function("return this")()},2597:function(t,r,e){var n=e(1702),o=e(7908),i=n({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,r){return i(o(t),r)}},3501:function(t){t.exports={}},842:function(t,r,e){var n=e(7854);t.exports=function(t,r){var e=n.console;e&&e.error&&(1==arguments.length?e.error(t):e.error(t,r))}},490:function(t,r,e){var n=e(5005);t.exports=n("document","documentElement")},4664:function(t,r,e){var n=e(9781),o=e(7293),i=e(317);t.exports=!n&&!o((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},8361:function(t,r,e){var n=e(1702),o=e(7293),i=e(4326),a=Object,c=n("".split);t.exports=o((function(){return!a("z").propertyIsEnumerable(0)}))?function(t){return"String"==i(t)?c(t,""):a(t)}:a},2788:function(t,r,e){var n=e(1702),o=e(614),i=e(5465),a=n(Function.toString);o(i.inspectSource)||(i.inspectSource=function(t){return a(t)}),t.exports=i.inspectSource},9909:function(t,r,e){var n,o,i,a=e(8536),c=e(7854),u=e(1702),s=e(111),f=e(8880),l=e(2597),p=e(5465),h=e(6200),v=e(3501),d="Object already initialized",y=c.TypeError,m=c.WeakMap;if(a||p.state){var g=p.state||(p.state=new m),b=u(g.get),x=u(g.has),w=u(g.set);n=function(t,r){if(x(g,t))throw new y(d);return r.facade=t,w(g,t,r),r},o=function(t){return b(g,t)||{}},i=function(t){return x(g,t)}}else{var _=h("state");v[_]=!0,n=function(t,r){if(l(t,_))throw new y(d);return r.facade=t,f(t,_,r),r},o=function(t){return l(t,_)?t[_]:{}},i=function(t){return l(t,_)}}t.exports={set:n,get:o,has:i,enforce:function(t){return i(t)?o(t):n(t,{})},getterFor:function(t){return function(r){var e;if(!s(r)||(e=o(r)).type!==t)throw y("Incompatible receiver, "+t+" required");return e}}}},7659:function(t,r,e){var n=e(5112),o=e(7497),i=n("iterator"),a=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||a[i]===t)}},3157:function(t,r,e){var n=e(4326);t.exports=Array.isArray||function(t){return"Array"==n(t)}},614:function(t){t.exports=function(t){return"function"==typeof t}},4411:function(t,r,e){var n=e(1702),o=e(7293),i=e(614),a=e(648),c=e(5005),u=e(2788),s=function(){},f=[],l=c("Reflect","construct"),p=/^\s*(?:class|function)\b/,h=n(p.exec),v=!p.exec(s),d=function(t){if(!i(t))return!1;try{return l(s,f,t),!0}catch(t){return!1}},y=function(t){if(!i(t))return!1;switch(a(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return v||!!h(p,u(t))}catch(t){return!0}};y.sham=!0,t.exports=!l||o((function(){var t;return d(d.call)||!d(Object)||!d((function(){t=!0}))||t}))?y:d},4705:function(t,r,e){var n=e(7293),o=e(614),i=/#|\.prototype\./,a=function(t,r){var e=u[c(t)];return e==f||e!=s&&(o(r)?n(r):!!r)},c=a.normalize=function(t){return String(t).replace(i,".").toLowerCase()},u=a.data={},s=a.NATIVE="N",f=a.POLYFILL="P";t.exports=a},111:function(t,r,e){var n=e(614);t.exports=function(t){return"object"==typeof t?null!==t:n(t)}},1913:function(t){t.exports=!1},7850:function(t,r,e){var n=e(111),o=e(4326),i=e(5112)("match");t.exports=function(t){var r;return n(t)&&(void 0!==(r=t[i])?!!r:"RegExp"==o(t))}},2190:function(t,r,e){var n=e(5005),o=e(614),i=e(7976),a=e(3307),c=Object;t.exports=a?function(t){return"symbol"==typeof t}:function(t){var r=n("Symbol");return o(r)&&i(r.prototype,c(t))}},408:function(t,r,e){var n=e(9974),o=e(6916),i=e(9670),a=e(6330),c=e(7659),u=e(6244),s=e(7976),f=e(8554),l=e(1246),p=e(9212),h=TypeError,v=function(t,r){this.stopped=t,this.result=r},d=v.prototype;t.exports=function(t,r,e){var y,m,g,b,x,w,_,S=e&&e.that,j=!(!e||!e.AS_ENTRIES),L=!(!e||!e.IS_ITERATOR),E=!(!e||!e.INTERRUPTED),O=n(r,S),k=function(t){return y&&p(y,"normal",t),new v(!0,t)},P=function(t){return j?(i(t),E?O(t[0],t[1],k):O(t[0],t[1])):E?O(t,k):O(t)};if(L)y=t;else{if(!(m=l(t)))throw h(a(t)+" is not iterable");if(c(m)){for(g=0,b=u(t);b>g;g++)if((x=P(t[g]))&&s(d,x))return x;return new v(!1)}y=f(t,m)}for(w=y.next;!(_=o(w,y)).done;){try{x=P(_.value)}catch(t){p(y,"throw",t)}if("object"==typeof x&&x&&s(d,x))return x}return new v(!1)}},9212:function(t,r,e){var n=e(6916),o=e(9670),i=e(8173);t.exports=function(t,r,e){var a,c;o(t);try{if(!(a=i(t,"return"))){if("throw"===r)throw e;return e}a=n(a,t)}catch(t){c=!0,a=t}if("throw"===r)throw e;if(c)throw a;return o(a),e}},3383:function(t,r,e){"use strict";var n,o,i,a=e(7293),c=e(614),u=e(30),s=e(9518),f=e(8052),l=e(5112),p=e(1913),h=l("iterator"),v=!1;[].keys&&("next"in(i=[].keys())?(o=s(s(i)))!==Object.prototype&&(n=o):v=!0),null==n||a((function(){var t={};return n[h].call(t)!==t}))?n={}:p&&(n=u(n)),c(n[h])||f(n,h,(function(){return this})),t.exports={IteratorPrototype:n,BUGGY_SAFARI_ITERATORS:v}},7497:function(t){t.exports={}},6244:function(t,r,e){var n=e(7466);t.exports=function(t){return n(t.length)}},6339:function(t,r,e){var n=e(7293),o=e(614),i=e(2597),a=e(9781),c=e(6530).CONFIGURABLE,u=e(2788),s=e(9909),f=s.enforce,l=s.get,p=Object.defineProperty,h=a&&!n((function(){return 8!==p((function(){}),"length",{value:8}).length})),v=String(String).split("String"),d=t.exports=function(t,r,e){"Symbol("===String(r).slice(0,7)&&(r="["+String(r).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),e&&e.getter&&(r="get "+r),e&&e.setter&&(r="set "+r),(!i(t,"name")||c&&t.name!==r)&&p(t,"name",{value:r,configurable:!0}),h&&e&&i(e,"arity")&&t.length!==e.arity&&p(t,"length",{value:e.arity});try{e&&i(e,"constructor")&&e.constructor?a&&p(t,"prototype",{writable:!1}):t.prototype&&(t.prototype=void 0)}catch(t){}var n=f(t);return i(n,"source")||(n.source=v.join("string"==typeof r?r:"")),t};Function.prototype.toString=d((function(){return o(this)&&l(this).source||u(this)}),"toString")},4758:function(t){var r=Math.ceil,e=Math.floor;t.exports=Math.trunc||function(t){var n=+t;return(n>0?e:r)(n)}},5948:function(t,r,e){var n,o,i,a,c,u,s,f,l=e(7854),p=e(9974),h=e(1236).f,v=e(261).set,d=e(6833),y=e(1528),m=e(1036),g=e(5268),b=l.MutationObserver||l.WebKitMutationObserver,x=l.document,w=l.process,_=l.Promise,S=h(l,"queueMicrotask"),j=S&&S.value;j||(n=function(){var t,r;for(g&&(t=w.domain)&&t.exit();o;){r=o.fn,o=o.next;try{r()}catch(t){throw o?a():i=void 0,t}}i=void 0,t&&t.enter()},d||g||m||!b||!x?!y&&_&&_.resolve?((s=_.resolve(void 0)).constructor=_,f=p(s.then,s),a=function(){f(n)}):g?a=function(){w.nextTick(n)}:(v=p(v,l),a=function(){v(n)}):(c=!0,u=x.createTextNode(""),new b(n).observe(u,{characterData:!0}),a=function(){u.data=c=!c})),t.exports=j||function(t){var r={fn:t,next:void 0};i&&(i.next=r),o||(o=r,a()),i=r}},735:function(t,r,e){var n=e(133);t.exports=n&&!!Symbol.for&&!!Symbol.keyFor},133:function(t,r,e){var n=e(7392),o=e(7293);t.exports=!!Object.getOwnPropertySymbols&&!o((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&n&&n<41}))},8536:function(t,r,e){var n=e(7854),o=e(614),i=e(2788),a=n.WeakMap;t.exports=o(a)&&/native code/.test(i(a))},8523:function(t,r,e){"use strict";var n=e(9662),o=function(t){var r,e;this.promise=new t((function(t,n){if(void 0!==r||void 0!==e)throw TypeError("Bad Promise constructor");r=t,e=n})),this.resolve=n(r),this.reject=n(e)};t.exports.f=function(t){return new o(t)}},3929:function(t,r,e){var n=e(7850),o=TypeError;t.exports=function(t){if(n(t))throw o("The method doesn't accept regular expressions");return t}},30:function(t,r,e){var n,o=e(9670),i=e(6048),a=e(748),c=e(3501),u=e(490),s=e(317),f=e(6200)("IE_PROTO"),l=function(){},p=function(t){return" + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/rmtDev/video 35/src/index.js b/rmtDev/video 35/src/index.js new file mode 100644 index 0000000..c7c6751 --- /dev/null +++ b/rmtDev/video 35/src/index.js @@ -0,0 +1,12 @@ +import './components/Bookmarks.js'; +import './components/Error.js'; +import './components/JobDetails.js'; +import './components/JobList.js'; +import './components/Pagination.js'; +import './components/Router.js'; +import './components/Search.js'; +import './components/Sorting.js'; +import './components/Spinner.js'; +import './components/Storage.js'; + +import './index.css'; \ No newline at end of file diff --git a/rmtDev/video 35/webpack.config.js b/rmtDev/video 35/webpack.config.js new file mode 100644 index 0000000..b970c2c --- /dev/null +++ b/rmtDev/video 35/webpack.config.js @@ -0,0 +1,27 @@ +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); + +module.exports = { + mode: 'production', + entry: './src/index.js', + output: { + filename: 'main.js' + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: 'main.css' + }) + ], + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: ['babel-loader'] + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] + } + ] + } +}; \ No newline at end of file From bd25de5be63b59aff2639fc942d6e4f2cbef27a9 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Fri, 10 Jun 2022 12:32:53 +0200 Subject: [PATCH 05/40] Add supplemental" --- Supplemental/video 38/babel.config.js | 11 + Supplemental/video 38/dist/index.html | 121 + Supplemental/video 38/dist/main.css | 1 + Supplemental/video 38/dist/main.js | 2 + .../video 38/dist/main.js.LICENSE.txt | 1 + Supplemental/video 38/package-lock.json | 8346 +++++++++++++++++ Supplemental/video 38/package.json | 28 + Supplemental/video 38/postcss.config.js | 7 + Supplemental/video 38/src/common.js | 53 + .../video 38/src/components/Bookmarks.js | 51 + Supplemental/video 38/src/components/Error.js | 15 + .../video 38/src/components/JobDetails.js | 63 + .../video 38/src/components/JobList.js | 105 + .../video 38/src/components/Pagination.js | 58 + .../video 38/src/components/Router.js | 52 + .../video 38/src/components/Search.js | 79 + .../video 38/src/components/Sorting.js | 51 + .../video 38/src/components/Spinner.js | 11 + .../video 38/src/components/Storage.js | 8 + Supplemental/video 38/src/index.css | 1130 +++ Supplemental/video 38/src/index.html | 121 + Supplemental/video 38/src/index.js | 12 + Supplemental/video 38/webpack.config.js | 27 + 23 files changed, 10353 insertions(+) create mode 100644 Supplemental/video 38/babel.config.js create mode 100644 Supplemental/video 38/dist/index.html create mode 100644 Supplemental/video 38/dist/main.css create mode 100644 Supplemental/video 38/dist/main.js create mode 100644 Supplemental/video 38/dist/main.js.LICENSE.txt create mode 100644 Supplemental/video 38/package-lock.json create mode 100644 Supplemental/video 38/package.json create mode 100644 Supplemental/video 38/postcss.config.js create mode 100644 Supplemental/video 38/src/common.js create mode 100644 Supplemental/video 38/src/components/Bookmarks.js create mode 100644 Supplemental/video 38/src/components/Error.js create mode 100644 Supplemental/video 38/src/components/JobDetails.js create mode 100644 Supplemental/video 38/src/components/JobList.js create mode 100644 Supplemental/video 38/src/components/Pagination.js create mode 100644 Supplemental/video 38/src/components/Router.js create mode 100644 Supplemental/video 38/src/components/Search.js create mode 100644 Supplemental/video 38/src/components/Sorting.js create mode 100644 Supplemental/video 38/src/components/Spinner.js create mode 100644 Supplemental/video 38/src/components/Storage.js create mode 100644 Supplemental/video 38/src/index.css create mode 100644 Supplemental/video 38/src/index.html create mode 100644 Supplemental/video 38/src/index.js create mode 100644 Supplemental/video 38/webpack.config.js diff --git a/Supplemental/video 38/babel.config.js b/Supplemental/video 38/babel.config.js new file mode 100644 index 0000000..9c22988 --- /dev/null +++ b/Supplemental/video 38/babel.config.js @@ -0,0 +1,11 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + useBuiltIns: 'usage', + corejs: '3.0' + } + ] + ] +}; \ No newline at end of file diff --git a/Supplemental/video 38/dist/index.html b/Supplemental/video 38/dist/index.html new file mode 100644 index 0000000..217a364 --- /dev/null +++ b/Supplemental/video 38/dist/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/Supplemental/video 38/dist/main.css b/Supplemental/video 38/dist/main.css new file mode 100644 index 0000000..56d663d --- /dev/null +++ b/Supplemental/video 38/dist/main.css @@ -0,0 +1 @@ +*,:after,:before{box-sizing:border-box;margin:0;padding:0}ol,ul{list-style:none}a{color:inherit;text-decoration:initial}button{background-color:initial}button,input{border:initial;font:inherit;outline:initial}.u-bold{font-weight:700}@-webkit-keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@-webkit-keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}@keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}.body{background-color:#dee3e9;color:#16181c;font-family:Inter,sans-serif;min-height:100vh;position:relative;scrollbar-width:none}.body::-webkit-scrollbar{display:none}.first-heading{color:#fff;display:none;font-size:1.9375rem;font-size:1.6875rem;font-weight:400}.second-heading{color:#fff;font-size:1.4375rem;font-weight:500}.third-heading{font-size:.8125rem;font-weight:600}.fourth-heading{font-size:1rem;font-weight:600;text-transform:capitalize}.background{background-image:linear-gradient(125deg,#1f74f1 -10%,#0850b9);box-shadow:0 .125rem .1875rem rgba(0,0,0,.1);height:13.125rem;left:0;overflow:hidden;position:absolute;top:0;width:100%;z-index:-2}.background:before{background-image:linear-gradient(-180deg,rgba(0,0,0,.025),rgba(0,0,0,.075) 99%);bottom:0;content:"";left:0;position:absolute;right:0;top:0}.background__pattern{left:0;position:absolute;top:-1.5625rem;-webkit-transform:scale(1.1);transform:scale(1.1);-webkit-user-select:none;-ms-user-select:none;user-select:none;z-index:-1}.header{margin-bottom:0;position:relative}.header__top{align-items:center;-webkit-animation:intro .3s;animation:intro .3s;display:flex;justify-content:center;margin:0 auto;max-width:62.5rem;padding:2.5rem .75rem 0;position:relative}.header__submit-job{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.75rem;margin-right:0;padding-left:.625rem;text-transform:lowercase;-webkit-transform:translateY(-.125rem);transform:translateY(-.125rem)}.header__submit-job:before{background-color:hsla(0,0%,100%,.25);content:"";display:inline-block;height:.8125rem;margin-right:.5rem;-webkit-transform:translateY(.1875rem);transform:translateY(.1875rem);width:.125rem}.logo{margin-left:-.5rem;-webkit-user-select:none;-ms-user-select:none;user-select:none}.logo__img{margin-bottom:-.3125rem}.bookmarks-btn{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.8125rem;height:2rem;margin-left:.8125rem;padding-left:.8125rem;position:relative;text-transform:lowercase;transition:all .2s}.bookmarks-btn--active,.bookmarks-btn:focus,.bookmarks-btn:hover{color:#fff}.bookmarks-btn--active .bookmarks-btn__icon,.bookmarks-btn:focus .bookmarks-btn__icon,.bookmarks-btn:hover .bookmarks-btn__icon{color:hsla(0,0%,100%,.8)}.bookmarks-btn:before{background-color:hsla(0,0%,100%,.3);content:"";display:block;height:.9375rem;left:0;position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:.125rem}.bookmarks-btn__icon{color:hsla(0,0%,100%,.6);font-size:.625rem;margin-left:.125rem;-webkit-transform:translateY(-.0625rem);transform:translateY(-.0625rem);transition:all .2s}.job-list{background-color:#fff;scrollbar-color:#cacdd0 #fff;scrollbar-width:thin}.job-list::-webkit-scrollbar{width:.25rem}.job-list::-webkit-scrollbar-track{background-color:#fff}.job-list::-webkit-scrollbar-thumb{background-color:#cacdd0;-webkit-transition:all .2s;transition:all .2s}.job-list::-webkit-scrollbar-thumb:hover{background-color:#b1b4b8}.job-list--bookmarks{border-radius:.25rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);left:50%;min-height:4.75rem;min-width:21.25rem;opacity:0;overflow:hidden;pointer-events:none;position:fixed;top:5.125rem;-webkit-transform:scale(.9) translateX(-50%);transform:scale(.9) translateX(-50%);-webkit-transform-origin:left;transform-origin:left;transition:all .2s;visibility:hidden;width:21.25rem;z-index:10}.job-list--bookmarks:hover{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-list--bookmarks:before{color:#616268;content:"Nothing bookmarked yet...";font-size:.8125rem;left:50%;position:absolute;top:49%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);z-index:-1}.job-list--visible{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-item{background-color:#fff;cursor:pointer;padding:.875rem 1.25rem;transition:all .2s}.job-item:not(:nth-child(7)){border-bottom:.0625rem solid #ebeff1}.job-item--active,.job-item:hover{background-color:#f4f5f7}.job-item__link{display:flex;height:100%;width:100%}.job-item__badge{align-items:center;background-color:#8dd335;border-radius:.3125rem;display:flex;font-size:.8125rem;font-weight:600;height:2.875rem;justify-content:center;margin-right:.8125rem;width:2.375rem}.job-item:nth-child(4n+2) .job-item__badge{background-color:#3d87f1}.job-item:nth-child(4n+3) .job-item__badge{background-color:#d2d631}.job-item:nth-child(4n+4) .job-item__badge{background-color:#d96a46}.job-item__company{font-size:.75rem;font-style:italic;margin-bottom:.125rem}.job-item__extras{-webkit-column-gap:.625rem;column-gap:.625rem;display:grid;grid-template-columns:4.0625rem 4.5rem 4.0625rem}.job-item__extra{color:#4d5054;font-size:.6875rem}.job-item__extra-icon{color:#bec5ce;font-size:.625rem;margin-right:.0625rem}.job-item__right{align-items:flex-end;display:flex;flex-direction:column;margin-left:auto}.job-item__bookmark-icon{color:#d7dbe0;cursor:pointer;font-size:.875rem;transition:all .2s}.job-item__bookmark-icon--bookmarked{color:#2671dd}.job-item__time{color:#515459;font-size:.625rem;margin-top:.25rem}.main{justify-content:center}.intro,.main{align-items:center;display:flex;flex-direction:column}.intro{margin-top:1.25rem;row-gap:2.0625rem;row-gap:1.25rem}.search{-webkit-animation:intro .3s .1s backwards;animation:intro .3s .1s backwards;position:relative}.search__submit-btn{cursor:pointer;left:1.5625rem;position:absolute;top:1.0625rem}.search__icon{color:rgba(0,0,0,.73);transition:all .2s}.search__icon:focus,.search__icon:hover{color:rgba(0,0,0,.93)}.search__input{background-color:hsla(0,0%,100%,.9);border-radius:.25rem;caret-color:rgba(0,0,0,.5);color:rgba(0,0,0,.9);height:3.5rem;padding-bottom:.125rem;padding-left:3.4375rem;padding-right:.9375rem;transition:all .2s,box-shadow .1s;width:38.125rem}.search__input::selection{background-color:rgba(0,0,0,.25)}.search__input:focus,.search__input:hover{background-color:#fff}.search__input:focus{box-shadow:0 0 0 .25rem hsla(0,0%,100%,.4)}.search__input::-webkit-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input:-ms-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input::placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input--invalid{box-shadow:0 0 0 .25rem rgba(47,19,19,.729)}.container{-webkit-animation:intro .3s .2s backwards;animation:intro .3s .2s backwards;background-color:#fff;border-radius:.5rem;border-top-right-radius:.5625rem;box-shadow:0 .1875rem .3125rem rgba(0,0,0,.07);display:flex;height:38.5rem;margin:2.5rem .75rem 0;width:61rem}.search-results{cursor:default;display:flex;flex-direction:column;position:relative;width:21.25rem}.search-results__top{align-items:center;border-bottom:.0625rem solid #e8edf0;display:flex;justify-content:space-between;padding:.625rem 1.25rem}.count{font-size:.75rem}.sorting__icon{color:#4c4f50;font-size:.6875rem;margin-right:.3125rem}.sorting__button{background-color:#e8edf0;border-radius:.1875rem;cursor:pointer;font-size:.625rem;margin-left:.125rem;padding:.375rem .5rem;text-transform:uppercase;transition:all .2s}.sorting__button:focus,.sorting__button:hover{background-color:#d0d5d8}.sorting__button--active,.sorting__button--active:focus,.sorting__button--active:hover{background-color:#3c4041;color:#fff}.pagination{align-items:center;border-top:.0625rem solid #e8edf0;display:flex;height:2.5rem;justify-content:space-between;margin-top:auto;padding:0 1.25rem .0625rem}.pagination__button{background-color:#eceff2;border-radius:31.25rem;color:#747c82;cursor:pointer;font-size:.625rem;padding:.25rem .625rem;transition:all .2s,visibility 0s}.pagination__button:focus,.pagination__button:hover{background-color:#dde2e6}.pagination__button--hidden{visibility:hidden}.pagination__number{font-weight:500}.pagination__icon{color:#9fa6b0;font-size:.5rem}.job-details{background-color:#eff2f5;border-bottom-right-radius:.5rem;border-top-right-radius:.75rem;flex:1;position:relative}.job-details__start-view{left:50%;position:absolute;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.job-details__start-text{color:#24292d;font-size:.875rem;opacity:.55;text-align:center;width:17.1875rem}.job-details__start-text--big{color:#0d1114;font-size:1.125rem;font-weight:600;margin-bottom:.625rem}.job-details__content{height:100%}.job-details__cover-img{border-top-right-radius:.5rem;height:10.875rem;-o-object-fit:cover;object-fit:cover;position:absolute;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none;width:100%;z-index:0}.job-details__footer{border-top:.0625rem solid #dce2e8;margin-left:2.625rem;margin-right:2.625rem;margin-top:2.0625rem;padding-top:.8125rem}.job-details__footer-text{color:#858b8f;font-size:.625rem}.apply-btn{align-items:center;background-color:#2671dd;border-radius:.1875rem;color:hsla(0,0%,100%,.92);cursor:pointer;display:flex;font-size:.6875rem;padding:.3125rem .625rem .375rem;position:absolute;right:.75rem;text-transform:uppercase;top:.75rem;transition:all .2s;z-index:2}.apply-btn:focus,.apply-btn:hover{background-color:#1d60bd;color:#fff}.apply-btn__icon{color:hsla(0,0%,100%,.65);font-size:.5rem;margin-left:.25rem;margin-top:-.0625rem}.job-info{-webkit-column-gap:1rem;column-gap:1rem;display:flex;margin-bottom:2.5rem;padding-top:7.5rem;position:relative;z-index:1}.job-info:before{background-image:linear-gradient(0deg,rgba(0,0,0,.7),rgba(0,0,0,.15));border-top-right-radius:.5rem;content:"";height:10.875rem;left:0;position:absolute;top:0;width:100%;z-index:-1}.job-info__left{padding-left:2.625rem}.job-info__right{padding-right:2.625rem}.job-info__badge{align-items:center;background-color:#d0d335;border-radius:.3125rem;display:flex;font-size:1.375rem;font-weight:600;height:4.375rem;justify-content:center;margin-bottom:.8125rem;width:3.4375rem}.job-info__below-badge{display:flex;justify-content:space-between}.job-info__time{color:#494d4f;font-size:.75rem;-webkit-transform:translateY(.0625rem);transform:translateY(.0625rem)}.job-info__bookmark-btn{cursor:pointer}.job-info__bookmark-btn:hover .job-info__bookmark-icon{color:#2671dd}.job-info__bookmark-icon{color:#d7dbe0;font-size:1.125rem;transition:all .2s}.job-info__bookmark-icon--bookmarked{color:#2671dd}.job-info__company{color:hsla(0,0%,100%,.8);font-size:.875rem;font-style:italic}.job-info__description{font-size:.875rem;line-height:1.4;margin-bottom:.75rem;margin-top:1.125rem}.job-info__extras{-webkit-column-gap:2.1875rem;column-gap:2.1875rem;display:flex}.job-info__extra{align-items:center;display:flex;font-size:.75rem}.job-info__extra-icon{align-items:center;background-color:#e4e9ed;border-radius:50%;color:#a1a8b0;display:flex;font-size:.5625rem;height:1.4375rem;justify-content:center;margin-right:.5rem;width:1.4375rem}.qualifications{display:flex;margin-bottom:1.875rem;padding-left:2.625rem;padding-right:2.625rem}.qualifications__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.qualifications__left{margin-right:2.1875rem}.qualifications__list{display:flex;flex-wrap:wrap;gap:.375rem}.qualifications__item{background-color:#e6ebee;border-radius:.125rem;color:#494d4f;font-size:.8125rem;padding:.375rem .625rem}.reviews{display:flex;padding-left:2.625rem;padding-right:2.625rem}.reviews__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.reviews__left{margin-right:2.1875rem}.reviews__list{-webkit-column-gap:1.25rem;column-gap:1.25rem;display:grid;flex:1;grid-template-columns:1fr 1fr;grid-template-rows:auto auto;row-gap:1.25rem}.reviews__item{color:#494d4f;font-size:.8125rem;font-style:italic;position:relative;-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.reviews__item:before{color:#d2d7db;content:"“";font-size:3.125rem;left:-.625rem;position:absolute;top:-.9375rem;-webkit-transform:translateZ(-.0625rem);transform:translateZ(-.0625rem)}.footer{align-items:center;color:#a4a9ac;display:flex;justify-content:space-between;margin:.9375rem auto 0;max-width:62.5rem;padding:0 .75rem}.copyright{font-size:.6875rem}.copyright ::selection{background-color:hsla(0,0%,100%,.1)}.copyright__text{line-height:1.2}.copyright__link{text-decoration:underline}.copyright__icon{color:#aeb3b6;font-size:.625rem;margin-left:.125rem;margin-right:.25rem}.jobs-available{align-self:flex-start;font-size:.6875rem}.spinner{-webkit-animation:spinner 1s linear infinite;animation:spinner 1s linear infinite;border-radius:50%;position:absolute;visibility:hidden}.spinner--search{border:.3125rem solid #e2e7e9;border-left-color:#ccd1d3;height:5.3125rem;left:50%;top:18%;width:5.3125rem}.spinner--job-details{border:.375rem solid #d5d9db;border-left-color:#bbc0c2;height:6.5625rem;left:50%;top:40%;width:6.5625rem}.spinner--visible{visibility:visible}.error{background:#fff;border-radius:.1875rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);display:flex;min-height:2.875rem;opacity:0;padding:.875rem 1.25rem;position:absolute;right:.9375rem;top:.9375rem;-webkit-transform:translateY(-7.5rem);transform:translateY(-7.5rem);transition:all .3s;visibility:hidden;width:17.5rem}.error--visible{opacity:1;-webkit-transform:initial;transform:none;visibility:initial}.error__icon{color:#7b4040;font-size:1rem;margin-top:.125rem}.error__right{margin-left:.625rem}.error__title{font-size:.75rem;font-weight:500;margin-bottom:.0625rem;text-transform:uppercase}.error__text{color:#616268;font-size:.8125rem}@media (max-height:925px) and (min-width:1010px){.body{padding-bottom:3.125rem}}@media (max-width:1179px){.job-list--bookmarks{right:0}}@media (max-width:1009px){.body{padding:0 .75rem 3.125rem}.header__top{max-width:50rem;padding-left:0;padding-right:0}.container{border-radius:.5rem;flex-direction:column;height:auto;max-width:50rem;overflow:hidden;width:100%}.search-results{width:100%}.job-details{display:none}.footer{max-width:50rem;padding-left:0;padding-right:0}}@media (max-width:660px){.intro,.search,.search__input{width:100%}.footer{justify-content:center}.copyright{text-align:center}.jobs-available{display:none;margin-left:.9375rem;text-align:right}.intro{row-gap:1.5625rem}.first-heading{max-width:25rem;text-align:center}}@media (max-width:370px){.job-list--bookmarks{min-width:auto;width:93vw}.job-item{width:100%}.job-item__badge{display:none}.error{left:50%;right:auto;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:93vw}} diff --git a/Supplemental/video 38/dist/main.js b/Supplemental/video 38/dist/main.js new file mode 100644 index 0000000..eba8c55 --- /dev/null +++ b/Supplemental/video 38/dist/main.js @@ -0,0 +1,2 @@ +/*! For license information please see main.js.LICENSE.txt */ +!function(){var t={9662:function(t,r,e){var n=e(614),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a function")}},9483:function(t,r,e){var n=e(4411),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a constructor")}},6077:function(t,r,e){var n=e(614),o=String,i=TypeError;t.exports=function(t){if("object"==typeof t||n(t))return t;throw i("Can't set "+o(t)+" as a prototype")}},1223:function(t,r,e){var n=e(5112),o=e(30),i=e(3070).f,a=n("unscopables"),c=Array.prototype;null==c[a]&&i(c,a,{configurable:!0,value:o(null)}),t.exports=function(t){c[a][t]=!0}},5787:function(t,r,e){var n=e(7976),o=TypeError;t.exports=function(t,r){if(n(r,t))return t;throw o("Incorrect invocation")}},9670:function(t,r,e){var n=e(111),o=String,i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not an object")}},8533:function(t,r,e){"use strict";var n=e(2092).forEach,o=e(2133)("forEach");t.exports=o?[].forEach:function(t){return n(this,t,arguments.length>1?arguments[1]:void 0)}},8457:function(t,r,e){"use strict";var n=e(9974),o=e(6916),i=e(7908),a=e(3411),c=e(7659),u=e(4411),s=e(6244),f=e(6135),l=e(8554),p=e(1246),h=Array;t.exports=function(t){var r=i(t),e=u(this),v=arguments.length,d=v>1?arguments[1]:void 0,y=void 0!==d;y&&(d=n(d,v>2?arguments[2]:void 0));var m,g,b,x,w,_,S=p(r),j=0;if(!S||this===h&&c(S))for(m=s(r),g=e?new this(m):h(m);m>j;j++)_=y?d(r[j],j):r[j],f(g,j,_);else for(w=(x=l(r,S)).next,g=e?new this:[];!(b=o(w,x)).done;j++)_=y?a(x,d,[b.value,j],!0):b.value,f(g,j,_);return g.length=j,g}},1318:function(t,r,e){var n=e(5656),o=e(1400),i=e(6244),a=function(t){return function(r,e,a){var c,u=n(r),s=i(u),f=o(a,s);if(t&&e!=e){for(;s>f;)if((c=u[f++])!=c)return!0}else for(;s>f;f++)if((t||f in u)&&u[f]===e)return t||f||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},2092:function(t,r,e){var n=e(9974),o=e(1702),i=e(8361),a=e(7908),c=e(6244),u=e(5417),s=o([].push),f=function(t){var r=1==t,e=2==t,o=3==t,f=4==t,l=6==t,p=7==t,h=5==t||l;return function(v,d,y,m){for(var g,b,x=a(v),w=i(x),_=n(d,y),S=c(w),j=0,L=m||u,E=r?L(v,S):e||p?L(v,0):void 0;S>j;j++)if((h||j in w)&&(b=_(g=w[j],j,x),t))if(r)E[j]=b;else if(b)switch(t){case 3:return!0;case 5:return g;case 6:return j;case 2:s(E,g)}else switch(t){case 4:return!1;case 7:s(E,g)}return l?-1:o||f?f:E}};t.exports={forEach:f(0),map:f(1),filter:f(2),some:f(3),every:f(4),find:f(5),findIndex:f(6),filterReject:f(7)}},1194:function(t,r,e){var n=e(7293),o=e(5112),i=e(7392),a=o("species");t.exports=function(t){return i>=51||!n((function(){var r=[];return(r.constructor={})[a]=function(){return{foo:1}},1!==r[t](Boolean).foo}))}},2133:function(t,r,e){"use strict";var n=e(7293);t.exports=function(t,r){var e=[][t];return!!e&&n((function(){e.call(null,r||function(){return 1},1)}))}},1589:function(t,r,e){var n=e(1400),o=e(6244),i=e(6135),a=Array,c=Math.max;t.exports=function(t,r,e){for(var u=o(t),s=n(r,u),f=n(void 0===e?u:e,u),l=a(c(f-s,0)),p=0;s0;)t[n]=t[--n];n!==i++&&(t[n]=e)}return t},c=function(t,r,e,n){for(var o=r.length,i=e.length,a=0,c=0;a9007199254740991)throw r("Maximum allowed index exceeded");return t}},8324:function(t){t.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},8509:function(t,r,e){var n=e(317)("span").classList,o=n&&n.constructor&&n.constructor.prototype;t.exports=o===Object.prototype?void 0:o},8886:function(t,r,e){var n=e(8113).match(/firefox\/(\d+)/i);t.exports=!!n&&+n[1]},7871:function(t){t.exports="object"==typeof window&&"object"!=typeof Deno},256:function(t,r,e){var n=e(8113);t.exports=/MSIE|Trident/.test(n)},1528:function(t,r,e){var n=e(8113),o=e(7854);t.exports=/ipad|iphone|ipod/i.test(n)&&void 0!==o.Pebble},6833:function(t,r,e){var n=e(8113);t.exports=/(?:ipad|iphone|ipod).*applewebkit/i.test(n)},5268:function(t,r,e){var n=e(4326),o=e(7854);t.exports="process"==n(o.process)},1036:function(t,r,e){var n=e(8113);t.exports=/web0s(?!.*chrome)/i.test(n)},8113:function(t,r,e){var n=e(5005);t.exports=n("navigator","userAgent")||""},7392:function(t,r,e){var n,o,i=e(7854),a=e(8113),c=i.process,u=i.Deno,s=c&&c.versions||u&&u.version,f=s&&s.v8;f&&(o=(n=f.split("."))[0]>0&&n[0]<4?1:+(n[0]+n[1])),!o&&a&&(!(n=a.match(/Edge\/(\d+)/))||n[1]>=74)&&(n=a.match(/Chrome\/(\d+)/))&&(o=+n[1]),t.exports=o},8008:function(t,r,e){var n=e(8113).match(/AppleWebKit\/(\d+)\./);t.exports=!!n&&+n[1]},748:function(t){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(t,r,e){var n=e(7854),o=e(1236).f,i=e(8880),a=e(8052),c=e(3072),u=e(9920),s=e(4705);t.exports=function(t,r){var e,f,l,p,h,v=t.target,d=t.global,y=t.stat;if(e=d?n:y?n[v]||c(v,{}):(n[v]||{}).prototype)for(f in r){if(p=r[f],l=t.dontCallGetSet?(h=o(e,f))&&h.value:e[f],!s(d?f:v+(y?".":"#")+f,t.forced)&&void 0!==l){if(typeof p==typeof l)continue;u(p,l)}(t.sham||l&&l.sham)&&i(p,"sham",!0),a(e,f,p,t)}}},7293:function(t){t.exports=function(t){try{return!!t()}catch(t){return!0}}},2104:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.apply,a=o.call;t.exports="object"==typeof Reflect&&Reflect.apply||(n?a.bind(i):function(){return a.apply(i,arguments)})},9974:function(t,r,e){var n=e(1702),o=e(9662),i=e(4374),a=n(n.bind);t.exports=function(t,r){return o(t),void 0===r?t:i?a(t,r):function(){return t.apply(r,arguments)}}},4374:function(t,r,e){var n=e(7293);t.exports=!n((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))},6916:function(t,r,e){var n=e(4374),o=Function.prototype.call;t.exports=n?o.bind(o):function(){return o.apply(o,arguments)}},6530:function(t,r,e){var n=e(9781),o=e(2597),i=Function.prototype,a=n&&Object.getOwnPropertyDescriptor,c=o(i,"name"),u=c&&"something"===function(){}.name,s=c&&(!n||n&&a(i,"name").configurable);t.exports={EXISTS:c,PROPER:u,CONFIGURABLE:s}},1702:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.bind,a=o.call,c=n&&i.bind(a,a);t.exports=n?function(t){return t&&c(t)}:function(t){return t&&function(){return a.apply(t,arguments)}}},5005:function(t,r,e){var n=e(7854),o=e(614),i=function(t){return o(t)?t:void 0};t.exports=function(t,r){return arguments.length<2?i(n[t]):n[t]&&n[t][r]}},1246:function(t,r,e){var n=e(648),o=e(8173),i=e(7497),a=e(5112)("iterator");t.exports=function(t){if(null!=t)return o(t,a)||o(t,"@@iterator")||i[n(t)]}},8554:function(t,r,e){var n=e(6916),o=e(9662),i=e(9670),a=e(6330),c=e(1246),u=TypeError;t.exports=function(t,r){var e=arguments.length<2?c(t):r;if(o(e))return i(n(e,t));throw u(a(t)+" is not iterable")}},8173:function(t,r,e){var n=e(9662);t.exports=function(t,r){var e=t[r];return null==e?void 0:n(e)}},7854:function(t,r,e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e.g&&e.g)||function(){return this}()||Function("return this")()},2597:function(t,r,e){var n=e(1702),o=e(7908),i=n({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,r){return i(o(t),r)}},3501:function(t){t.exports={}},842:function(t,r,e){var n=e(7854);t.exports=function(t,r){var e=n.console;e&&e.error&&(1==arguments.length?e.error(t):e.error(t,r))}},490:function(t,r,e){var n=e(5005);t.exports=n("document","documentElement")},4664:function(t,r,e){var n=e(9781),o=e(7293),i=e(317);t.exports=!n&&!o((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},8361:function(t,r,e){var n=e(1702),o=e(7293),i=e(4326),a=Object,c=n("".split);t.exports=o((function(){return!a("z").propertyIsEnumerable(0)}))?function(t){return"String"==i(t)?c(t,""):a(t)}:a},2788:function(t,r,e){var n=e(1702),o=e(614),i=e(5465),a=n(Function.toString);o(i.inspectSource)||(i.inspectSource=function(t){return a(t)}),t.exports=i.inspectSource},9909:function(t,r,e){var n,o,i,a=e(8536),c=e(7854),u=e(1702),s=e(111),f=e(8880),l=e(2597),p=e(5465),h=e(6200),v=e(3501),d="Object already initialized",y=c.TypeError,m=c.WeakMap;if(a||p.state){var g=p.state||(p.state=new m),b=u(g.get),x=u(g.has),w=u(g.set);n=function(t,r){if(x(g,t))throw new y(d);return r.facade=t,w(g,t,r),r},o=function(t){return b(g,t)||{}},i=function(t){return x(g,t)}}else{var _=h("state");v[_]=!0,n=function(t,r){if(l(t,_))throw new y(d);return r.facade=t,f(t,_,r),r},o=function(t){return l(t,_)?t[_]:{}},i=function(t){return l(t,_)}}t.exports={set:n,get:o,has:i,enforce:function(t){return i(t)?o(t):n(t,{})},getterFor:function(t){return function(r){var e;if(!s(r)||(e=o(r)).type!==t)throw y("Incompatible receiver, "+t+" required");return e}}}},7659:function(t,r,e){var n=e(5112),o=e(7497),i=n("iterator"),a=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||a[i]===t)}},3157:function(t,r,e){var n=e(4326);t.exports=Array.isArray||function(t){return"Array"==n(t)}},614:function(t){t.exports=function(t){return"function"==typeof t}},4411:function(t,r,e){var n=e(1702),o=e(7293),i=e(614),a=e(648),c=e(5005),u=e(2788),s=function(){},f=[],l=c("Reflect","construct"),p=/^\s*(?:class|function)\b/,h=n(p.exec),v=!p.exec(s),d=function(t){if(!i(t))return!1;try{return l(s,f,t),!0}catch(t){return!1}},y=function(t){if(!i(t))return!1;switch(a(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return v||!!h(p,u(t))}catch(t){return!0}};y.sham=!0,t.exports=!l||o((function(){var t;return d(d.call)||!d(Object)||!d((function(){t=!0}))||t}))?y:d},4705:function(t,r,e){var n=e(7293),o=e(614),i=/#|\.prototype\./,a=function(t,r){var e=u[c(t)];return e==f||e!=s&&(o(r)?n(r):!!r)},c=a.normalize=function(t){return String(t).replace(i,".").toLowerCase()},u=a.data={},s=a.NATIVE="N",f=a.POLYFILL="P";t.exports=a},111:function(t,r,e){var n=e(614);t.exports=function(t){return"object"==typeof t?null!==t:n(t)}},1913:function(t){t.exports=!1},7850:function(t,r,e){var n=e(111),o=e(4326),i=e(5112)("match");t.exports=function(t){var r;return n(t)&&(void 0!==(r=t[i])?!!r:"RegExp"==o(t))}},2190:function(t,r,e){var n=e(5005),o=e(614),i=e(7976),a=e(3307),c=Object;t.exports=a?function(t){return"symbol"==typeof t}:function(t){var r=n("Symbol");return o(r)&&i(r.prototype,c(t))}},408:function(t,r,e){var n=e(9974),o=e(6916),i=e(9670),a=e(6330),c=e(7659),u=e(6244),s=e(7976),f=e(8554),l=e(1246),p=e(9212),h=TypeError,v=function(t,r){this.stopped=t,this.result=r},d=v.prototype;t.exports=function(t,r,e){var y,m,g,b,x,w,_,S=e&&e.that,j=!(!e||!e.AS_ENTRIES),L=!(!e||!e.IS_ITERATOR),E=!(!e||!e.INTERRUPTED),O=n(r,S),k=function(t){return y&&p(y,"normal",t),new v(!0,t)},P=function(t){return j?(i(t),E?O(t[0],t[1],k):O(t[0],t[1])):E?O(t,k):O(t)};if(L)y=t;else{if(!(m=l(t)))throw h(a(t)+" is not iterable");if(c(m)){for(g=0,b=u(t);b>g;g++)if((x=P(t[g]))&&s(d,x))return x;return new v(!1)}y=f(t,m)}for(w=y.next;!(_=o(w,y)).done;){try{x=P(_.value)}catch(t){p(y,"throw",t)}if("object"==typeof x&&x&&s(d,x))return x}return new v(!1)}},9212:function(t,r,e){var n=e(6916),o=e(9670),i=e(8173);t.exports=function(t,r,e){var a,c;o(t);try{if(!(a=i(t,"return"))){if("throw"===r)throw e;return e}a=n(a,t)}catch(t){c=!0,a=t}if("throw"===r)throw e;if(c)throw a;return o(a),e}},3383:function(t,r,e){"use strict";var n,o,i,a=e(7293),c=e(614),u=e(30),s=e(9518),f=e(8052),l=e(5112),p=e(1913),h=l("iterator"),v=!1;[].keys&&("next"in(i=[].keys())?(o=s(s(i)))!==Object.prototype&&(n=o):v=!0),null==n||a((function(){var t={};return n[h].call(t)!==t}))?n={}:p&&(n=u(n)),c(n[h])||f(n,h,(function(){return this})),t.exports={IteratorPrototype:n,BUGGY_SAFARI_ITERATORS:v}},7497:function(t){t.exports={}},6244:function(t,r,e){var n=e(7466);t.exports=function(t){return n(t.length)}},6339:function(t,r,e){var n=e(7293),o=e(614),i=e(2597),a=e(9781),c=e(6530).CONFIGURABLE,u=e(2788),s=e(9909),f=s.enforce,l=s.get,p=Object.defineProperty,h=a&&!n((function(){return 8!==p((function(){}),"length",{value:8}).length})),v=String(String).split("String"),d=t.exports=function(t,r,e){"Symbol("===String(r).slice(0,7)&&(r="["+String(r).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),e&&e.getter&&(r="get "+r),e&&e.setter&&(r="set "+r),(!i(t,"name")||c&&t.name!==r)&&p(t,"name",{value:r,configurable:!0}),h&&e&&i(e,"arity")&&t.length!==e.arity&&p(t,"length",{value:e.arity});try{e&&i(e,"constructor")&&e.constructor?a&&p(t,"prototype",{writable:!1}):t.prototype&&(t.prototype=void 0)}catch(t){}var n=f(t);return i(n,"source")||(n.source=v.join("string"==typeof r?r:"")),t};Function.prototype.toString=d((function(){return o(this)&&l(this).source||u(this)}),"toString")},4758:function(t){var r=Math.ceil,e=Math.floor;t.exports=Math.trunc||function(t){var n=+t;return(n>0?e:r)(n)}},5948:function(t,r,e){var n,o,i,a,c,u,s,f,l=e(7854),p=e(9974),h=e(1236).f,v=e(261).set,d=e(6833),y=e(1528),m=e(1036),g=e(5268),b=l.MutationObserver||l.WebKitMutationObserver,x=l.document,w=l.process,_=l.Promise,S=h(l,"queueMicrotask"),j=S&&S.value;j||(n=function(){var t,r;for(g&&(t=w.domain)&&t.exit();o;){r=o.fn,o=o.next;try{r()}catch(t){throw o?a():i=void 0,t}}i=void 0,t&&t.enter()},d||g||m||!b||!x?!y&&_&&_.resolve?((s=_.resolve(void 0)).constructor=_,f=p(s.then,s),a=function(){f(n)}):g?a=function(){w.nextTick(n)}:(v=p(v,l),a=function(){v(n)}):(c=!0,u=x.createTextNode(""),new b(n).observe(u,{characterData:!0}),a=function(){u.data=c=!c})),t.exports=j||function(t){var r={fn:t,next:void 0};i&&(i.next=r),o||(o=r,a()),i=r}},735:function(t,r,e){var n=e(133);t.exports=n&&!!Symbol.for&&!!Symbol.keyFor},133:function(t,r,e){var n=e(7392),o=e(7293);t.exports=!!Object.getOwnPropertySymbols&&!o((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&n&&n<41}))},8536:function(t,r,e){var n=e(7854),o=e(614),i=e(2788),a=n.WeakMap;t.exports=o(a)&&/native code/.test(i(a))},8523:function(t,r,e){"use strict";var n=e(9662),o=function(t){var r,e;this.promise=new t((function(t,n){if(void 0!==r||void 0!==e)throw TypeError("Bad Promise constructor");r=t,e=n})),this.resolve=n(r),this.reject=n(e)};t.exports.f=function(t){return new o(t)}},3929:function(t,r,e){var n=e(7850),o=TypeError;t.exports=function(t){if(n(t))throw o("The method doesn't accept regular expressions");return t}},30:function(t,r,e){var n,o=e(9670),i=e(6048),a=e(748),c=e(3501),u=e(490),s=e(317),f=e(6200)("IE_PROTO"),l=function(){},p=function(t){return" + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/Supplemental/video 38/src/index.js b/Supplemental/video 38/src/index.js new file mode 100644 index 0000000..7dfe77f --- /dev/null +++ b/Supplemental/video 38/src/index.js @@ -0,0 +1,12 @@ +import './components/Bookmarks.js'; +import './components/Error.js'; +import './components/JobDetails.js'; +import './components/JobList.js'; +import './components/Pagination.js'; +import './components/Router.js'; +import './components/Search.js'; +import './components/Sorting.js'; +import './components/Spinner.js'; +import './components/Storage.js'; + +// import './index.css'; \ No newline at end of file diff --git a/Supplemental/video 38/webpack.config.js b/Supplemental/video 38/webpack.config.js new file mode 100644 index 0000000..b970c2c --- /dev/null +++ b/Supplemental/video 38/webpack.config.js @@ -0,0 +1,27 @@ +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); + +module.exports = { + mode: 'production', + entry: './src/index.js', + output: { + filename: 'main.js' + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: 'main.css' + }) + ], + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: ['babel-loader'] + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] + } + ] + } +}; \ No newline at end of file From 1e926563ae50a598e754848f08c943b12fc43cd6 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Fri, 10 Jun 2022 12:35:08 +0200 Subject: [PATCH 06/40] Rename folders for order --- CorpComment/{video 4 => video 04}/index.html | 0 CorpComment/{video 4 => video 04}/script.js | 0 CorpComment/{video 4 => video 04}/style.css | 0 CorpComment/{video 5 => video 05}/index.html | 0 CorpComment/{video 5 => video 05}/script.js | 0 CorpComment/{video 5 => video 05}/style.css | 0 CorpComment/{video 6 => video 06}/index.html | 0 CorpComment/{video 6 => video 06}/script.js | 0 CorpComment/{video 6 => video 06}/style.css | 0 CorpComment/{video 7 => video 07}/index.html | 0 CorpComment/{video 7 => video 07}/script.js | 0 CorpComment/{video 7 => video 07}/style.css | 0 CorpComment/{video 8 => video 08}/index.html | 0 CorpComment/{video 8 => video 08}/script.js | 0 CorpComment/{video 8 => video 08}/style.css | 0 CorpComment/{video 9 => video 09}/index.html | 0 CorpComment/{video 9 => video 09}/script.js | 0 CorpComment/{video 9 => video 09}/style.css | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename CorpComment/{video 4 => video 04}/index.html (100%) rename CorpComment/{video 4 => video 04}/script.js (100%) rename CorpComment/{video 4 => video 04}/style.css (100%) rename CorpComment/{video 5 => video 05}/index.html (100%) rename CorpComment/{video 5 => video 05}/script.js (100%) rename CorpComment/{video 5 => video 05}/style.css (100%) rename CorpComment/{video 6 => video 06}/index.html (100%) rename CorpComment/{video 6 => video 06}/script.js (100%) rename CorpComment/{video 6 => video 06}/style.css (100%) rename CorpComment/{video 7 => video 07}/index.html (100%) rename CorpComment/{video 7 => video 07}/script.js (100%) rename CorpComment/{video 7 => video 07}/style.css (100%) rename CorpComment/{video 8 => video 08}/index.html (100%) rename CorpComment/{video 8 => video 08}/script.js (100%) rename CorpComment/{video 8 => video 08}/style.css (100%) rename CorpComment/{video 9 => video 09}/index.html (100%) rename CorpComment/{video 9 => video 09}/script.js (100%) rename CorpComment/{video 9 => video 09}/style.css (100%) diff --git a/CorpComment/video 4/index.html b/CorpComment/video 04/index.html similarity index 100% rename from CorpComment/video 4/index.html rename to CorpComment/video 04/index.html diff --git a/CorpComment/video 4/script.js b/CorpComment/video 04/script.js similarity index 100% rename from CorpComment/video 4/script.js rename to CorpComment/video 04/script.js diff --git a/CorpComment/video 4/style.css b/CorpComment/video 04/style.css similarity index 100% rename from CorpComment/video 4/style.css rename to CorpComment/video 04/style.css diff --git a/CorpComment/video 5/index.html b/CorpComment/video 05/index.html similarity index 100% rename from CorpComment/video 5/index.html rename to CorpComment/video 05/index.html diff --git a/CorpComment/video 5/script.js b/CorpComment/video 05/script.js similarity index 100% rename from CorpComment/video 5/script.js rename to CorpComment/video 05/script.js diff --git a/CorpComment/video 5/style.css b/CorpComment/video 05/style.css similarity index 100% rename from CorpComment/video 5/style.css rename to CorpComment/video 05/style.css diff --git a/CorpComment/video 6/index.html b/CorpComment/video 06/index.html similarity index 100% rename from CorpComment/video 6/index.html rename to CorpComment/video 06/index.html diff --git a/CorpComment/video 6/script.js b/CorpComment/video 06/script.js similarity index 100% rename from CorpComment/video 6/script.js rename to CorpComment/video 06/script.js diff --git a/CorpComment/video 6/style.css b/CorpComment/video 06/style.css similarity index 100% rename from CorpComment/video 6/style.css rename to CorpComment/video 06/style.css diff --git a/CorpComment/video 7/index.html b/CorpComment/video 07/index.html similarity index 100% rename from CorpComment/video 7/index.html rename to CorpComment/video 07/index.html diff --git a/CorpComment/video 7/script.js b/CorpComment/video 07/script.js similarity index 100% rename from CorpComment/video 7/script.js rename to CorpComment/video 07/script.js diff --git a/CorpComment/video 7/style.css b/CorpComment/video 07/style.css similarity index 100% rename from CorpComment/video 7/style.css rename to CorpComment/video 07/style.css diff --git a/CorpComment/video 8/index.html b/CorpComment/video 08/index.html similarity index 100% rename from CorpComment/video 8/index.html rename to CorpComment/video 08/index.html diff --git a/CorpComment/video 8/script.js b/CorpComment/video 08/script.js similarity index 100% rename from CorpComment/video 8/script.js rename to CorpComment/video 08/script.js diff --git a/CorpComment/video 8/style.css b/CorpComment/video 08/style.css similarity index 100% rename from CorpComment/video 8/style.css rename to CorpComment/video 08/style.css diff --git a/CorpComment/video 9/index.html b/CorpComment/video 09/index.html similarity index 100% rename from CorpComment/video 9/index.html rename to CorpComment/video 09/index.html diff --git a/CorpComment/video 9/script.js b/CorpComment/video 09/script.js similarity index 100% rename from CorpComment/video 9/script.js rename to CorpComment/video 09/script.js diff --git a/CorpComment/video 9/style.css b/CorpComment/video 09/style.css similarity index 100% rename from CorpComment/video 9/style.css rename to CorpComment/video 09/style.css From 405a775717911001ca7e0b176141a6137cd43396 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Fri, 10 Jun 2022 12:37:45 +0200 Subject: [PATCH 07/40] Fix Supplemental --- Supplemental/video 36/babel.config.js | 11 + Supplemental/video 36/dist/index.html | 121 + Supplemental/video 36/dist/main.css | 1 + Supplemental/video 36/dist/main.js | 2 + .../video 36/dist/main.js.LICENSE.txt | 1 + Supplemental/video 36/package-lock.json | 8346 +++++++++++++++++ Supplemental/video 36/package.json | 28 + Supplemental/video 36/postcss.config.js | 7 + Supplemental/video 36/src/common.js | 46 + .../video 36/src/components/Bookmarks.js | 51 + Supplemental/video 36/src/components/Error.js | 15 + .../video 36/src/components/JobDetails.js | 63 + .../video 36/src/components/JobList.js | 105 + .../video 36/src/components/Pagination.js | 58 + .../video 36/src/components/Router.js | 52 + .../video 36/src/components/Search.js | 73 + .../video 36/src/components/Sorting.js | 51 + .../video 36/src/components/Spinner.js | 11 + .../video 36/src/components/Storage.js | 8 + Supplemental/video 36/src/index.css | 1130 +++ Supplemental/video 36/src/index.html | 121 + Supplemental/video 36/src/index.js | 12 + Supplemental/video 36/webpack.config.js | 27 + Supplemental/video 37/index.html | 94 + Supplemental/video 37/script.js | 215 + Supplemental/video 37/style.css | 671 ++ 26 files changed, 11320 insertions(+) create mode 100644 Supplemental/video 36/babel.config.js create mode 100644 Supplemental/video 36/dist/index.html create mode 100644 Supplemental/video 36/dist/main.css create mode 100644 Supplemental/video 36/dist/main.js create mode 100644 Supplemental/video 36/dist/main.js.LICENSE.txt create mode 100644 Supplemental/video 36/package-lock.json create mode 100644 Supplemental/video 36/package.json create mode 100644 Supplemental/video 36/postcss.config.js create mode 100644 Supplemental/video 36/src/common.js create mode 100644 Supplemental/video 36/src/components/Bookmarks.js create mode 100644 Supplemental/video 36/src/components/Error.js create mode 100644 Supplemental/video 36/src/components/JobDetails.js create mode 100644 Supplemental/video 36/src/components/JobList.js create mode 100644 Supplemental/video 36/src/components/Pagination.js create mode 100644 Supplemental/video 36/src/components/Router.js create mode 100644 Supplemental/video 36/src/components/Search.js create mode 100644 Supplemental/video 36/src/components/Sorting.js create mode 100644 Supplemental/video 36/src/components/Spinner.js create mode 100644 Supplemental/video 36/src/components/Storage.js create mode 100644 Supplemental/video 36/src/index.css create mode 100644 Supplemental/video 36/src/index.html create mode 100644 Supplemental/video 36/src/index.js create mode 100644 Supplemental/video 36/webpack.config.js create mode 100644 Supplemental/video 37/index.html create mode 100644 Supplemental/video 37/script.js create mode 100644 Supplemental/video 37/style.css diff --git a/Supplemental/video 36/babel.config.js b/Supplemental/video 36/babel.config.js new file mode 100644 index 0000000..9c22988 --- /dev/null +++ b/Supplemental/video 36/babel.config.js @@ -0,0 +1,11 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + useBuiltIns: 'usage', + corejs: '3.0' + } + ] + ] +}; \ No newline at end of file diff --git a/Supplemental/video 36/dist/index.html b/Supplemental/video 36/dist/index.html new file mode 100644 index 0000000..217a364 --- /dev/null +++ b/Supplemental/video 36/dist/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/Supplemental/video 36/dist/main.css b/Supplemental/video 36/dist/main.css new file mode 100644 index 0000000..56d663d --- /dev/null +++ b/Supplemental/video 36/dist/main.css @@ -0,0 +1 @@ +*,:after,:before{box-sizing:border-box;margin:0;padding:0}ol,ul{list-style:none}a{color:inherit;text-decoration:initial}button{background-color:initial}button,input{border:initial;font:inherit;outline:initial}.u-bold{font-weight:700}@-webkit-keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@-webkit-keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}@keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}.body{background-color:#dee3e9;color:#16181c;font-family:Inter,sans-serif;min-height:100vh;position:relative;scrollbar-width:none}.body::-webkit-scrollbar{display:none}.first-heading{color:#fff;display:none;font-size:1.9375rem;font-size:1.6875rem;font-weight:400}.second-heading{color:#fff;font-size:1.4375rem;font-weight:500}.third-heading{font-size:.8125rem;font-weight:600}.fourth-heading{font-size:1rem;font-weight:600;text-transform:capitalize}.background{background-image:linear-gradient(125deg,#1f74f1 -10%,#0850b9);box-shadow:0 .125rem .1875rem rgba(0,0,0,.1);height:13.125rem;left:0;overflow:hidden;position:absolute;top:0;width:100%;z-index:-2}.background:before{background-image:linear-gradient(-180deg,rgba(0,0,0,.025),rgba(0,0,0,.075) 99%);bottom:0;content:"";left:0;position:absolute;right:0;top:0}.background__pattern{left:0;position:absolute;top:-1.5625rem;-webkit-transform:scale(1.1);transform:scale(1.1);-webkit-user-select:none;-ms-user-select:none;user-select:none;z-index:-1}.header{margin-bottom:0;position:relative}.header__top{align-items:center;-webkit-animation:intro .3s;animation:intro .3s;display:flex;justify-content:center;margin:0 auto;max-width:62.5rem;padding:2.5rem .75rem 0;position:relative}.header__submit-job{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.75rem;margin-right:0;padding-left:.625rem;text-transform:lowercase;-webkit-transform:translateY(-.125rem);transform:translateY(-.125rem)}.header__submit-job:before{background-color:hsla(0,0%,100%,.25);content:"";display:inline-block;height:.8125rem;margin-right:.5rem;-webkit-transform:translateY(.1875rem);transform:translateY(.1875rem);width:.125rem}.logo{margin-left:-.5rem;-webkit-user-select:none;-ms-user-select:none;user-select:none}.logo__img{margin-bottom:-.3125rem}.bookmarks-btn{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.8125rem;height:2rem;margin-left:.8125rem;padding-left:.8125rem;position:relative;text-transform:lowercase;transition:all .2s}.bookmarks-btn--active,.bookmarks-btn:focus,.bookmarks-btn:hover{color:#fff}.bookmarks-btn--active .bookmarks-btn__icon,.bookmarks-btn:focus .bookmarks-btn__icon,.bookmarks-btn:hover .bookmarks-btn__icon{color:hsla(0,0%,100%,.8)}.bookmarks-btn:before{background-color:hsla(0,0%,100%,.3);content:"";display:block;height:.9375rem;left:0;position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:.125rem}.bookmarks-btn__icon{color:hsla(0,0%,100%,.6);font-size:.625rem;margin-left:.125rem;-webkit-transform:translateY(-.0625rem);transform:translateY(-.0625rem);transition:all .2s}.job-list{background-color:#fff;scrollbar-color:#cacdd0 #fff;scrollbar-width:thin}.job-list::-webkit-scrollbar{width:.25rem}.job-list::-webkit-scrollbar-track{background-color:#fff}.job-list::-webkit-scrollbar-thumb{background-color:#cacdd0;-webkit-transition:all .2s;transition:all .2s}.job-list::-webkit-scrollbar-thumb:hover{background-color:#b1b4b8}.job-list--bookmarks{border-radius:.25rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);left:50%;min-height:4.75rem;min-width:21.25rem;opacity:0;overflow:hidden;pointer-events:none;position:fixed;top:5.125rem;-webkit-transform:scale(.9) translateX(-50%);transform:scale(.9) translateX(-50%);-webkit-transform-origin:left;transform-origin:left;transition:all .2s;visibility:hidden;width:21.25rem;z-index:10}.job-list--bookmarks:hover{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-list--bookmarks:before{color:#616268;content:"Nothing bookmarked yet...";font-size:.8125rem;left:50%;position:absolute;top:49%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);z-index:-1}.job-list--visible{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-item{background-color:#fff;cursor:pointer;padding:.875rem 1.25rem;transition:all .2s}.job-item:not(:nth-child(7)){border-bottom:.0625rem solid #ebeff1}.job-item--active,.job-item:hover{background-color:#f4f5f7}.job-item__link{display:flex;height:100%;width:100%}.job-item__badge{align-items:center;background-color:#8dd335;border-radius:.3125rem;display:flex;font-size:.8125rem;font-weight:600;height:2.875rem;justify-content:center;margin-right:.8125rem;width:2.375rem}.job-item:nth-child(4n+2) .job-item__badge{background-color:#3d87f1}.job-item:nth-child(4n+3) .job-item__badge{background-color:#d2d631}.job-item:nth-child(4n+4) .job-item__badge{background-color:#d96a46}.job-item__company{font-size:.75rem;font-style:italic;margin-bottom:.125rem}.job-item__extras{-webkit-column-gap:.625rem;column-gap:.625rem;display:grid;grid-template-columns:4.0625rem 4.5rem 4.0625rem}.job-item__extra{color:#4d5054;font-size:.6875rem}.job-item__extra-icon{color:#bec5ce;font-size:.625rem;margin-right:.0625rem}.job-item__right{align-items:flex-end;display:flex;flex-direction:column;margin-left:auto}.job-item__bookmark-icon{color:#d7dbe0;cursor:pointer;font-size:.875rem;transition:all .2s}.job-item__bookmark-icon--bookmarked{color:#2671dd}.job-item__time{color:#515459;font-size:.625rem;margin-top:.25rem}.main{justify-content:center}.intro,.main{align-items:center;display:flex;flex-direction:column}.intro{margin-top:1.25rem;row-gap:2.0625rem;row-gap:1.25rem}.search{-webkit-animation:intro .3s .1s backwards;animation:intro .3s .1s backwards;position:relative}.search__submit-btn{cursor:pointer;left:1.5625rem;position:absolute;top:1.0625rem}.search__icon{color:rgba(0,0,0,.73);transition:all .2s}.search__icon:focus,.search__icon:hover{color:rgba(0,0,0,.93)}.search__input{background-color:hsla(0,0%,100%,.9);border-radius:.25rem;caret-color:rgba(0,0,0,.5);color:rgba(0,0,0,.9);height:3.5rem;padding-bottom:.125rem;padding-left:3.4375rem;padding-right:.9375rem;transition:all .2s,box-shadow .1s;width:38.125rem}.search__input::selection{background-color:rgba(0,0,0,.25)}.search__input:focus,.search__input:hover{background-color:#fff}.search__input:focus{box-shadow:0 0 0 .25rem hsla(0,0%,100%,.4)}.search__input::-webkit-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input:-ms-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input::placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input--invalid{box-shadow:0 0 0 .25rem rgba(47,19,19,.729)}.container{-webkit-animation:intro .3s .2s backwards;animation:intro .3s .2s backwards;background-color:#fff;border-radius:.5rem;border-top-right-radius:.5625rem;box-shadow:0 .1875rem .3125rem rgba(0,0,0,.07);display:flex;height:38.5rem;margin:2.5rem .75rem 0;width:61rem}.search-results{cursor:default;display:flex;flex-direction:column;position:relative;width:21.25rem}.search-results__top{align-items:center;border-bottom:.0625rem solid #e8edf0;display:flex;justify-content:space-between;padding:.625rem 1.25rem}.count{font-size:.75rem}.sorting__icon{color:#4c4f50;font-size:.6875rem;margin-right:.3125rem}.sorting__button{background-color:#e8edf0;border-radius:.1875rem;cursor:pointer;font-size:.625rem;margin-left:.125rem;padding:.375rem .5rem;text-transform:uppercase;transition:all .2s}.sorting__button:focus,.sorting__button:hover{background-color:#d0d5d8}.sorting__button--active,.sorting__button--active:focus,.sorting__button--active:hover{background-color:#3c4041;color:#fff}.pagination{align-items:center;border-top:.0625rem solid #e8edf0;display:flex;height:2.5rem;justify-content:space-between;margin-top:auto;padding:0 1.25rem .0625rem}.pagination__button{background-color:#eceff2;border-radius:31.25rem;color:#747c82;cursor:pointer;font-size:.625rem;padding:.25rem .625rem;transition:all .2s,visibility 0s}.pagination__button:focus,.pagination__button:hover{background-color:#dde2e6}.pagination__button--hidden{visibility:hidden}.pagination__number{font-weight:500}.pagination__icon{color:#9fa6b0;font-size:.5rem}.job-details{background-color:#eff2f5;border-bottom-right-radius:.5rem;border-top-right-radius:.75rem;flex:1;position:relative}.job-details__start-view{left:50%;position:absolute;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.job-details__start-text{color:#24292d;font-size:.875rem;opacity:.55;text-align:center;width:17.1875rem}.job-details__start-text--big{color:#0d1114;font-size:1.125rem;font-weight:600;margin-bottom:.625rem}.job-details__content{height:100%}.job-details__cover-img{border-top-right-radius:.5rem;height:10.875rem;-o-object-fit:cover;object-fit:cover;position:absolute;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none;width:100%;z-index:0}.job-details__footer{border-top:.0625rem solid #dce2e8;margin-left:2.625rem;margin-right:2.625rem;margin-top:2.0625rem;padding-top:.8125rem}.job-details__footer-text{color:#858b8f;font-size:.625rem}.apply-btn{align-items:center;background-color:#2671dd;border-radius:.1875rem;color:hsla(0,0%,100%,.92);cursor:pointer;display:flex;font-size:.6875rem;padding:.3125rem .625rem .375rem;position:absolute;right:.75rem;text-transform:uppercase;top:.75rem;transition:all .2s;z-index:2}.apply-btn:focus,.apply-btn:hover{background-color:#1d60bd;color:#fff}.apply-btn__icon{color:hsla(0,0%,100%,.65);font-size:.5rem;margin-left:.25rem;margin-top:-.0625rem}.job-info{-webkit-column-gap:1rem;column-gap:1rem;display:flex;margin-bottom:2.5rem;padding-top:7.5rem;position:relative;z-index:1}.job-info:before{background-image:linear-gradient(0deg,rgba(0,0,0,.7),rgba(0,0,0,.15));border-top-right-radius:.5rem;content:"";height:10.875rem;left:0;position:absolute;top:0;width:100%;z-index:-1}.job-info__left{padding-left:2.625rem}.job-info__right{padding-right:2.625rem}.job-info__badge{align-items:center;background-color:#d0d335;border-radius:.3125rem;display:flex;font-size:1.375rem;font-weight:600;height:4.375rem;justify-content:center;margin-bottom:.8125rem;width:3.4375rem}.job-info__below-badge{display:flex;justify-content:space-between}.job-info__time{color:#494d4f;font-size:.75rem;-webkit-transform:translateY(.0625rem);transform:translateY(.0625rem)}.job-info__bookmark-btn{cursor:pointer}.job-info__bookmark-btn:hover .job-info__bookmark-icon{color:#2671dd}.job-info__bookmark-icon{color:#d7dbe0;font-size:1.125rem;transition:all .2s}.job-info__bookmark-icon--bookmarked{color:#2671dd}.job-info__company{color:hsla(0,0%,100%,.8);font-size:.875rem;font-style:italic}.job-info__description{font-size:.875rem;line-height:1.4;margin-bottom:.75rem;margin-top:1.125rem}.job-info__extras{-webkit-column-gap:2.1875rem;column-gap:2.1875rem;display:flex}.job-info__extra{align-items:center;display:flex;font-size:.75rem}.job-info__extra-icon{align-items:center;background-color:#e4e9ed;border-radius:50%;color:#a1a8b0;display:flex;font-size:.5625rem;height:1.4375rem;justify-content:center;margin-right:.5rem;width:1.4375rem}.qualifications{display:flex;margin-bottom:1.875rem;padding-left:2.625rem;padding-right:2.625rem}.qualifications__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.qualifications__left{margin-right:2.1875rem}.qualifications__list{display:flex;flex-wrap:wrap;gap:.375rem}.qualifications__item{background-color:#e6ebee;border-radius:.125rem;color:#494d4f;font-size:.8125rem;padding:.375rem .625rem}.reviews{display:flex;padding-left:2.625rem;padding-right:2.625rem}.reviews__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.reviews__left{margin-right:2.1875rem}.reviews__list{-webkit-column-gap:1.25rem;column-gap:1.25rem;display:grid;flex:1;grid-template-columns:1fr 1fr;grid-template-rows:auto auto;row-gap:1.25rem}.reviews__item{color:#494d4f;font-size:.8125rem;font-style:italic;position:relative;-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.reviews__item:before{color:#d2d7db;content:"“";font-size:3.125rem;left:-.625rem;position:absolute;top:-.9375rem;-webkit-transform:translateZ(-.0625rem);transform:translateZ(-.0625rem)}.footer{align-items:center;color:#a4a9ac;display:flex;justify-content:space-between;margin:.9375rem auto 0;max-width:62.5rem;padding:0 .75rem}.copyright{font-size:.6875rem}.copyright ::selection{background-color:hsla(0,0%,100%,.1)}.copyright__text{line-height:1.2}.copyright__link{text-decoration:underline}.copyright__icon{color:#aeb3b6;font-size:.625rem;margin-left:.125rem;margin-right:.25rem}.jobs-available{align-self:flex-start;font-size:.6875rem}.spinner{-webkit-animation:spinner 1s linear infinite;animation:spinner 1s linear infinite;border-radius:50%;position:absolute;visibility:hidden}.spinner--search{border:.3125rem solid #e2e7e9;border-left-color:#ccd1d3;height:5.3125rem;left:50%;top:18%;width:5.3125rem}.spinner--job-details{border:.375rem solid #d5d9db;border-left-color:#bbc0c2;height:6.5625rem;left:50%;top:40%;width:6.5625rem}.spinner--visible{visibility:visible}.error{background:#fff;border-radius:.1875rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);display:flex;min-height:2.875rem;opacity:0;padding:.875rem 1.25rem;position:absolute;right:.9375rem;top:.9375rem;-webkit-transform:translateY(-7.5rem);transform:translateY(-7.5rem);transition:all .3s;visibility:hidden;width:17.5rem}.error--visible{opacity:1;-webkit-transform:initial;transform:none;visibility:initial}.error__icon{color:#7b4040;font-size:1rem;margin-top:.125rem}.error__right{margin-left:.625rem}.error__title{font-size:.75rem;font-weight:500;margin-bottom:.0625rem;text-transform:uppercase}.error__text{color:#616268;font-size:.8125rem}@media (max-height:925px) and (min-width:1010px){.body{padding-bottom:3.125rem}}@media (max-width:1179px){.job-list--bookmarks{right:0}}@media (max-width:1009px){.body{padding:0 .75rem 3.125rem}.header__top{max-width:50rem;padding-left:0;padding-right:0}.container{border-radius:.5rem;flex-direction:column;height:auto;max-width:50rem;overflow:hidden;width:100%}.search-results{width:100%}.job-details{display:none}.footer{max-width:50rem;padding-left:0;padding-right:0}}@media (max-width:660px){.intro,.search,.search__input{width:100%}.footer{justify-content:center}.copyright{text-align:center}.jobs-available{display:none;margin-left:.9375rem;text-align:right}.intro{row-gap:1.5625rem}.first-heading{max-width:25rem;text-align:center}}@media (max-width:370px){.job-list--bookmarks{min-width:auto;width:93vw}.job-item{width:100%}.job-item__badge{display:none}.error{left:50%;right:auto;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:93vw}} diff --git a/Supplemental/video 36/dist/main.js b/Supplemental/video 36/dist/main.js new file mode 100644 index 0000000..eba8c55 --- /dev/null +++ b/Supplemental/video 36/dist/main.js @@ -0,0 +1,2 @@ +/*! For license information please see main.js.LICENSE.txt */ +!function(){var t={9662:function(t,r,e){var n=e(614),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a function")}},9483:function(t,r,e){var n=e(4411),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a constructor")}},6077:function(t,r,e){var n=e(614),o=String,i=TypeError;t.exports=function(t){if("object"==typeof t||n(t))return t;throw i("Can't set "+o(t)+" as a prototype")}},1223:function(t,r,e){var n=e(5112),o=e(30),i=e(3070).f,a=n("unscopables"),c=Array.prototype;null==c[a]&&i(c,a,{configurable:!0,value:o(null)}),t.exports=function(t){c[a][t]=!0}},5787:function(t,r,e){var n=e(7976),o=TypeError;t.exports=function(t,r){if(n(r,t))return t;throw o("Incorrect invocation")}},9670:function(t,r,e){var n=e(111),o=String,i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not an object")}},8533:function(t,r,e){"use strict";var n=e(2092).forEach,o=e(2133)("forEach");t.exports=o?[].forEach:function(t){return n(this,t,arguments.length>1?arguments[1]:void 0)}},8457:function(t,r,e){"use strict";var n=e(9974),o=e(6916),i=e(7908),a=e(3411),c=e(7659),u=e(4411),s=e(6244),f=e(6135),l=e(8554),p=e(1246),h=Array;t.exports=function(t){var r=i(t),e=u(this),v=arguments.length,d=v>1?arguments[1]:void 0,y=void 0!==d;y&&(d=n(d,v>2?arguments[2]:void 0));var m,g,b,x,w,_,S=p(r),j=0;if(!S||this===h&&c(S))for(m=s(r),g=e?new this(m):h(m);m>j;j++)_=y?d(r[j],j):r[j],f(g,j,_);else for(w=(x=l(r,S)).next,g=e?new this:[];!(b=o(w,x)).done;j++)_=y?a(x,d,[b.value,j],!0):b.value,f(g,j,_);return g.length=j,g}},1318:function(t,r,e){var n=e(5656),o=e(1400),i=e(6244),a=function(t){return function(r,e,a){var c,u=n(r),s=i(u),f=o(a,s);if(t&&e!=e){for(;s>f;)if((c=u[f++])!=c)return!0}else for(;s>f;f++)if((t||f in u)&&u[f]===e)return t||f||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},2092:function(t,r,e){var n=e(9974),o=e(1702),i=e(8361),a=e(7908),c=e(6244),u=e(5417),s=o([].push),f=function(t){var r=1==t,e=2==t,o=3==t,f=4==t,l=6==t,p=7==t,h=5==t||l;return function(v,d,y,m){for(var g,b,x=a(v),w=i(x),_=n(d,y),S=c(w),j=0,L=m||u,E=r?L(v,S):e||p?L(v,0):void 0;S>j;j++)if((h||j in w)&&(b=_(g=w[j],j,x),t))if(r)E[j]=b;else if(b)switch(t){case 3:return!0;case 5:return g;case 6:return j;case 2:s(E,g)}else switch(t){case 4:return!1;case 7:s(E,g)}return l?-1:o||f?f:E}};t.exports={forEach:f(0),map:f(1),filter:f(2),some:f(3),every:f(4),find:f(5),findIndex:f(6),filterReject:f(7)}},1194:function(t,r,e){var n=e(7293),o=e(5112),i=e(7392),a=o("species");t.exports=function(t){return i>=51||!n((function(){var r=[];return(r.constructor={})[a]=function(){return{foo:1}},1!==r[t](Boolean).foo}))}},2133:function(t,r,e){"use strict";var n=e(7293);t.exports=function(t,r){var e=[][t];return!!e&&n((function(){e.call(null,r||function(){return 1},1)}))}},1589:function(t,r,e){var n=e(1400),o=e(6244),i=e(6135),a=Array,c=Math.max;t.exports=function(t,r,e){for(var u=o(t),s=n(r,u),f=n(void 0===e?u:e,u),l=a(c(f-s,0)),p=0;s0;)t[n]=t[--n];n!==i++&&(t[n]=e)}return t},c=function(t,r,e,n){for(var o=r.length,i=e.length,a=0,c=0;a9007199254740991)throw r("Maximum allowed index exceeded");return t}},8324:function(t){t.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},8509:function(t,r,e){var n=e(317)("span").classList,o=n&&n.constructor&&n.constructor.prototype;t.exports=o===Object.prototype?void 0:o},8886:function(t,r,e){var n=e(8113).match(/firefox\/(\d+)/i);t.exports=!!n&&+n[1]},7871:function(t){t.exports="object"==typeof window&&"object"!=typeof Deno},256:function(t,r,e){var n=e(8113);t.exports=/MSIE|Trident/.test(n)},1528:function(t,r,e){var n=e(8113),o=e(7854);t.exports=/ipad|iphone|ipod/i.test(n)&&void 0!==o.Pebble},6833:function(t,r,e){var n=e(8113);t.exports=/(?:ipad|iphone|ipod).*applewebkit/i.test(n)},5268:function(t,r,e){var n=e(4326),o=e(7854);t.exports="process"==n(o.process)},1036:function(t,r,e){var n=e(8113);t.exports=/web0s(?!.*chrome)/i.test(n)},8113:function(t,r,e){var n=e(5005);t.exports=n("navigator","userAgent")||""},7392:function(t,r,e){var n,o,i=e(7854),a=e(8113),c=i.process,u=i.Deno,s=c&&c.versions||u&&u.version,f=s&&s.v8;f&&(o=(n=f.split("."))[0]>0&&n[0]<4?1:+(n[0]+n[1])),!o&&a&&(!(n=a.match(/Edge\/(\d+)/))||n[1]>=74)&&(n=a.match(/Chrome\/(\d+)/))&&(o=+n[1]),t.exports=o},8008:function(t,r,e){var n=e(8113).match(/AppleWebKit\/(\d+)\./);t.exports=!!n&&+n[1]},748:function(t){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(t,r,e){var n=e(7854),o=e(1236).f,i=e(8880),a=e(8052),c=e(3072),u=e(9920),s=e(4705);t.exports=function(t,r){var e,f,l,p,h,v=t.target,d=t.global,y=t.stat;if(e=d?n:y?n[v]||c(v,{}):(n[v]||{}).prototype)for(f in r){if(p=r[f],l=t.dontCallGetSet?(h=o(e,f))&&h.value:e[f],!s(d?f:v+(y?".":"#")+f,t.forced)&&void 0!==l){if(typeof p==typeof l)continue;u(p,l)}(t.sham||l&&l.sham)&&i(p,"sham",!0),a(e,f,p,t)}}},7293:function(t){t.exports=function(t){try{return!!t()}catch(t){return!0}}},2104:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.apply,a=o.call;t.exports="object"==typeof Reflect&&Reflect.apply||(n?a.bind(i):function(){return a.apply(i,arguments)})},9974:function(t,r,e){var n=e(1702),o=e(9662),i=e(4374),a=n(n.bind);t.exports=function(t,r){return o(t),void 0===r?t:i?a(t,r):function(){return t.apply(r,arguments)}}},4374:function(t,r,e){var n=e(7293);t.exports=!n((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))},6916:function(t,r,e){var n=e(4374),o=Function.prototype.call;t.exports=n?o.bind(o):function(){return o.apply(o,arguments)}},6530:function(t,r,e){var n=e(9781),o=e(2597),i=Function.prototype,a=n&&Object.getOwnPropertyDescriptor,c=o(i,"name"),u=c&&"something"===function(){}.name,s=c&&(!n||n&&a(i,"name").configurable);t.exports={EXISTS:c,PROPER:u,CONFIGURABLE:s}},1702:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.bind,a=o.call,c=n&&i.bind(a,a);t.exports=n?function(t){return t&&c(t)}:function(t){return t&&function(){return a.apply(t,arguments)}}},5005:function(t,r,e){var n=e(7854),o=e(614),i=function(t){return o(t)?t:void 0};t.exports=function(t,r){return arguments.length<2?i(n[t]):n[t]&&n[t][r]}},1246:function(t,r,e){var n=e(648),o=e(8173),i=e(7497),a=e(5112)("iterator");t.exports=function(t){if(null!=t)return o(t,a)||o(t,"@@iterator")||i[n(t)]}},8554:function(t,r,e){var n=e(6916),o=e(9662),i=e(9670),a=e(6330),c=e(1246),u=TypeError;t.exports=function(t,r){var e=arguments.length<2?c(t):r;if(o(e))return i(n(e,t));throw u(a(t)+" is not iterable")}},8173:function(t,r,e){var n=e(9662);t.exports=function(t,r){var e=t[r];return null==e?void 0:n(e)}},7854:function(t,r,e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e.g&&e.g)||function(){return this}()||Function("return this")()},2597:function(t,r,e){var n=e(1702),o=e(7908),i=n({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,r){return i(o(t),r)}},3501:function(t){t.exports={}},842:function(t,r,e){var n=e(7854);t.exports=function(t,r){var e=n.console;e&&e.error&&(1==arguments.length?e.error(t):e.error(t,r))}},490:function(t,r,e){var n=e(5005);t.exports=n("document","documentElement")},4664:function(t,r,e){var n=e(9781),o=e(7293),i=e(317);t.exports=!n&&!o((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},8361:function(t,r,e){var n=e(1702),o=e(7293),i=e(4326),a=Object,c=n("".split);t.exports=o((function(){return!a("z").propertyIsEnumerable(0)}))?function(t){return"String"==i(t)?c(t,""):a(t)}:a},2788:function(t,r,e){var n=e(1702),o=e(614),i=e(5465),a=n(Function.toString);o(i.inspectSource)||(i.inspectSource=function(t){return a(t)}),t.exports=i.inspectSource},9909:function(t,r,e){var n,o,i,a=e(8536),c=e(7854),u=e(1702),s=e(111),f=e(8880),l=e(2597),p=e(5465),h=e(6200),v=e(3501),d="Object already initialized",y=c.TypeError,m=c.WeakMap;if(a||p.state){var g=p.state||(p.state=new m),b=u(g.get),x=u(g.has),w=u(g.set);n=function(t,r){if(x(g,t))throw new y(d);return r.facade=t,w(g,t,r),r},o=function(t){return b(g,t)||{}},i=function(t){return x(g,t)}}else{var _=h("state");v[_]=!0,n=function(t,r){if(l(t,_))throw new y(d);return r.facade=t,f(t,_,r),r},o=function(t){return l(t,_)?t[_]:{}},i=function(t){return l(t,_)}}t.exports={set:n,get:o,has:i,enforce:function(t){return i(t)?o(t):n(t,{})},getterFor:function(t){return function(r){var e;if(!s(r)||(e=o(r)).type!==t)throw y("Incompatible receiver, "+t+" required");return e}}}},7659:function(t,r,e){var n=e(5112),o=e(7497),i=n("iterator"),a=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||a[i]===t)}},3157:function(t,r,e){var n=e(4326);t.exports=Array.isArray||function(t){return"Array"==n(t)}},614:function(t){t.exports=function(t){return"function"==typeof t}},4411:function(t,r,e){var n=e(1702),o=e(7293),i=e(614),a=e(648),c=e(5005),u=e(2788),s=function(){},f=[],l=c("Reflect","construct"),p=/^\s*(?:class|function)\b/,h=n(p.exec),v=!p.exec(s),d=function(t){if(!i(t))return!1;try{return l(s,f,t),!0}catch(t){return!1}},y=function(t){if(!i(t))return!1;switch(a(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return v||!!h(p,u(t))}catch(t){return!0}};y.sham=!0,t.exports=!l||o((function(){var t;return d(d.call)||!d(Object)||!d((function(){t=!0}))||t}))?y:d},4705:function(t,r,e){var n=e(7293),o=e(614),i=/#|\.prototype\./,a=function(t,r){var e=u[c(t)];return e==f||e!=s&&(o(r)?n(r):!!r)},c=a.normalize=function(t){return String(t).replace(i,".").toLowerCase()},u=a.data={},s=a.NATIVE="N",f=a.POLYFILL="P";t.exports=a},111:function(t,r,e){var n=e(614);t.exports=function(t){return"object"==typeof t?null!==t:n(t)}},1913:function(t){t.exports=!1},7850:function(t,r,e){var n=e(111),o=e(4326),i=e(5112)("match");t.exports=function(t){var r;return n(t)&&(void 0!==(r=t[i])?!!r:"RegExp"==o(t))}},2190:function(t,r,e){var n=e(5005),o=e(614),i=e(7976),a=e(3307),c=Object;t.exports=a?function(t){return"symbol"==typeof t}:function(t){var r=n("Symbol");return o(r)&&i(r.prototype,c(t))}},408:function(t,r,e){var n=e(9974),o=e(6916),i=e(9670),a=e(6330),c=e(7659),u=e(6244),s=e(7976),f=e(8554),l=e(1246),p=e(9212),h=TypeError,v=function(t,r){this.stopped=t,this.result=r},d=v.prototype;t.exports=function(t,r,e){var y,m,g,b,x,w,_,S=e&&e.that,j=!(!e||!e.AS_ENTRIES),L=!(!e||!e.IS_ITERATOR),E=!(!e||!e.INTERRUPTED),O=n(r,S),k=function(t){return y&&p(y,"normal",t),new v(!0,t)},P=function(t){return j?(i(t),E?O(t[0],t[1],k):O(t[0],t[1])):E?O(t,k):O(t)};if(L)y=t;else{if(!(m=l(t)))throw h(a(t)+" is not iterable");if(c(m)){for(g=0,b=u(t);b>g;g++)if((x=P(t[g]))&&s(d,x))return x;return new v(!1)}y=f(t,m)}for(w=y.next;!(_=o(w,y)).done;){try{x=P(_.value)}catch(t){p(y,"throw",t)}if("object"==typeof x&&x&&s(d,x))return x}return new v(!1)}},9212:function(t,r,e){var n=e(6916),o=e(9670),i=e(8173);t.exports=function(t,r,e){var a,c;o(t);try{if(!(a=i(t,"return"))){if("throw"===r)throw e;return e}a=n(a,t)}catch(t){c=!0,a=t}if("throw"===r)throw e;if(c)throw a;return o(a),e}},3383:function(t,r,e){"use strict";var n,o,i,a=e(7293),c=e(614),u=e(30),s=e(9518),f=e(8052),l=e(5112),p=e(1913),h=l("iterator"),v=!1;[].keys&&("next"in(i=[].keys())?(o=s(s(i)))!==Object.prototype&&(n=o):v=!0),null==n||a((function(){var t={};return n[h].call(t)!==t}))?n={}:p&&(n=u(n)),c(n[h])||f(n,h,(function(){return this})),t.exports={IteratorPrototype:n,BUGGY_SAFARI_ITERATORS:v}},7497:function(t){t.exports={}},6244:function(t,r,e){var n=e(7466);t.exports=function(t){return n(t.length)}},6339:function(t,r,e){var n=e(7293),o=e(614),i=e(2597),a=e(9781),c=e(6530).CONFIGURABLE,u=e(2788),s=e(9909),f=s.enforce,l=s.get,p=Object.defineProperty,h=a&&!n((function(){return 8!==p((function(){}),"length",{value:8}).length})),v=String(String).split("String"),d=t.exports=function(t,r,e){"Symbol("===String(r).slice(0,7)&&(r="["+String(r).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),e&&e.getter&&(r="get "+r),e&&e.setter&&(r="set "+r),(!i(t,"name")||c&&t.name!==r)&&p(t,"name",{value:r,configurable:!0}),h&&e&&i(e,"arity")&&t.length!==e.arity&&p(t,"length",{value:e.arity});try{e&&i(e,"constructor")&&e.constructor?a&&p(t,"prototype",{writable:!1}):t.prototype&&(t.prototype=void 0)}catch(t){}var n=f(t);return i(n,"source")||(n.source=v.join("string"==typeof r?r:"")),t};Function.prototype.toString=d((function(){return o(this)&&l(this).source||u(this)}),"toString")},4758:function(t){var r=Math.ceil,e=Math.floor;t.exports=Math.trunc||function(t){var n=+t;return(n>0?e:r)(n)}},5948:function(t,r,e){var n,o,i,a,c,u,s,f,l=e(7854),p=e(9974),h=e(1236).f,v=e(261).set,d=e(6833),y=e(1528),m=e(1036),g=e(5268),b=l.MutationObserver||l.WebKitMutationObserver,x=l.document,w=l.process,_=l.Promise,S=h(l,"queueMicrotask"),j=S&&S.value;j||(n=function(){var t,r;for(g&&(t=w.domain)&&t.exit();o;){r=o.fn,o=o.next;try{r()}catch(t){throw o?a():i=void 0,t}}i=void 0,t&&t.enter()},d||g||m||!b||!x?!y&&_&&_.resolve?((s=_.resolve(void 0)).constructor=_,f=p(s.then,s),a=function(){f(n)}):g?a=function(){w.nextTick(n)}:(v=p(v,l),a=function(){v(n)}):(c=!0,u=x.createTextNode(""),new b(n).observe(u,{characterData:!0}),a=function(){u.data=c=!c})),t.exports=j||function(t){var r={fn:t,next:void 0};i&&(i.next=r),o||(o=r,a()),i=r}},735:function(t,r,e){var n=e(133);t.exports=n&&!!Symbol.for&&!!Symbol.keyFor},133:function(t,r,e){var n=e(7392),o=e(7293);t.exports=!!Object.getOwnPropertySymbols&&!o((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&n&&n<41}))},8536:function(t,r,e){var n=e(7854),o=e(614),i=e(2788),a=n.WeakMap;t.exports=o(a)&&/native code/.test(i(a))},8523:function(t,r,e){"use strict";var n=e(9662),o=function(t){var r,e;this.promise=new t((function(t,n){if(void 0!==r||void 0!==e)throw TypeError("Bad Promise constructor");r=t,e=n})),this.resolve=n(r),this.reject=n(e)};t.exports.f=function(t){return new o(t)}},3929:function(t,r,e){var n=e(7850),o=TypeError;t.exports=function(t){if(n(t))throw o("The method doesn't accept regular expressions");return t}},30:function(t,r,e){var n,o=e(9670),i=e(6048),a=e(748),c=e(3501),u=e(490),s=e(317),f=e(6200)("IE_PROTO"),l=function(){},p=function(t){return" + + rmtDev -- Find your remote developer job + + + + +
    + Background pattern +
    + +
    +
    + + +
      + +
    +
    +
    + +
    +
    +

    Find your remote developer job

    + +
    + +
    +
    + + +
    +

    + 0 results +

    + +
    + + + +
    +
    + + + +
    + + +
    +
    + +
    +
    + +
    + +
    +

    What are you looking for?

    +

    Start by searching for any technology + your ideal job is working with

    +
    + +
    +
    +
    +
    + +
    + + + + +

    109573 total jobs available

    +
    + +
    + +
    +

    Something went wrong

    +

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    +
    +
    + + + + \ No newline at end of file diff --git a/Supplemental/video 36/src/index.js b/Supplemental/video 36/src/index.js new file mode 100644 index 0000000..c7c6751 --- /dev/null +++ b/Supplemental/video 36/src/index.js @@ -0,0 +1,12 @@ +import './components/Bookmarks.js'; +import './components/Error.js'; +import './components/JobDetails.js'; +import './components/JobList.js'; +import './components/Pagination.js'; +import './components/Router.js'; +import './components/Search.js'; +import './components/Sorting.js'; +import './components/Spinner.js'; +import './components/Storage.js'; + +import './index.css'; \ No newline at end of file diff --git a/Supplemental/video 36/webpack.config.js b/Supplemental/video 36/webpack.config.js new file mode 100644 index 0000000..b970c2c --- /dev/null +++ b/Supplemental/video 36/webpack.config.js @@ -0,0 +1,27 @@ +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); + +module.exports = { + mode: 'production', + entry: './src/index.js', + output: { + filename: 'main.js' + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: 'main.css' + }) + ], + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: ['babel-loader'] + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] + } + ] + } +}; \ No newline at end of file diff --git a/Supplemental/video 37/index.html b/Supplemental/video 37/index.html new file mode 100644 index 0000000..b4a3c9b --- /dev/null +++ b/Supplemental/video 37/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + CorpComment -- Give Feedback, Publicly + + + + +
    +
    + + + + +
    + +
    +
    + pattern + +

    Give Feedback. Publicly.

    +
    + + +
    +

    150

    + +
    +
    +
    + +
      +
      +
    +
    + +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + + + + + \ No newline at end of file diff --git a/Supplemental/video 37/script.js b/Supplemental/video 37/script.js new file mode 100644 index 0000000..809fa3e --- /dev/null +++ b/Supplemental/video 37/script.js @@ -0,0 +1,215 @@ +// -- GLOBAL -- +const MAX_CHARS = 150; +const BASE_API_URL = 'https://bytegrad.com/course-assets/js/1/api'; + +const textareaEl = document.querySelector('.form__textarea'); +const counterEl = document.querySelector('.counter'); +const formEl = document.querySelector('.form'); +const feedbackListEl = document.querySelector('.feedbacks'); +const submitBtnEl = document.querySelector('.submit-btn'); +const spinnerEl = document.querySelector('.spinner'); +const hashtagListEl = document.querySelector('.hashtags'); + +const renderFeedbackItem = feedbackItem => { + // new feedback item HTML + const feedbackItemHTML = ` + + `; + + // insert new feedback item in list + feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); +}; + + +// -- COUNTER COMPONENT -- +(() => { + const inputHandler = () => { + // determine maximum number of characters + const maxNrChars = MAX_CHARS; + + // determine number of characters currently typed + const nrCharsTyped = textareaEl.value.length; + + // calculate number of characters left (maximum minus currently typed) + const charsLeft = maxNrChars - nrCharsTyped; + + // show number of characters left + counterEl.textContent = charsLeft; + }; + + textareaEl.addEventListener('input', inputHandler); +})(); + + +// -- FORM COMPONENT -- +(() => { + const showVisualIndicator = textCheck => { + const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; + + // show valid indicator + formEl.classList.add(className); + + // remove visual indicator + setTimeout(() => { + formEl.classList.remove(className); + }, 2000); + }; + + const submitHandler = event => { + // prevent default browser action (submitting form data to 'action'-address and refreshing page) + event.preventDefault(); + + // get text from textarea + const text = textareaEl.value; + + // validate text (e.g. check if #hashtag is present and text is long enough) + if (text.includes('#') && text.length >= 5) { + showVisualIndicator('valid'); + } else { + showVisualIndicator('invalid'); + + // focus textarea + textareaEl.focus(); + + // stop this function execution + return; + } + + // we have text, now extract other info from text + const hashtag = text.split(' ').find(word => word.includes('#')); + const company = hashtag.substring(1); + const badgeLetter = company.substring(0, 1).toUpperCase(); + const upvoteCount = 0; + const daysAgo = 0; + + // render feedback item in list + const feedbackItem = { + upvoteCount: upvoteCount, + company: company, + badgeLetter: badgeLetter, + daysAgo: daysAgo, + text: text + }; + renderFeedbackItem(feedbackItem); + + // send feedback item to server + fetch(`${BASE_API_URL}/feedbacks`, { + method: 'POST', + body: JSON.stringify(feedbackItem), + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + }).then(response => { + if (!response.ok) { + console.log('Something went wrong'); + return; + } + + console.log('Successfully submitted'); + }).catch(error => console.log(error)); + + // clear textarea + textareaEl.value = ''; + + // blur submit button + submitBtnEl.blur(); + + // reset counter + counterEl.textContent = MAX_CHARS; + }; + + formEl.addEventListener('submit', submitHandler); +})(); + + +// -- FEEDBACK LIST COMPONENT -- +(() => { + const clickHandler = event => { + // get clicked HTML-element + const clickedEl = event.target; + + // determine if user intended to upvote or expand + const upvoteIntention = clickedEl.className.includes('upvote'); + + // run the appropriate logic + if (upvoteIntention) { + // get the closest upvote button + const upvoteBtnEl = clickedEl.closest('.upvote'); + + // disable upvote button (prevent double-clicks, spam) + upvoteBtnEl.disabled = true; + + // select the upvote count element within the upvote button + const upvoteCountEl = upvoteBtnEl.querySelector('.upvote__count'); + + // get currently displayed upvote count as number (+) + let upvoteCount = +upvoteCountEl.textContent; + + // set upvote count incremented by 1 + upvoteCountEl.textContent = ++upvoteCount; + } else { + // expand the clicked feedback item + clickedEl.closest('.feedback').classList.toggle('feedback--expand'); + } + }; + + feedbackListEl.addEventListener('click', clickHandler); + + fetch(`${BASE_API_URL}/feedbacks`) + .then(response => response.json()) + .then(data => { + // remove spinner + spinnerEl.remove(); + + // iterate over each element in feedbacks array and render it in list + data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); + }) + .catch(error => { + feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; + }); +})(); + + +// -- HASHTAG LIST COMPONENT -- +(() => { + const clickHandler = event => { + // get the clicked element + const clickedEl = event.target; + + // stop function if click happened in list, but outside buttons + if (clickedEl.className === 'hashtags') return; + + // extract company name + const companyNameFromHashtag = clickedEl.textContent.substring(1).toLowerCase().trim(); + + // iterate over each feedback item in the list + feedbackListEl.childNodes.forEach(childNode => { + // stop this iteration if it's a text node + if (childNode.nodeType === 3) return; + + // extract company name + const companyNameFromFeedbackItem = childNode.querySelector('.feedback__company').textContent.toLowerCase().trim(); + + // remove feedback item from list if company names are not equal + if (companyNameFromHashtag !== companyNameFromFeedbackItem) { + childNode.remove(); + } + }); + }; + + hashtagListEl.addEventListener('click', clickHandler); +})(); diff --git a/Supplemental/video 37/style.css b/Supplemental/video 37/style.css new file mode 100644 index 0000000..fd4b37f --- /dev/null +++ b/Supplemental/video 37/style.css @@ -0,0 +1,671 @@ +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +textarea { + font: inherit; + border: initial; + resize: none; + outline: initial; /* create alternative for focus state */ +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +/* UTILITIES */ +.u-bold { + font-weight: 600; +} + +.u-medium { + font-weight: 500; +} + +.u-italic { + font-style: italic; +} + +.u-transparent { + color: rgba(255, 255, 255, 0.8); +} + +/* KEYFRAMES */ +@keyframes intro { + 0% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } +} + +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +/* BASE */ +.body { + background-image: linear-gradient(200deg, #654a86, #534292); + min-height: 100vh; + font-family: 'Inter', sans-serif; + display: flex; + justify-content: center; + align-items: center; + + scrollbar-width: none; /* Firefox */ +} + +.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +.body__container { + position: relative; + display: flex; + justify-content: center; + height: 850px; +} + +/* HEADINGS */ +.first-heading { + font-weight: 700; + font-size: 36px; + position: relative; + z-index: 1; + color: rgba(255, 255, 255, 0.93); +} + +.first-heading::selection, +.first-heading *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* APP */ +.app { + width: 715px; + height: 100%; + border-radius: 6px; + overflow: hidden; + animation: intro 0.4s; +} + +/* HEADER */ +.header { + height: 277px; + background-color: #121618; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + z-index: 999; + backface-visibility: hidden; + transform: translateZ(0); + box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; + padding-bottom: 3px; +} + +.header__pattern { + position: absolute; + top: 0; + z-index: 0; +} + +.header__pattern::selection { + background-color: initial; +} + +/* LOGO */ +.logo { + height: 27.5px; + +} + +.logo::selection { + background-color: initial; +} + +.logo__img { + position: relative; + z-index: 1; + backface-visibility: hidden; + transform: translateZ(0); + +} + +/* FORM */ +.form { + margin-top: 20px; + height: 123px; + width: 470px; + background-color: rgba(255, 255, 255, 0.04); + border-radius: 6px; + overflow: hidden; + position: relative; + transition: background-color 0.2s, box-shadow 0.2s; +} + +.form:hover, +.form:focus { + background-color: rgba(255, 255, 255, 0.055); +} + +.form:focus-within { + outline: 2px solid rgba(255, 255, 255, 0.125); +} + +.form--invalid { + box-shadow: 0 0 0 2px #8a3d2c; +} + +.form--valid { + box-shadow: 0 0 0 2px #2c8a5e; +} + +.form__textarea { + width: 100%; + height: 85px; + background-color: transparent; + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + padding: 15px 20px; + caret-color: rgba(255, 255, 255, 0.25); +} + +.form__textarea::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__textarea::placeholder { + opacity: 0; +} + +.form__textarea:focus + .form__label, +.form__textarea:not(:placeholder-shown) + .form__label { + opacity: 0; +} + +.form__label { + position: absolute; + top: 13.5px; + left: 20px; + color: #fff; + opacity: 0.42; + pointer-events: none; +} + +.form__label::selection, +.form__label *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +.form__icon { + font-size: 12px; + margin-left: 4px; + color: #fff; + opacity: 0.5; +} + +.form__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + margin-top: -8px; +} + +/* COUNTER */ +.counter { + font-weight: 500; + font-size: 13px; + color: rgba(255, 255, 255, 0.25); + pointer-events: none; +} + +.counter::selection, +.counter > *::selection { + color: rgba(255, 255, 255, 0.85); + background-color: rgba(255, 255, 255, 0.05); +} + +/* SUBMIT BTN */ +.submit-btn { + color: #161921; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + padding: 4px 16px 9px; + border-radius: 500px; + font-weight: 600; + background-color: #fff; + cursor: pointer; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(-2px) translateZ(0) rotate(0); +} + +.submit-btn:hover, +.submit-btn:focus { + transform: translateY(-2px) translateZ(0) scale(1.15); +} + +.submit-btn:active { + transform: translateY(-2px) translateZ(0) scale(1.07); +} + +.submit-btn:hover .submit-btn__text { + transform: translateY(2px) translateZ(0); +} + +.submit-btn__text { + display: block; + transition: all 0.2s; + backface-visibility: hidden; + transform: translateY(2px) translateZ(0) rotate(0); +} + +.submit-btn__text::selection { + color: rgba(0, 0, 0, 0.85); + background-color: rgba(0, 0, 0, 0.1); +} + +/* FEEDBACKS */ +.feedbacks { + height: 573px; + overflow-y: scroll; + overflow-x: hidden; + /* background-color: #F3F6F8; */ + background-color: #f7f8f9; + + scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 7px; +} + +.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #dbdfe4; +} + +.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #979ca0; + transition: all 0.2s; +} + +.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #787c80; +} + +/* FEEDBACK ITEM */ +.feedback { + display: grid; + grid-template-columns: 40px 85px 6fr 1fr; + align-items: center; + height: 82px; + padding-right: 35px; + padding-left: 30px; + cursor: pointer; + border-bottom: 1px solid #e4e7eb; + transition: all 0.2s; + color: #3a3c42; +} + +.feedback--expand { + height: 100px; + background-color: #fff; +} + +.feedback--expand .feedback__text { + -webkit-line-clamp: initial !important; + -webkit-box-orient: initial !important; + overflow: initial !important; +} + +.feedback *::selection { + background-color: rgba(0, 0, 0, 0.1); +} + +.feedback:hover { + background-color: #fff; +} + +.feedback:hover .upvote, +.feedback:hover .feedback__badge, +.feedback:hover .feedback__content, +.feedback:hover .feedback__date { + transform: translateX(5px); +} + +.feedback__badge { + height: 49px; + width: 49px; + border-radius: 6px; + background-color: #564989; + display: flex; + justify-content: center; + align-items: center; + margin-right: 16px; + transition: all 0.2s; + margin-left: 20px; +} + +.feedback:nth-child(6n+2) .feedback__badge { + background-color: #6D4989; +} + +.feedback:nth-child(6n+3) .feedback__badge { + background-color: #3c7789; +} + +.feedback:nth-child(6n+4) .feedback__badge { + background-color: #897749; +} + +.feedback:nth-child(6n+5) .feedback__badge { + background-color: #4a8b6b; +} + +.feedback:nth-child(6n+6) .feedback__badge { + background-color: #495789; +} + +.feedback__letter { + font-size: 24px; + color: #fff; + font-weight: 700; + margin-right: -2px; +} + +.feedback__content { + transition: all 0.2s; +} + +.feedback__company { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 500; + color: #898D96; + margin-top: -1px; + display: block; + transition: all 0.2s; +} + +.feedback__text { + color: #141518; + font-size: 13px; + margin-top: 1px; + transition: all 0.2s; + + display: -webkit-box; + max-width: 100%; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.feedback__date { + font-size: 12px; + color: #898b92; + margin-left: auto; + transition: all 0.2s; +} + +/* UPVOTE BTN */ +.upvote { + cursor: pointer; + height: 40px; + width: 40px; + border-radius: 6px; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote:hover { + background-color: #F3F6F8; +} + +.upvote:hover .upvote__icon, +.upvote:hover .upvote__count { + color: #784a86; +} + +.upvote:disabled .upvote__icon { + display: none; +} + +.upvote:disabled { + pointer-events: none; +} + +.upvote__icon { + color: #D7DBE2; + font-size: 19px; + display: block; + transition: all 0.2s; +} + +.upvote__count { + color: #6C6F76; + font-size: 11px; + margin-top: -1px; +} + +/* HASHTAGS */ +.hashtags { + position: absolute; + right: -137px; + top: 0px; +} + +.hashtags__item { + max-width: max-content; + margin-bottom: 11px; +} + +/* HASHTAG */ +.hashtag { + align-self: flex-start; + font-size: 13px; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + padding: 7px 14px 9px; + background-color: rgba(255, 255, 255, 0.06); + border-radius: 500px; + cursor: pointer; + transition: all 0.2s; +} + +.hashtag:hover, +.hashtag:focus { + color: #fff; + transform: scale(1.11); +} + +.hashtag:active { + transform: scale(1.06); +} + +.hashtag::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +/* FOOTER */ +.footer { + position: absolute; + transform: rotate(-90deg); + left: -225px; + bottom: 174px; +} + +/* COPYRIGHT */ +.copyright { + color: #A6ADB5; + font-size: 11px; +} + +.copyright *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.copyright__text { + opacity: 0.4; +} + +.copyright__link { + text-decoration: underline; +} + +.copyright__icon { + font-size: 10px; + opacity: 0.5; + margin-right: 4px; + margin-left: 2px; +} + +/* SPINNER */ +.spinner { + position: absolute; + left: 50%; + top: 46%; + transform: translateX(-50%) translateZ(0); + border-radius: 50%; + width: 100px; + height: 100px; + border-top: 7px solid #e2e7e9; + border-right: 7px solid #e2e7e9; + border-bottom: 7px solid #e2e7e9; + border-left: 7px solid #ccd1d3; + animation: spinner 1s infinite linear; +} + + +/* MEDIA QUERIES */ +@media (max-height: 925px) { + .body { + padding: 60px 0; + } +} + +@media (max-width: 1050px) { + .body__container { + flex-direction: column-reverse; + height: initial; + } + + .hashtags { + display: none; + } + + .footer { + transform: initial; + position: relative; + left: initial; + bottom: initial; + text-align: center; + margin-top: 20px; + } +} + +@media (max-width: 775px) { + .body { + padding-top: 0; + padding-bottom: 20px; + align-items: initial; + } + + .body__container { + width: 100vw; + } + + .app { + width: 100%; + border-radius: 0; + } + + .feedback__badge { + min-width: 49px; + } + + .feedback__content { + padding-right: 25px; + } + + .feedback__date { + margin-left: auto; + } +} + +@media (max-width: 525px) { + .header { + padding: 35px 15px; + height: initial; + } + + .first-heading { + text-align: center; + } + + .form { + width: initial; + align-self: stretch; + } + + .form__label { + padding-right: 20px; + } + + .feedback { + grid-template-columns: 40px 85px 1fr; + padding-right: 15px; + padding-left: 15px; + } + + .feedback--expand { + height: initial; + padding-top: 10px; + padding-bottom: 10px; + } + + .feedback__date { + display: none; + } + + .footer { + padding: 0 15px; + } +} \ No newline at end of file From f3ee4a5a9f68a755214fbc32cfd38d38faa2a179 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Fri, 10 Jun 2022 12:41:49 +0200 Subject: [PATCH 08/40] Change folder names for order --- {Modern JS Fundamentals => 01-Modern JS Fundamentals}/index.html | 0 {Modern JS Fundamentals => 01-Modern JS Fundamentals}/script.js | 0 {Modern JS Fundamentals => 01-Modern JS Fundamentals}/style.css | 0 {CorpComment => 02-CorpComment}/video 04/index.html | 0 {CorpComment => 02-CorpComment}/video 04/script.js | 0 {CorpComment => 02-CorpComment}/video 04/style.css | 0 {CorpComment => 02-CorpComment}/video 05/index.html | 0 {CorpComment => 02-CorpComment}/video 05/script.js | 0 {CorpComment => 02-CorpComment}/video 05/style.css | 0 {CorpComment => 02-CorpComment}/video 06/index.html | 0 {CorpComment => 02-CorpComment}/video 06/script.js | 0 {CorpComment => 02-CorpComment}/video 06/style.css | 0 {CorpComment => 02-CorpComment}/video 07/index.html | 0 {CorpComment => 02-CorpComment}/video 07/script.js | 0 {CorpComment => 02-CorpComment}/video 07/style.css | 0 {CorpComment => 02-CorpComment}/video 08/index.html | 0 {CorpComment => 02-CorpComment}/video 08/script.js | 0 {CorpComment => 02-CorpComment}/video 08/style.css | 0 {CorpComment => 02-CorpComment}/video 09/index.html | 0 {CorpComment => 02-CorpComment}/video 09/script.js | 0 {CorpComment => 02-CorpComment}/video 09/style.css | 0 {CorpComment => 02-CorpComment}/video 10/index.html | 0 {CorpComment => 02-CorpComment}/video 10/script.js | 0 {CorpComment => 02-CorpComment}/video 10/style.css | 0 {CorpComment => 02-CorpComment}/video 11/index.html | 0 {CorpComment => 02-CorpComment}/video 11/script.js | 0 {CorpComment => 02-CorpComment}/video 11/style.css | 0 {CorpComment => 02-CorpComment}/video 12/index.html | 0 {CorpComment => 02-CorpComment}/video 12/script.js | 0 {CorpComment => 02-CorpComment}/video 12/style.css | 0 {CorpComment => 02-CorpComment}/video 13/index.html | 0 {CorpComment => 02-CorpComment}/video 13/script.js | 0 {CorpComment => 02-CorpComment}/video 13/style.css | 0 {CorpComment => 02-CorpComment}/video 14/index.html | 0 {CorpComment => 02-CorpComment}/video 14/script.js | 0 {CorpComment => 02-CorpComment}/video 14/style.css | 0 {Supplemental/video 36/src => 03-rmtDev/video 16}/index.css | 0 {rmtDev => 03-rmtDev}/video 16/index.html | 0 {rmtDev => 03-rmtDev}/video 16/index.js | 0 {Supplemental/video 38/src => 03-rmtDev/video 17}/index.css | 0 {rmtDev => 03-rmtDev}/video 17/index.html | 0 {rmtDev => 03-rmtDev}/video 17/index.js | 0 {rmtDev/video 16 => 03-rmtDev/video 18}/index.css | 0 {rmtDev => 03-rmtDev}/video 18/index.html | 0 {rmtDev => 03-rmtDev}/video 18/index.js | 0 {rmtDev/video 17 => 03-rmtDev/video 19}/index.css | 0 {Supplemental/video 36/src => 03-rmtDev/video 19}/index.html | 0 {rmtDev => 03-rmtDev}/video 19/index.js | 0 {rmtDev => 03-rmtDev}/video 19/src/common.js | 0 {rmtDev => 03-rmtDev}/video 19/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 19/src/components/JobList.js | 0 {rmtDev => 03-rmtDev}/video 19/src/components/Search.js | 0 {rmtDev => 03-rmtDev}/video 19/src/components/Spinner.js | 0 {rmtDev/video 18 => 03-rmtDev/video 20}/index.css | 0 {Supplemental/video 38/src => 03-rmtDev/video 20}/index.html | 0 {rmtDev => 03-rmtDev}/video 20/index.js | 0 {rmtDev => 03-rmtDev}/video 20/src/common.js | 0 {rmtDev => 03-rmtDev}/video 20/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 20/src/components/JobList.js | 0 {rmtDev => 03-rmtDev}/video 20/src/components/Search.js | 0 .../video 36 => 03-rmtDev/video 20}/src/components/Spinner.js | 0 {rmtDev/video 19 => 03-rmtDev/video 21}/index.css | 0 {rmtDev/video 19 => 03-rmtDev/video 21}/index.html | 0 {rmtDev => 03-rmtDev}/video 21/index.js | 0 {rmtDev => 03-rmtDev}/video 21/src/common.js | 0 .../video 36 => 03-rmtDev/video 21}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 21/src/components/JobList.js | 0 {rmtDev => 03-rmtDev}/video 21/src/components/Search.js | 0 .../video 38 => 03-rmtDev/video 21}/src/components/Spinner.js | 0 {rmtDev/video 20 => 03-rmtDev/video 22}/index.css | 0 {rmtDev/video 20 => 03-rmtDev/video 22}/index.html | 0 {rmtDev => 03-rmtDev}/video 22/index.js | 0 {rmtDev => 03-rmtDev}/video 22/src/common.js | 0 .../video 38 => 03-rmtDev/video 22}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 22/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 22/src/components/JobList.js | 0 {rmtDev => 03-rmtDev}/video 22/src/components/Search.js | 0 {rmtDev/video 20 => 03-rmtDev/video 22}/src/components/Spinner.js | 0 {rmtDev/video 21 => 03-rmtDev/video 23}/index.css | 0 {rmtDev/video 21 => 03-rmtDev/video 23}/index.html | 0 {rmtDev => 03-rmtDev}/video 23/index.js | 0 {rmtDev => 03-rmtDev}/video 23/src/common.js | 0 {rmtDev/video 21 => 03-rmtDev/video 23}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 23/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 23/src/components/JobList.js | 0 {rmtDev => 03-rmtDev}/video 23/src/components/Search.js | 0 {rmtDev/video 21 => 03-rmtDev/video 23}/src/components/Spinner.js | 0 {rmtDev/video 22 => 03-rmtDev/video 24}/index.css | 0 {rmtDev/video 22 => 03-rmtDev/video 24}/index.html | 0 {rmtDev => 03-rmtDev}/video 24/index.js | 0 {rmtDev => 03-rmtDev}/video 24/src/common.js | 0 {rmtDev/video 22 => 03-rmtDev/video 24}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 24/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 24/src/components/JobList.js | 0 {rmtDev => 03-rmtDev}/video 24/src/components/Search.js | 0 {rmtDev/video 22 => 03-rmtDev/video 24}/src/components/Spinner.js | 0 {rmtDev/video 23 => 03-rmtDev/video 25}/index.css | 0 {rmtDev/video 23 => 03-rmtDev/video 25}/index.html | 0 {rmtDev => 03-rmtDev}/video 25/index.js | 0 {rmtDev => 03-rmtDev}/video 25/src/common.js | 0 {rmtDev/video 23 => 03-rmtDev/video 25}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 25/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 25/src/components/JobList.js | 0 {rmtDev => 03-rmtDev}/video 25/src/components/Search.js | 0 {rmtDev/video 23 => 03-rmtDev/video 25}/src/components/Spinner.js | 0 {rmtDev/video 24 => 03-rmtDev/video 26}/index.css | 0 {rmtDev/video 24 => 03-rmtDev/video 26}/index.html | 0 {rmtDev => 03-rmtDev}/video 26/index.js | 0 {rmtDev => 03-rmtDev}/video 26/src/common.js | 0 {rmtDev/video 24 => 03-rmtDev/video 26}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 26/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 26/src/components/JobList.js | 0 {rmtDev => 03-rmtDev}/video 26/src/components/Search.js | 0 {rmtDev => 03-rmtDev}/video 26/src/components/Sorting.js | 0 {rmtDev/video 24 => 03-rmtDev/video 26}/src/components/Spinner.js | 0 {rmtDev/video 25 => 03-rmtDev/video 27}/index.css | 0 {rmtDev/video 25 => 03-rmtDev/video 27}/index.html | 0 {rmtDev => 03-rmtDev}/video 27/index.js | 0 {rmtDev => 03-rmtDev}/video 27/src/common.js | 0 {rmtDev/video 25 => 03-rmtDev/video 27}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 27/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 27/src/components/JobList.js | 0 {rmtDev => 03-rmtDev}/video 27/src/components/Search.js | 0 {rmtDev => 03-rmtDev}/video 27/src/components/Sorting.js | 0 {rmtDev/video 25 => 03-rmtDev/video 27}/src/components/Spinner.js | 0 {rmtDev/video 26 => 03-rmtDev/video 28}/index.css | 0 {rmtDev/video 26 => 03-rmtDev/video 28}/index.html | 0 {rmtDev => 03-rmtDev}/video 28/index.js | 0 {rmtDev => 03-rmtDev}/video 28/src/common.js | 0 {rmtDev/video 26 => 03-rmtDev/video 28}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 28/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 28/src/components/JobList.js | 0 {rmtDev => 03-rmtDev}/video 28/src/components/Search.js | 0 {rmtDev => 03-rmtDev}/video 28/src/components/Sorting.js | 0 {rmtDev/video 26 => 03-rmtDev/video 28}/src/components/Spinner.js | 0 {rmtDev/video 27 => 03-rmtDev/video 29}/index.css | 0 {rmtDev/video 27 => 03-rmtDev/video 29}/index.html | 0 {rmtDev => 03-rmtDev}/video 29/index.js | 0 {rmtDev => 03-rmtDev}/video 29/src/common.js | 0 {rmtDev/video 27 => 03-rmtDev/video 29}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 29/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 29/src/components/JobList.js | 0 .../video 36 => 03-rmtDev/video 29}/src/components/Pagination.js | 0 .../video 36 => 03-rmtDev/video 29}/src/components/Search.js | 0 .../video 36 => 03-rmtDev/video 29}/src/components/Sorting.js | 0 {rmtDev/video 27 => 03-rmtDev/video 29}/src/components/Spinner.js | 0 {rmtDev/video 28 => 03-rmtDev/video 30}/index.css | 0 {rmtDev/video 28 => 03-rmtDev/video 30}/index.html | 0 {rmtDev => 03-rmtDev}/video 30/index.js | 0 {rmtDev => 03-rmtDev}/video 30/src/common.js | 0 {rmtDev/video 28 => 03-rmtDev/video 30}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 30/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 30/src/components/JobList.js | 0 .../video 38 => 03-rmtDev/video 30}/src/components/Pagination.js | 0 {rmtDev => 03-rmtDev}/video 30/src/components/Router.js | 0 {rmtDev/video 29 => 03-rmtDev/video 30}/src/components/Search.js | 0 .../video 38 => 03-rmtDev/video 30}/src/components/Sorting.js | 0 {rmtDev/video 28 => 03-rmtDev/video 30}/src/components/Spinner.js | 0 {rmtDev/video 29 => 03-rmtDev/video 31}/index.css | 0 {rmtDev/video 29 => 03-rmtDev/video 31}/index.html | 0 {rmtDev => 03-rmtDev}/video 31/index.js | 0 {rmtDev => 03-rmtDev}/video 31/src/common.js | 0 {rmtDev/video 29 => 03-rmtDev/video 31}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 31/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 31/src/components/JobList.js | 0 .../video 29 => 03-rmtDev/video 31}/src/components/Pagination.js | 0 {rmtDev => 03-rmtDev}/video 31/src/components/Router.js | 0 {rmtDev/video 30 => 03-rmtDev/video 31}/src/components/Search.js | 0 {rmtDev/video 29 => 03-rmtDev/video 31}/src/components/Sorting.js | 0 {rmtDev/video 29 => 03-rmtDev/video 31}/src/components/Spinner.js | 0 {rmtDev/video 30 => 03-rmtDev/video 32}/index.css | 0 {rmtDev/video 30 => 03-rmtDev/video 32}/index.html | 0 {rmtDev => 03-rmtDev}/video 32/index.js | 0 {Supplemental/video 36 => 03-rmtDev/video 32}/src/common.js | 0 {rmtDev => 03-rmtDev}/video 32/src/components/Bookmarks.js | 0 {rmtDev/video 30 => 03-rmtDev/video 32}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 32/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 32/src/components/JobList.js | 0 .../video 30 => 03-rmtDev/video 32}/src/components/Pagination.js | 0 {rmtDev => 03-rmtDev}/video 32/src/components/Router.js | 0 {rmtDev/video 31 => 03-rmtDev/video 32}/src/components/Search.js | 0 {rmtDev/video 30 => 03-rmtDev/video 32}/src/components/Sorting.js | 0 {rmtDev/video 30 => 03-rmtDev/video 32}/src/components/Spinner.js | 0 {rmtDev/video 31 => 03-rmtDev/video 33}/index.css | 0 {rmtDev/video 31 => 03-rmtDev/video 33}/index.html | 0 {rmtDev => 03-rmtDev}/video 33/index.js | 0 {rmtDev/video 32 => 03-rmtDev/video 33}/src/common.js | 0 {rmtDev => 03-rmtDev}/video 33/src/components/Bookmarks.js | 0 {rmtDev/video 31 => 03-rmtDev/video 33}/src/components/Error.js | 0 {rmtDev => 03-rmtDev}/video 33/src/components/JobDetails.js | 0 {rmtDev => 03-rmtDev}/video 33/src/components/JobList.js | 0 .../video 31 => 03-rmtDev/video 33}/src/components/Pagination.js | 0 {rmtDev => 03-rmtDev}/video 33/src/components/Router.js | 0 {rmtDev/video 32 => 03-rmtDev/video 33}/src/components/Search.js | 0 {rmtDev/video 31 => 03-rmtDev/video 33}/src/components/Sorting.js | 0 {rmtDev/video 31 => 03-rmtDev/video 33}/src/components/Spinner.js | 0 .../video 36 => 03-rmtDev/video 33}/src/components/Storage.js | 0 {rmtDev/video 32 => 03-rmtDev/video 34}/index.css | 0 {rmtDev/video 32 => 03-rmtDev/video 34}/index.html | 0 {rmtDev => 03-rmtDev}/video 34/index.js | 0 {rmtDev/video 33 => 03-rmtDev/video 34}/src/common.js | 0 .../video 36 => 03-rmtDev/video 34}/src/components/Bookmarks.js | 0 {rmtDev/video 32 => 03-rmtDev/video 34}/src/components/Error.js | 0 .../video 36 => 03-rmtDev/video 34}/src/components/JobDetails.js | 0 .../video 36 => 03-rmtDev/video 34}/src/components/JobList.js | 0 .../video 32 => 03-rmtDev/video 34}/src/components/Pagination.js | 0 .../video 36 => 03-rmtDev/video 34}/src/components/Router.js | 0 {rmtDev/video 33 => 03-rmtDev/video 34}/src/components/Search.js | 0 {rmtDev/video 32 => 03-rmtDev/video 34}/src/components/Sorting.js | 0 {rmtDev/video 32 => 03-rmtDev/video 34}/src/components/Spinner.js | 0 .../video 38 => 03-rmtDev/video 34}/src/components/Storage.js | 0 {Supplemental/video 36 => 03-rmtDev/video 35}/babel.config.js | 0 {Supplemental/video 36 => 03-rmtDev/video 35}/dist/index.html | 0 {Supplemental/video 36 => 03-rmtDev/video 35}/dist/main.css | 0 {Supplemental/video 36 => 03-rmtDev/video 35}/dist/main.js | 0 .../video 36 => 03-rmtDev/video 35}/dist/main.js.LICENSE.txt | 0 {Supplemental/video 36 => 03-rmtDev/video 35}/package-lock.json | 0 {Supplemental/video 36 => 03-rmtDev/video 35}/package.json | 0 {Supplemental/video 36 => 03-rmtDev/video 35}/postcss.config.js | 0 {rmtDev/video 34 => 03-rmtDev/video 35}/src/common.js | 0 .../video 38 => 03-rmtDev/video 35}/src/components/Bookmarks.js | 0 {rmtDev/video 33 => 03-rmtDev/video 35}/src/components/Error.js | 0 .../video 38 => 03-rmtDev/video 35}/src/components/JobDetails.js | 0 {rmtDev/video 34 => 03-rmtDev/video 35}/src/components/JobList.js | 0 .../video 33 => 03-rmtDev/video 35}/src/components/Pagination.js | 0 .../video 38 => 03-rmtDev/video 35}/src/components/Router.js | 0 {rmtDev/video 34 => 03-rmtDev/video 35}/src/components/Search.js | 0 {rmtDev/video 33 => 03-rmtDev/video 35}/src/components/Sorting.js | 0 {rmtDev/video 33 => 03-rmtDev/video 35}/src/components/Spinner.js | 0 {rmtDev/video 33 => 03-rmtDev/video 35}/src/components/Storage.js | 0 {rmtDev/video 33 => 03-rmtDev/video 35/src}/index.css | 0 {rmtDev/video 33 => 03-rmtDev/video 35/src}/index.html | 0 {Supplemental/video 36 => 03-rmtDev/video 35}/src/index.js | 0 {Supplemental/video 36 => 03-rmtDev/video 35}/webpack.config.js | 0 .../video 38 => 04-Supplemental/video 36}/babel.config.js | 0 .../video 38 => 04-Supplemental/video 36}/dist/index.html | 0 {Supplemental/video 38 => 04-Supplemental/video 36}/dist/main.css | 0 {Supplemental/video 38 => 04-Supplemental/video 36}/dist/main.js | 0 .../video 36}/dist/main.js.LICENSE.txt | 0 .../video 38 => 04-Supplemental/video 36}/package-lock.json | 0 {Supplemental/video 38 => 04-Supplemental/video 36}/package.json | 0 .../video 38 => 04-Supplemental/video 36}/postcss.config.js | 0 {rmtDev/video 35 => 04-Supplemental/video 36}/src/common.js | 0 .../video 36}/src/components/Bookmarks.js | 0 .../video 34 => 04-Supplemental/video 36}/src/components/Error.js | 0 .../video 36}/src/components/JobDetails.js | 0 .../video 36}/src/components/JobList.js | 0 .../video 36}/src/components/Pagination.js | 0 .../video 36}/src/components/Router.js | 0 .../video 36}/src/components/Search.js | 0 .../video 36}/src/components/Sorting.js | 0 .../video 36}/src/components/Spinner.js | 0 .../video 36}/src/components/Storage.js | 0 {rmtDev/video 34 => 04-Supplemental/video 36/src}/index.css | 0 {rmtDev/video 34 => 04-Supplemental/video 36/src}/index.html | 0 {rmtDev/video 35 => 04-Supplemental/video 36}/src/index.js | 0 .../video 38 => 04-Supplemental/video 36}/webpack.config.js | 0 {Supplemental => 04-Supplemental}/video 37/index.html | 0 {Supplemental => 04-Supplemental}/video 37/script.js | 0 {Supplemental => 04-Supplemental}/video 37/style.css | 0 {rmtDev/video 35 => 04-Supplemental/video 38}/babel.config.js | 0 {rmtDev/video 35 => 04-Supplemental/video 38}/dist/index.html | 0 {rmtDev/video 35 => 04-Supplemental/video 38}/dist/main.css | 0 {rmtDev/video 35 => 04-Supplemental/video 38}/dist/main.js | 0 .../video 38}/dist/main.js.LICENSE.txt | 0 {rmtDev/video 35 => 04-Supplemental/video 38}/package-lock.json | 0 {rmtDev/video 35 => 04-Supplemental/video 38}/package.json | 0 {rmtDev/video 35 => 04-Supplemental/video 38}/postcss.config.js | 0 {Supplemental => 04-Supplemental}/video 38/src/common.js | 0 .../video 38}/src/components/Bookmarks.js | 0 .../video 35 => 04-Supplemental/video 38}/src/components/Error.js | 0 .../video 38}/src/components/JobDetails.js | 0 .../video 38/src/components/JobList.js | 0 .../video 38}/src/components/Pagination.js | 0 .../video 38}/src/components/Router.js | 0 .../video 38/src/components/Search.js | 0 .../video 38}/src/components/Sorting.js | 0 .../video 38}/src/components/Spinner.js | 0 .../video 38}/src/components/Storage.js | 0 {rmtDev/video 35 => 04-Supplemental/video 38}/src/index.css | 0 {rmtDev/video 35 => 04-Supplemental/video 38}/src/index.html | 0 {Supplemental => 04-Supplemental}/video 38/src/index.js | 0 {rmtDev/video 35 => 04-Supplemental/video 38}/webpack.config.js | 0 283 files changed, 0 insertions(+), 0 deletions(-) rename {Modern JS Fundamentals => 01-Modern JS Fundamentals}/index.html (100%) rename {Modern JS Fundamentals => 01-Modern JS Fundamentals}/script.js (100%) rename {Modern JS Fundamentals => 01-Modern JS Fundamentals}/style.css (100%) rename {CorpComment => 02-CorpComment}/video 04/index.html (100%) rename {CorpComment => 02-CorpComment}/video 04/script.js (100%) rename {CorpComment => 02-CorpComment}/video 04/style.css (100%) rename {CorpComment => 02-CorpComment}/video 05/index.html (100%) rename {CorpComment => 02-CorpComment}/video 05/script.js (100%) rename {CorpComment => 02-CorpComment}/video 05/style.css (100%) rename {CorpComment => 02-CorpComment}/video 06/index.html (100%) rename {CorpComment => 02-CorpComment}/video 06/script.js (100%) rename {CorpComment => 02-CorpComment}/video 06/style.css (100%) rename {CorpComment => 02-CorpComment}/video 07/index.html (100%) rename {CorpComment => 02-CorpComment}/video 07/script.js (100%) rename {CorpComment => 02-CorpComment}/video 07/style.css (100%) rename {CorpComment => 02-CorpComment}/video 08/index.html (100%) rename {CorpComment => 02-CorpComment}/video 08/script.js (100%) rename {CorpComment => 02-CorpComment}/video 08/style.css (100%) rename {CorpComment => 02-CorpComment}/video 09/index.html (100%) rename {CorpComment => 02-CorpComment}/video 09/script.js (100%) rename {CorpComment => 02-CorpComment}/video 09/style.css (100%) rename {CorpComment => 02-CorpComment}/video 10/index.html (100%) rename {CorpComment => 02-CorpComment}/video 10/script.js (100%) rename {CorpComment => 02-CorpComment}/video 10/style.css (100%) rename {CorpComment => 02-CorpComment}/video 11/index.html (100%) rename {CorpComment => 02-CorpComment}/video 11/script.js (100%) rename {CorpComment => 02-CorpComment}/video 11/style.css (100%) rename {CorpComment => 02-CorpComment}/video 12/index.html (100%) rename {CorpComment => 02-CorpComment}/video 12/script.js (100%) rename {CorpComment => 02-CorpComment}/video 12/style.css (100%) rename {CorpComment => 02-CorpComment}/video 13/index.html (100%) rename {CorpComment => 02-CorpComment}/video 13/script.js (100%) rename {CorpComment => 02-CorpComment}/video 13/style.css (100%) rename {CorpComment => 02-CorpComment}/video 14/index.html (100%) rename {CorpComment => 02-CorpComment}/video 14/script.js (100%) rename {CorpComment => 02-CorpComment}/video 14/style.css (100%) rename {Supplemental/video 36/src => 03-rmtDev/video 16}/index.css (100%) rename {rmtDev => 03-rmtDev}/video 16/index.html (100%) rename {rmtDev => 03-rmtDev}/video 16/index.js (100%) rename {Supplemental/video 38/src => 03-rmtDev/video 17}/index.css (100%) rename {rmtDev => 03-rmtDev}/video 17/index.html (100%) rename {rmtDev => 03-rmtDev}/video 17/index.js (100%) rename {rmtDev/video 16 => 03-rmtDev/video 18}/index.css (100%) rename {rmtDev => 03-rmtDev}/video 18/index.html (100%) rename {rmtDev => 03-rmtDev}/video 18/index.js (100%) rename {rmtDev/video 17 => 03-rmtDev/video 19}/index.css (100%) rename {Supplemental/video 36/src => 03-rmtDev/video 19}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 19/index.js (100%) rename {rmtDev => 03-rmtDev}/video 19/src/common.js (100%) rename {rmtDev => 03-rmtDev}/video 19/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 19/src/components/JobList.js (100%) rename {rmtDev => 03-rmtDev}/video 19/src/components/Search.js (100%) rename {rmtDev => 03-rmtDev}/video 19/src/components/Spinner.js (100%) rename {rmtDev/video 18 => 03-rmtDev/video 20}/index.css (100%) rename {Supplemental/video 38/src => 03-rmtDev/video 20}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 20/index.js (100%) rename {rmtDev => 03-rmtDev}/video 20/src/common.js (100%) rename {rmtDev => 03-rmtDev}/video 20/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 20/src/components/JobList.js (100%) rename {rmtDev => 03-rmtDev}/video 20/src/components/Search.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 20}/src/components/Spinner.js (100%) rename {rmtDev/video 19 => 03-rmtDev/video 21}/index.css (100%) rename {rmtDev/video 19 => 03-rmtDev/video 21}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 21/index.js (100%) rename {rmtDev => 03-rmtDev}/video 21/src/common.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 21}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 21/src/components/JobList.js (100%) rename {rmtDev => 03-rmtDev}/video 21/src/components/Search.js (100%) rename {Supplemental/video 38 => 03-rmtDev/video 21}/src/components/Spinner.js (100%) rename {rmtDev/video 20 => 03-rmtDev/video 22}/index.css (100%) rename {rmtDev/video 20 => 03-rmtDev/video 22}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 22/index.js (100%) rename {rmtDev => 03-rmtDev}/video 22/src/common.js (100%) rename {Supplemental/video 38 => 03-rmtDev/video 22}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 22/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 22/src/components/JobList.js (100%) rename {rmtDev => 03-rmtDev}/video 22/src/components/Search.js (100%) rename {rmtDev/video 20 => 03-rmtDev/video 22}/src/components/Spinner.js (100%) rename {rmtDev/video 21 => 03-rmtDev/video 23}/index.css (100%) rename {rmtDev/video 21 => 03-rmtDev/video 23}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 23/index.js (100%) rename {rmtDev => 03-rmtDev}/video 23/src/common.js (100%) rename {rmtDev/video 21 => 03-rmtDev/video 23}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 23/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 23/src/components/JobList.js (100%) rename {rmtDev => 03-rmtDev}/video 23/src/components/Search.js (100%) rename {rmtDev/video 21 => 03-rmtDev/video 23}/src/components/Spinner.js (100%) rename {rmtDev/video 22 => 03-rmtDev/video 24}/index.css (100%) rename {rmtDev/video 22 => 03-rmtDev/video 24}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 24/index.js (100%) rename {rmtDev => 03-rmtDev}/video 24/src/common.js (100%) rename {rmtDev/video 22 => 03-rmtDev/video 24}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 24/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 24/src/components/JobList.js (100%) rename {rmtDev => 03-rmtDev}/video 24/src/components/Search.js (100%) rename {rmtDev/video 22 => 03-rmtDev/video 24}/src/components/Spinner.js (100%) rename {rmtDev/video 23 => 03-rmtDev/video 25}/index.css (100%) rename {rmtDev/video 23 => 03-rmtDev/video 25}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 25/index.js (100%) rename {rmtDev => 03-rmtDev}/video 25/src/common.js (100%) rename {rmtDev/video 23 => 03-rmtDev/video 25}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 25/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 25/src/components/JobList.js (100%) rename {rmtDev => 03-rmtDev}/video 25/src/components/Search.js (100%) rename {rmtDev/video 23 => 03-rmtDev/video 25}/src/components/Spinner.js (100%) rename {rmtDev/video 24 => 03-rmtDev/video 26}/index.css (100%) rename {rmtDev/video 24 => 03-rmtDev/video 26}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 26/index.js (100%) rename {rmtDev => 03-rmtDev}/video 26/src/common.js (100%) rename {rmtDev/video 24 => 03-rmtDev/video 26}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 26/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 26/src/components/JobList.js (100%) rename {rmtDev => 03-rmtDev}/video 26/src/components/Search.js (100%) rename {rmtDev => 03-rmtDev}/video 26/src/components/Sorting.js (100%) rename {rmtDev/video 24 => 03-rmtDev/video 26}/src/components/Spinner.js (100%) rename {rmtDev/video 25 => 03-rmtDev/video 27}/index.css (100%) rename {rmtDev/video 25 => 03-rmtDev/video 27}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 27/index.js (100%) rename {rmtDev => 03-rmtDev}/video 27/src/common.js (100%) rename {rmtDev/video 25 => 03-rmtDev/video 27}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 27/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 27/src/components/JobList.js (100%) rename {rmtDev => 03-rmtDev}/video 27/src/components/Search.js (100%) rename {rmtDev => 03-rmtDev}/video 27/src/components/Sorting.js (100%) rename {rmtDev/video 25 => 03-rmtDev/video 27}/src/components/Spinner.js (100%) rename {rmtDev/video 26 => 03-rmtDev/video 28}/index.css (100%) rename {rmtDev/video 26 => 03-rmtDev/video 28}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 28/index.js (100%) rename {rmtDev => 03-rmtDev}/video 28/src/common.js (100%) rename {rmtDev/video 26 => 03-rmtDev/video 28}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 28/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 28/src/components/JobList.js (100%) rename {rmtDev => 03-rmtDev}/video 28/src/components/Search.js (100%) rename {rmtDev => 03-rmtDev}/video 28/src/components/Sorting.js (100%) rename {rmtDev/video 26 => 03-rmtDev/video 28}/src/components/Spinner.js (100%) rename {rmtDev/video 27 => 03-rmtDev/video 29}/index.css (100%) rename {rmtDev/video 27 => 03-rmtDev/video 29}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 29/index.js (100%) rename {rmtDev => 03-rmtDev}/video 29/src/common.js (100%) rename {rmtDev/video 27 => 03-rmtDev/video 29}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 29/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 29/src/components/JobList.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 29}/src/components/Pagination.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 29}/src/components/Search.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 29}/src/components/Sorting.js (100%) rename {rmtDev/video 27 => 03-rmtDev/video 29}/src/components/Spinner.js (100%) rename {rmtDev/video 28 => 03-rmtDev/video 30}/index.css (100%) rename {rmtDev/video 28 => 03-rmtDev/video 30}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 30/index.js (100%) rename {rmtDev => 03-rmtDev}/video 30/src/common.js (100%) rename {rmtDev/video 28 => 03-rmtDev/video 30}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 30/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 30/src/components/JobList.js (100%) rename {Supplemental/video 38 => 03-rmtDev/video 30}/src/components/Pagination.js (100%) rename {rmtDev => 03-rmtDev}/video 30/src/components/Router.js (100%) rename {rmtDev/video 29 => 03-rmtDev/video 30}/src/components/Search.js (100%) rename {Supplemental/video 38 => 03-rmtDev/video 30}/src/components/Sorting.js (100%) rename {rmtDev/video 28 => 03-rmtDev/video 30}/src/components/Spinner.js (100%) rename {rmtDev/video 29 => 03-rmtDev/video 31}/index.css (100%) rename {rmtDev/video 29 => 03-rmtDev/video 31}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 31/index.js (100%) rename {rmtDev => 03-rmtDev}/video 31/src/common.js (100%) rename {rmtDev/video 29 => 03-rmtDev/video 31}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 31/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 31/src/components/JobList.js (100%) rename {rmtDev/video 29 => 03-rmtDev/video 31}/src/components/Pagination.js (100%) rename {rmtDev => 03-rmtDev}/video 31/src/components/Router.js (100%) rename {rmtDev/video 30 => 03-rmtDev/video 31}/src/components/Search.js (100%) rename {rmtDev/video 29 => 03-rmtDev/video 31}/src/components/Sorting.js (100%) rename {rmtDev/video 29 => 03-rmtDev/video 31}/src/components/Spinner.js (100%) rename {rmtDev/video 30 => 03-rmtDev/video 32}/index.css (100%) rename {rmtDev/video 30 => 03-rmtDev/video 32}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 32/index.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 32}/src/common.js (100%) rename {rmtDev => 03-rmtDev}/video 32/src/components/Bookmarks.js (100%) rename {rmtDev/video 30 => 03-rmtDev/video 32}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 32/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 32/src/components/JobList.js (100%) rename {rmtDev/video 30 => 03-rmtDev/video 32}/src/components/Pagination.js (100%) rename {rmtDev => 03-rmtDev}/video 32/src/components/Router.js (100%) rename {rmtDev/video 31 => 03-rmtDev/video 32}/src/components/Search.js (100%) rename {rmtDev/video 30 => 03-rmtDev/video 32}/src/components/Sorting.js (100%) rename {rmtDev/video 30 => 03-rmtDev/video 32}/src/components/Spinner.js (100%) rename {rmtDev/video 31 => 03-rmtDev/video 33}/index.css (100%) rename {rmtDev/video 31 => 03-rmtDev/video 33}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 33/index.js (100%) rename {rmtDev/video 32 => 03-rmtDev/video 33}/src/common.js (100%) rename {rmtDev => 03-rmtDev}/video 33/src/components/Bookmarks.js (100%) rename {rmtDev/video 31 => 03-rmtDev/video 33}/src/components/Error.js (100%) rename {rmtDev => 03-rmtDev}/video 33/src/components/JobDetails.js (100%) rename {rmtDev => 03-rmtDev}/video 33/src/components/JobList.js (100%) rename {rmtDev/video 31 => 03-rmtDev/video 33}/src/components/Pagination.js (100%) rename {rmtDev => 03-rmtDev}/video 33/src/components/Router.js (100%) rename {rmtDev/video 32 => 03-rmtDev/video 33}/src/components/Search.js (100%) rename {rmtDev/video 31 => 03-rmtDev/video 33}/src/components/Sorting.js (100%) rename {rmtDev/video 31 => 03-rmtDev/video 33}/src/components/Spinner.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 33}/src/components/Storage.js (100%) rename {rmtDev/video 32 => 03-rmtDev/video 34}/index.css (100%) rename {rmtDev/video 32 => 03-rmtDev/video 34}/index.html (100%) rename {rmtDev => 03-rmtDev}/video 34/index.js (100%) rename {rmtDev/video 33 => 03-rmtDev/video 34}/src/common.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 34}/src/components/Bookmarks.js (100%) rename {rmtDev/video 32 => 03-rmtDev/video 34}/src/components/Error.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 34}/src/components/JobDetails.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 34}/src/components/JobList.js (100%) rename {rmtDev/video 32 => 03-rmtDev/video 34}/src/components/Pagination.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 34}/src/components/Router.js (100%) rename {rmtDev/video 33 => 03-rmtDev/video 34}/src/components/Search.js (100%) rename {rmtDev/video 32 => 03-rmtDev/video 34}/src/components/Sorting.js (100%) rename {rmtDev/video 32 => 03-rmtDev/video 34}/src/components/Spinner.js (100%) rename {Supplemental/video 38 => 03-rmtDev/video 34}/src/components/Storage.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 35}/babel.config.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 35}/dist/index.html (100%) rename {Supplemental/video 36 => 03-rmtDev/video 35}/dist/main.css (100%) rename {Supplemental/video 36 => 03-rmtDev/video 35}/dist/main.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 35}/dist/main.js.LICENSE.txt (100%) rename {Supplemental/video 36 => 03-rmtDev/video 35}/package-lock.json (100%) rename {Supplemental/video 36 => 03-rmtDev/video 35}/package.json (100%) rename {Supplemental/video 36 => 03-rmtDev/video 35}/postcss.config.js (100%) rename {rmtDev/video 34 => 03-rmtDev/video 35}/src/common.js (100%) rename {Supplemental/video 38 => 03-rmtDev/video 35}/src/components/Bookmarks.js (100%) rename {rmtDev/video 33 => 03-rmtDev/video 35}/src/components/Error.js (100%) rename {Supplemental/video 38 => 03-rmtDev/video 35}/src/components/JobDetails.js (100%) rename {rmtDev/video 34 => 03-rmtDev/video 35}/src/components/JobList.js (100%) rename {rmtDev/video 33 => 03-rmtDev/video 35}/src/components/Pagination.js (100%) rename {Supplemental/video 38 => 03-rmtDev/video 35}/src/components/Router.js (100%) rename {rmtDev/video 34 => 03-rmtDev/video 35}/src/components/Search.js (100%) rename {rmtDev/video 33 => 03-rmtDev/video 35}/src/components/Sorting.js (100%) rename {rmtDev/video 33 => 03-rmtDev/video 35}/src/components/Spinner.js (100%) rename {rmtDev/video 33 => 03-rmtDev/video 35}/src/components/Storage.js (100%) rename {rmtDev/video 33 => 03-rmtDev/video 35/src}/index.css (100%) rename {rmtDev/video 33 => 03-rmtDev/video 35/src}/index.html (100%) rename {Supplemental/video 36 => 03-rmtDev/video 35}/src/index.js (100%) rename {Supplemental/video 36 => 03-rmtDev/video 35}/webpack.config.js (100%) rename {Supplemental/video 38 => 04-Supplemental/video 36}/babel.config.js (100%) rename {Supplemental/video 38 => 04-Supplemental/video 36}/dist/index.html (100%) rename {Supplemental/video 38 => 04-Supplemental/video 36}/dist/main.css (100%) rename {Supplemental/video 38 => 04-Supplemental/video 36}/dist/main.js (100%) rename {Supplemental/video 38 => 04-Supplemental/video 36}/dist/main.js.LICENSE.txt (100%) rename {Supplemental/video 38 => 04-Supplemental/video 36}/package-lock.json (100%) rename {Supplemental/video 38 => 04-Supplemental/video 36}/package.json (100%) rename {Supplemental/video 38 => 04-Supplemental/video 36}/postcss.config.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 36}/src/common.js (100%) rename {rmtDev/video 34 => 04-Supplemental/video 36}/src/components/Bookmarks.js (100%) rename {rmtDev/video 34 => 04-Supplemental/video 36}/src/components/Error.js (100%) rename {rmtDev/video 34 => 04-Supplemental/video 36}/src/components/JobDetails.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 36}/src/components/JobList.js (100%) rename {rmtDev/video 34 => 04-Supplemental/video 36}/src/components/Pagination.js (100%) rename {rmtDev/video 34 => 04-Supplemental/video 36}/src/components/Router.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 36}/src/components/Search.js (100%) rename {rmtDev/video 34 => 04-Supplemental/video 36}/src/components/Sorting.js (100%) rename {rmtDev/video 34 => 04-Supplemental/video 36}/src/components/Spinner.js (100%) rename {rmtDev/video 34 => 04-Supplemental/video 36}/src/components/Storage.js (100%) rename {rmtDev/video 34 => 04-Supplemental/video 36/src}/index.css (100%) rename {rmtDev/video 34 => 04-Supplemental/video 36/src}/index.html (100%) rename {rmtDev/video 35 => 04-Supplemental/video 36}/src/index.js (100%) rename {Supplemental/video 38 => 04-Supplemental/video 36}/webpack.config.js (100%) rename {Supplemental => 04-Supplemental}/video 37/index.html (100%) rename {Supplemental => 04-Supplemental}/video 37/script.js (100%) rename {Supplemental => 04-Supplemental}/video 37/style.css (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/babel.config.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/dist/index.html (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/dist/main.css (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/dist/main.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/dist/main.js.LICENSE.txt (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/package-lock.json (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/package.json (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/postcss.config.js (100%) rename {Supplemental => 04-Supplemental}/video 38/src/common.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/src/components/Bookmarks.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/src/components/Error.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/src/components/JobDetails.js (100%) rename {Supplemental => 04-Supplemental}/video 38/src/components/JobList.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/src/components/Pagination.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/src/components/Router.js (100%) rename {Supplemental => 04-Supplemental}/video 38/src/components/Search.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/src/components/Sorting.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/src/components/Spinner.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/src/components/Storage.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/src/index.css (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/src/index.html (100%) rename {Supplemental => 04-Supplemental}/video 38/src/index.js (100%) rename {rmtDev/video 35 => 04-Supplemental/video 38}/webpack.config.js (100%) diff --git a/Modern JS Fundamentals/index.html b/01-Modern JS Fundamentals/index.html similarity index 100% rename from Modern JS Fundamentals/index.html rename to 01-Modern JS Fundamentals/index.html diff --git a/Modern JS Fundamentals/script.js b/01-Modern JS Fundamentals/script.js similarity index 100% rename from Modern JS Fundamentals/script.js rename to 01-Modern JS Fundamentals/script.js diff --git a/Modern JS Fundamentals/style.css b/01-Modern JS Fundamentals/style.css similarity index 100% rename from Modern JS Fundamentals/style.css rename to 01-Modern JS Fundamentals/style.css diff --git a/CorpComment/video 04/index.html b/02-CorpComment/video 04/index.html similarity index 100% rename from CorpComment/video 04/index.html rename to 02-CorpComment/video 04/index.html diff --git a/CorpComment/video 04/script.js b/02-CorpComment/video 04/script.js similarity index 100% rename from CorpComment/video 04/script.js rename to 02-CorpComment/video 04/script.js diff --git a/CorpComment/video 04/style.css b/02-CorpComment/video 04/style.css similarity index 100% rename from CorpComment/video 04/style.css rename to 02-CorpComment/video 04/style.css diff --git a/CorpComment/video 05/index.html b/02-CorpComment/video 05/index.html similarity index 100% rename from CorpComment/video 05/index.html rename to 02-CorpComment/video 05/index.html diff --git a/CorpComment/video 05/script.js b/02-CorpComment/video 05/script.js similarity index 100% rename from CorpComment/video 05/script.js rename to 02-CorpComment/video 05/script.js diff --git a/CorpComment/video 05/style.css b/02-CorpComment/video 05/style.css similarity index 100% rename from CorpComment/video 05/style.css rename to 02-CorpComment/video 05/style.css diff --git a/CorpComment/video 06/index.html b/02-CorpComment/video 06/index.html similarity index 100% rename from CorpComment/video 06/index.html rename to 02-CorpComment/video 06/index.html diff --git a/CorpComment/video 06/script.js b/02-CorpComment/video 06/script.js similarity index 100% rename from CorpComment/video 06/script.js rename to 02-CorpComment/video 06/script.js diff --git a/CorpComment/video 06/style.css b/02-CorpComment/video 06/style.css similarity index 100% rename from CorpComment/video 06/style.css rename to 02-CorpComment/video 06/style.css diff --git a/CorpComment/video 07/index.html b/02-CorpComment/video 07/index.html similarity index 100% rename from CorpComment/video 07/index.html rename to 02-CorpComment/video 07/index.html diff --git a/CorpComment/video 07/script.js b/02-CorpComment/video 07/script.js similarity index 100% rename from CorpComment/video 07/script.js rename to 02-CorpComment/video 07/script.js diff --git a/CorpComment/video 07/style.css b/02-CorpComment/video 07/style.css similarity index 100% rename from CorpComment/video 07/style.css rename to 02-CorpComment/video 07/style.css diff --git a/CorpComment/video 08/index.html b/02-CorpComment/video 08/index.html similarity index 100% rename from CorpComment/video 08/index.html rename to 02-CorpComment/video 08/index.html diff --git a/CorpComment/video 08/script.js b/02-CorpComment/video 08/script.js similarity index 100% rename from CorpComment/video 08/script.js rename to 02-CorpComment/video 08/script.js diff --git a/CorpComment/video 08/style.css b/02-CorpComment/video 08/style.css similarity index 100% rename from CorpComment/video 08/style.css rename to 02-CorpComment/video 08/style.css diff --git a/CorpComment/video 09/index.html b/02-CorpComment/video 09/index.html similarity index 100% rename from CorpComment/video 09/index.html rename to 02-CorpComment/video 09/index.html diff --git a/CorpComment/video 09/script.js b/02-CorpComment/video 09/script.js similarity index 100% rename from CorpComment/video 09/script.js rename to 02-CorpComment/video 09/script.js diff --git a/CorpComment/video 09/style.css b/02-CorpComment/video 09/style.css similarity index 100% rename from CorpComment/video 09/style.css rename to 02-CorpComment/video 09/style.css diff --git a/CorpComment/video 10/index.html b/02-CorpComment/video 10/index.html similarity index 100% rename from CorpComment/video 10/index.html rename to 02-CorpComment/video 10/index.html diff --git a/CorpComment/video 10/script.js b/02-CorpComment/video 10/script.js similarity index 100% rename from CorpComment/video 10/script.js rename to 02-CorpComment/video 10/script.js diff --git a/CorpComment/video 10/style.css b/02-CorpComment/video 10/style.css similarity index 100% rename from CorpComment/video 10/style.css rename to 02-CorpComment/video 10/style.css diff --git a/CorpComment/video 11/index.html b/02-CorpComment/video 11/index.html similarity index 100% rename from CorpComment/video 11/index.html rename to 02-CorpComment/video 11/index.html diff --git a/CorpComment/video 11/script.js b/02-CorpComment/video 11/script.js similarity index 100% rename from CorpComment/video 11/script.js rename to 02-CorpComment/video 11/script.js diff --git a/CorpComment/video 11/style.css b/02-CorpComment/video 11/style.css similarity index 100% rename from CorpComment/video 11/style.css rename to 02-CorpComment/video 11/style.css diff --git a/CorpComment/video 12/index.html b/02-CorpComment/video 12/index.html similarity index 100% rename from CorpComment/video 12/index.html rename to 02-CorpComment/video 12/index.html diff --git a/CorpComment/video 12/script.js b/02-CorpComment/video 12/script.js similarity index 100% rename from CorpComment/video 12/script.js rename to 02-CorpComment/video 12/script.js diff --git a/CorpComment/video 12/style.css b/02-CorpComment/video 12/style.css similarity index 100% rename from CorpComment/video 12/style.css rename to 02-CorpComment/video 12/style.css diff --git a/CorpComment/video 13/index.html b/02-CorpComment/video 13/index.html similarity index 100% rename from CorpComment/video 13/index.html rename to 02-CorpComment/video 13/index.html diff --git a/CorpComment/video 13/script.js b/02-CorpComment/video 13/script.js similarity index 100% rename from CorpComment/video 13/script.js rename to 02-CorpComment/video 13/script.js diff --git a/CorpComment/video 13/style.css b/02-CorpComment/video 13/style.css similarity index 100% rename from CorpComment/video 13/style.css rename to 02-CorpComment/video 13/style.css diff --git a/CorpComment/video 14/index.html b/02-CorpComment/video 14/index.html similarity index 100% rename from CorpComment/video 14/index.html rename to 02-CorpComment/video 14/index.html diff --git a/CorpComment/video 14/script.js b/02-CorpComment/video 14/script.js similarity index 100% rename from CorpComment/video 14/script.js rename to 02-CorpComment/video 14/script.js diff --git a/CorpComment/video 14/style.css b/02-CorpComment/video 14/style.css similarity index 100% rename from CorpComment/video 14/style.css rename to 02-CorpComment/video 14/style.css diff --git a/Supplemental/video 36/src/index.css b/03-rmtDev/video 16/index.css similarity index 100% rename from Supplemental/video 36/src/index.css rename to 03-rmtDev/video 16/index.css diff --git a/rmtDev/video 16/index.html b/03-rmtDev/video 16/index.html similarity index 100% rename from rmtDev/video 16/index.html rename to 03-rmtDev/video 16/index.html diff --git a/rmtDev/video 16/index.js b/03-rmtDev/video 16/index.js similarity index 100% rename from rmtDev/video 16/index.js rename to 03-rmtDev/video 16/index.js diff --git a/Supplemental/video 38/src/index.css b/03-rmtDev/video 17/index.css similarity index 100% rename from Supplemental/video 38/src/index.css rename to 03-rmtDev/video 17/index.css diff --git a/rmtDev/video 17/index.html b/03-rmtDev/video 17/index.html similarity index 100% rename from rmtDev/video 17/index.html rename to 03-rmtDev/video 17/index.html diff --git a/rmtDev/video 17/index.js b/03-rmtDev/video 17/index.js similarity index 100% rename from rmtDev/video 17/index.js rename to 03-rmtDev/video 17/index.js diff --git a/rmtDev/video 16/index.css b/03-rmtDev/video 18/index.css similarity index 100% rename from rmtDev/video 16/index.css rename to 03-rmtDev/video 18/index.css diff --git a/rmtDev/video 18/index.html b/03-rmtDev/video 18/index.html similarity index 100% rename from rmtDev/video 18/index.html rename to 03-rmtDev/video 18/index.html diff --git a/rmtDev/video 18/index.js b/03-rmtDev/video 18/index.js similarity index 100% rename from rmtDev/video 18/index.js rename to 03-rmtDev/video 18/index.js diff --git a/rmtDev/video 17/index.css b/03-rmtDev/video 19/index.css similarity index 100% rename from rmtDev/video 17/index.css rename to 03-rmtDev/video 19/index.css diff --git a/Supplemental/video 36/src/index.html b/03-rmtDev/video 19/index.html similarity index 100% rename from Supplemental/video 36/src/index.html rename to 03-rmtDev/video 19/index.html diff --git a/rmtDev/video 19/index.js b/03-rmtDev/video 19/index.js similarity index 100% rename from rmtDev/video 19/index.js rename to 03-rmtDev/video 19/index.js diff --git a/rmtDev/video 19/src/common.js b/03-rmtDev/video 19/src/common.js similarity index 100% rename from rmtDev/video 19/src/common.js rename to 03-rmtDev/video 19/src/common.js diff --git a/rmtDev/video 19/src/components/Error.js b/03-rmtDev/video 19/src/components/Error.js similarity index 100% rename from rmtDev/video 19/src/components/Error.js rename to 03-rmtDev/video 19/src/components/Error.js diff --git a/rmtDev/video 19/src/components/JobList.js b/03-rmtDev/video 19/src/components/JobList.js similarity index 100% rename from rmtDev/video 19/src/components/JobList.js rename to 03-rmtDev/video 19/src/components/JobList.js diff --git a/rmtDev/video 19/src/components/Search.js b/03-rmtDev/video 19/src/components/Search.js similarity index 100% rename from rmtDev/video 19/src/components/Search.js rename to 03-rmtDev/video 19/src/components/Search.js diff --git a/rmtDev/video 19/src/components/Spinner.js b/03-rmtDev/video 19/src/components/Spinner.js similarity index 100% rename from rmtDev/video 19/src/components/Spinner.js rename to 03-rmtDev/video 19/src/components/Spinner.js diff --git a/rmtDev/video 18/index.css b/03-rmtDev/video 20/index.css similarity index 100% rename from rmtDev/video 18/index.css rename to 03-rmtDev/video 20/index.css diff --git a/Supplemental/video 38/src/index.html b/03-rmtDev/video 20/index.html similarity index 100% rename from Supplemental/video 38/src/index.html rename to 03-rmtDev/video 20/index.html diff --git a/rmtDev/video 20/index.js b/03-rmtDev/video 20/index.js similarity index 100% rename from rmtDev/video 20/index.js rename to 03-rmtDev/video 20/index.js diff --git a/rmtDev/video 20/src/common.js b/03-rmtDev/video 20/src/common.js similarity index 100% rename from rmtDev/video 20/src/common.js rename to 03-rmtDev/video 20/src/common.js diff --git a/rmtDev/video 20/src/components/Error.js b/03-rmtDev/video 20/src/components/Error.js similarity index 100% rename from rmtDev/video 20/src/components/Error.js rename to 03-rmtDev/video 20/src/components/Error.js diff --git a/rmtDev/video 20/src/components/JobList.js b/03-rmtDev/video 20/src/components/JobList.js similarity index 100% rename from rmtDev/video 20/src/components/JobList.js rename to 03-rmtDev/video 20/src/components/JobList.js diff --git a/rmtDev/video 20/src/components/Search.js b/03-rmtDev/video 20/src/components/Search.js similarity index 100% rename from rmtDev/video 20/src/components/Search.js rename to 03-rmtDev/video 20/src/components/Search.js diff --git a/Supplemental/video 36/src/components/Spinner.js b/03-rmtDev/video 20/src/components/Spinner.js similarity index 100% rename from Supplemental/video 36/src/components/Spinner.js rename to 03-rmtDev/video 20/src/components/Spinner.js diff --git a/rmtDev/video 19/index.css b/03-rmtDev/video 21/index.css similarity index 100% rename from rmtDev/video 19/index.css rename to 03-rmtDev/video 21/index.css diff --git a/rmtDev/video 19/index.html b/03-rmtDev/video 21/index.html similarity index 100% rename from rmtDev/video 19/index.html rename to 03-rmtDev/video 21/index.html diff --git a/rmtDev/video 21/index.js b/03-rmtDev/video 21/index.js similarity index 100% rename from rmtDev/video 21/index.js rename to 03-rmtDev/video 21/index.js diff --git a/rmtDev/video 21/src/common.js b/03-rmtDev/video 21/src/common.js similarity index 100% rename from rmtDev/video 21/src/common.js rename to 03-rmtDev/video 21/src/common.js diff --git a/Supplemental/video 36/src/components/Error.js b/03-rmtDev/video 21/src/components/Error.js similarity index 100% rename from Supplemental/video 36/src/components/Error.js rename to 03-rmtDev/video 21/src/components/Error.js diff --git a/rmtDev/video 21/src/components/JobList.js b/03-rmtDev/video 21/src/components/JobList.js similarity index 100% rename from rmtDev/video 21/src/components/JobList.js rename to 03-rmtDev/video 21/src/components/JobList.js diff --git a/rmtDev/video 21/src/components/Search.js b/03-rmtDev/video 21/src/components/Search.js similarity index 100% rename from rmtDev/video 21/src/components/Search.js rename to 03-rmtDev/video 21/src/components/Search.js diff --git a/Supplemental/video 38/src/components/Spinner.js b/03-rmtDev/video 21/src/components/Spinner.js similarity index 100% rename from Supplemental/video 38/src/components/Spinner.js rename to 03-rmtDev/video 21/src/components/Spinner.js diff --git a/rmtDev/video 20/index.css b/03-rmtDev/video 22/index.css similarity index 100% rename from rmtDev/video 20/index.css rename to 03-rmtDev/video 22/index.css diff --git a/rmtDev/video 20/index.html b/03-rmtDev/video 22/index.html similarity index 100% rename from rmtDev/video 20/index.html rename to 03-rmtDev/video 22/index.html diff --git a/rmtDev/video 22/index.js b/03-rmtDev/video 22/index.js similarity index 100% rename from rmtDev/video 22/index.js rename to 03-rmtDev/video 22/index.js diff --git a/rmtDev/video 22/src/common.js b/03-rmtDev/video 22/src/common.js similarity index 100% rename from rmtDev/video 22/src/common.js rename to 03-rmtDev/video 22/src/common.js diff --git a/Supplemental/video 38/src/components/Error.js b/03-rmtDev/video 22/src/components/Error.js similarity index 100% rename from Supplemental/video 38/src/components/Error.js rename to 03-rmtDev/video 22/src/components/Error.js diff --git a/rmtDev/video 22/src/components/JobDetails.js b/03-rmtDev/video 22/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 22/src/components/JobDetails.js rename to 03-rmtDev/video 22/src/components/JobDetails.js diff --git a/rmtDev/video 22/src/components/JobList.js b/03-rmtDev/video 22/src/components/JobList.js similarity index 100% rename from rmtDev/video 22/src/components/JobList.js rename to 03-rmtDev/video 22/src/components/JobList.js diff --git a/rmtDev/video 22/src/components/Search.js b/03-rmtDev/video 22/src/components/Search.js similarity index 100% rename from rmtDev/video 22/src/components/Search.js rename to 03-rmtDev/video 22/src/components/Search.js diff --git a/rmtDev/video 20/src/components/Spinner.js b/03-rmtDev/video 22/src/components/Spinner.js similarity index 100% rename from rmtDev/video 20/src/components/Spinner.js rename to 03-rmtDev/video 22/src/components/Spinner.js diff --git a/rmtDev/video 21/index.css b/03-rmtDev/video 23/index.css similarity index 100% rename from rmtDev/video 21/index.css rename to 03-rmtDev/video 23/index.css diff --git a/rmtDev/video 21/index.html b/03-rmtDev/video 23/index.html similarity index 100% rename from rmtDev/video 21/index.html rename to 03-rmtDev/video 23/index.html diff --git a/rmtDev/video 23/index.js b/03-rmtDev/video 23/index.js similarity index 100% rename from rmtDev/video 23/index.js rename to 03-rmtDev/video 23/index.js diff --git a/rmtDev/video 23/src/common.js b/03-rmtDev/video 23/src/common.js similarity index 100% rename from rmtDev/video 23/src/common.js rename to 03-rmtDev/video 23/src/common.js diff --git a/rmtDev/video 21/src/components/Error.js b/03-rmtDev/video 23/src/components/Error.js similarity index 100% rename from rmtDev/video 21/src/components/Error.js rename to 03-rmtDev/video 23/src/components/Error.js diff --git a/rmtDev/video 23/src/components/JobDetails.js b/03-rmtDev/video 23/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 23/src/components/JobDetails.js rename to 03-rmtDev/video 23/src/components/JobDetails.js diff --git a/rmtDev/video 23/src/components/JobList.js b/03-rmtDev/video 23/src/components/JobList.js similarity index 100% rename from rmtDev/video 23/src/components/JobList.js rename to 03-rmtDev/video 23/src/components/JobList.js diff --git a/rmtDev/video 23/src/components/Search.js b/03-rmtDev/video 23/src/components/Search.js similarity index 100% rename from rmtDev/video 23/src/components/Search.js rename to 03-rmtDev/video 23/src/components/Search.js diff --git a/rmtDev/video 21/src/components/Spinner.js b/03-rmtDev/video 23/src/components/Spinner.js similarity index 100% rename from rmtDev/video 21/src/components/Spinner.js rename to 03-rmtDev/video 23/src/components/Spinner.js diff --git a/rmtDev/video 22/index.css b/03-rmtDev/video 24/index.css similarity index 100% rename from rmtDev/video 22/index.css rename to 03-rmtDev/video 24/index.css diff --git a/rmtDev/video 22/index.html b/03-rmtDev/video 24/index.html similarity index 100% rename from rmtDev/video 22/index.html rename to 03-rmtDev/video 24/index.html diff --git a/rmtDev/video 24/index.js b/03-rmtDev/video 24/index.js similarity index 100% rename from rmtDev/video 24/index.js rename to 03-rmtDev/video 24/index.js diff --git a/rmtDev/video 24/src/common.js b/03-rmtDev/video 24/src/common.js similarity index 100% rename from rmtDev/video 24/src/common.js rename to 03-rmtDev/video 24/src/common.js diff --git a/rmtDev/video 22/src/components/Error.js b/03-rmtDev/video 24/src/components/Error.js similarity index 100% rename from rmtDev/video 22/src/components/Error.js rename to 03-rmtDev/video 24/src/components/Error.js diff --git a/rmtDev/video 24/src/components/JobDetails.js b/03-rmtDev/video 24/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 24/src/components/JobDetails.js rename to 03-rmtDev/video 24/src/components/JobDetails.js diff --git a/rmtDev/video 24/src/components/JobList.js b/03-rmtDev/video 24/src/components/JobList.js similarity index 100% rename from rmtDev/video 24/src/components/JobList.js rename to 03-rmtDev/video 24/src/components/JobList.js diff --git a/rmtDev/video 24/src/components/Search.js b/03-rmtDev/video 24/src/components/Search.js similarity index 100% rename from rmtDev/video 24/src/components/Search.js rename to 03-rmtDev/video 24/src/components/Search.js diff --git a/rmtDev/video 22/src/components/Spinner.js b/03-rmtDev/video 24/src/components/Spinner.js similarity index 100% rename from rmtDev/video 22/src/components/Spinner.js rename to 03-rmtDev/video 24/src/components/Spinner.js diff --git a/rmtDev/video 23/index.css b/03-rmtDev/video 25/index.css similarity index 100% rename from rmtDev/video 23/index.css rename to 03-rmtDev/video 25/index.css diff --git a/rmtDev/video 23/index.html b/03-rmtDev/video 25/index.html similarity index 100% rename from rmtDev/video 23/index.html rename to 03-rmtDev/video 25/index.html diff --git a/rmtDev/video 25/index.js b/03-rmtDev/video 25/index.js similarity index 100% rename from rmtDev/video 25/index.js rename to 03-rmtDev/video 25/index.js diff --git a/rmtDev/video 25/src/common.js b/03-rmtDev/video 25/src/common.js similarity index 100% rename from rmtDev/video 25/src/common.js rename to 03-rmtDev/video 25/src/common.js diff --git a/rmtDev/video 23/src/components/Error.js b/03-rmtDev/video 25/src/components/Error.js similarity index 100% rename from rmtDev/video 23/src/components/Error.js rename to 03-rmtDev/video 25/src/components/Error.js diff --git a/rmtDev/video 25/src/components/JobDetails.js b/03-rmtDev/video 25/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 25/src/components/JobDetails.js rename to 03-rmtDev/video 25/src/components/JobDetails.js diff --git a/rmtDev/video 25/src/components/JobList.js b/03-rmtDev/video 25/src/components/JobList.js similarity index 100% rename from rmtDev/video 25/src/components/JobList.js rename to 03-rmtDev/video 25/src/components/JobList.js diff --git a/rmtDev/video 25/src/components/Search.js b/03-rmtDev/video 25/src/components/Search.js similarity index 100% rename from rmtDev/video 25/src/components/Search.js rename to 03-rmtDev/video 25/src/components/Search.js diff --git a/rmtDev/video 23/src/components/Spinner.js b/03-rmtDev/video 25/src/components/Spinner.js similarity index 100% rename from rmtDev/video 23/src/components/Spinner.js rename to 03-rmtDev/video 25/src/components/Spinner.js diff --git a/rmtDev/video 24/index.css b/03-rmtDev/video 26/index.css similarity index 100% rename from rmtDev/video 24/index.css rename to 03-rmtDev/video 26/index.css diff --git a/rmtDev/video 24/index.html b/03-rmtDev/video 26/index.html similarity index 100% rename from rmtDev/video 24/index.html rename to 03-rmtDev/video 26/index.html diff --git a/rmtDev/video 26/index.js b/03-rmtDev/video 26/index.js similarity index 100% rename from rmtDev/video 26/index.js rename to 03-rmtDev/video 26/index.js diff --git a/rmtDev/video 26/src/common.js b/03-rmtDev/video 26/src/common.js similarity index 100% rename from rmtDev/video 26/src/common.js rename to 03-rmtDev/video 26/src/common.js diff --git a/rmtDev/video 24/src/components/Error.js b/03-rmtDev/video 26/src/components/Error.js similarity index 100% rename from rmtDev/video 24/src/components/Error.js rename to 03-rmtDev/video 26/src/components/Error.js diff --git a/rmtDev/video 26/src/components/JobDetails.js b/03-rmtDev/video 26/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 26/src/components/JobDetails.js rename to 03-rmtDev/video 26/src/components/JobDetails.js diff --git a/rmtDev/video 26/src/components/JobList.js b/03-rmtDev/video 26/src/components/JobList.js similarity index 100% rename from rmtDev/video 26/src/components/JobList.js rename to 03-rmtDev/video 26/src/components/JobList.js diff --git a/rmtDev/video 26/src/components/Search.js b/03-rmtDev/video 26/src/components/Search.js similarity index 100% rename from rmtDev/video 26/src/components/Search.js rename to 03-rmtDev/video 26/src/components/Search.js diff --git a/rmtDev/video 26/src/components/Sorting.js b/03-rmtDev/video 26/src/components/Sorting.js similarity index 100% rename from rmtDev/video 26/src/components/Sorting.js rename to 03-rmtDev/video 26/src/components/Sorting.js diff --git a/rmtDev/video 24/src/components/Spinner.js b/03-rmtDev/video 26/src/components/Spinner.js similarity index 100% rename from rmtDev/video 24/src/components/Spinner.js rename to 03-rmtDev/video 26/src/components/Spinner.js diff --git a/rmtDev/video 25/index.css b/03-rmtDev/video 27/index.css similarity index 100% rename from rmtDev/video 25/index.css rename to 03-rmtDev/video 27/index.css diff --git a/rmtDev/video 25/index.html b/03-rmtDev/video 27/index.html similarity index 100% rename from rmtDev/video 25/index.html rename to 03-rmtDev/video 27/index.html diff --git a/rmtDev/video 27/index.js b/03-rmtDev/video 27/index.js similarity index 100% rename from rmtDev/video 27/index.js rename to 03-rmtDev/video 27/index.js diff --git a/rmtDev/video 27/src/common.js b/03-rmtDev/video 27/src/common.js similarity index 100% rename from rmtDev/video 27/src/common.js rename to 03-rmtDev/video 27/src/common.js diff --git a/rmtDev/video 25/src/components/Error.js b/03-rmtDev/video 27/src/components/Error.js similarity index 100% rename from rmtDev/video 25/src/components/Error.js rename to 03-rmtDev/video 27/src/components/Error.js diff --git a/rmtDev/video 27/src/components/JobDetails.js b/03-rmtDev/video 27/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 27/src/components/JobDetails.js rename to 03-rmtDev/video 27/src/components/JobDetails.js diff --git a/rmtDev/video 27/src/components/JobList.js b/03-rmtDev/video 27/src/components/JobList.js similarity index 100% rename from rmtDev/video 27/src/components/JobList.js rename to 03-rmtDev/video 27/src/components/JobList.js diff --git a/rmtDev/video 27/src/components/Search.js b/03-rmtDev/video 27/src/components/Search.js similarity index 100% rename from rmtDev/video 27/src/components/Search.js rename to 03-rmtDev/video 27/src/components/Search.js diff --git a/rmtDev/video 27/src/components/Sorting.js b/03-rmtDev/video 27/src/components/Sorting.js similarity index 100% rename from rmtDev/video 27/src/components/Sorting.js rename to 03-rmtDev/video 27/src/components/Sorting.js diff --git a/rmtDev/video 25/src/components/Spinner.js b/03-rmtDev/video 27/src/components/Spinner.js similarity index 100% rename from rmtDev/video 25/src/components/Spinner.js rename to 03-rmtDev/video 27/src/components/Spinner.js diff --git a/rmtDev/video 26/index.css b/03-rmtDev/video 28/index.css similarity index 100% rename from rmtDev/video 26/index.css rename to 03-rmtDev/video 28/index.css diff --git a/rmtDev/video 26/index.html b/03-rmtDev/video 28/index.html similarity index 100% rename from rmtDev/video 26/index.html rename to 03-rmtDev/video 28/index.html diff --git a/rmtDev/video 28/index.js b/03-rmtDev/video 28/index.js similarity index 100% rename from rmtDev/video 28/index.js rename to 03-rmtDev/video 28/index.js diff --git a/rmtDev/video 28/src/common.js b/03-rmtDev/video 28/src/common.js similarity index 100% rename from rmtDev/video 28/src/common.js rename to 03-rmtDev/video 28/src/common.js diff --git a/rmtDev/video 26/src/components/Error.js b/03-rmtDev/video 28/src/components/Error.js similarity index 100% rename from rmtDev/video 26/src/components/Error.js rename to 03-rmtDev/video 28/src/components/Error.js diff --git a/rmtDev/video 28/src/components/JobDetails.js b/03-rmtDev/video 28/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 28/src/components/JobDetails.js rename to 03-rmtDev/video 28/src/components/JobDetails.js diff --git a/rmtDev/video 28/src/components/JobList.js b/03-rmtDev/video 28/src/components/JobList.js similarity index 100% rename from rmtDev/video 28/src/components/JobList.js rename to 03-rmtDev/video 28/src/components/JobList.js diff --git a/rmtDev/video 28/src/components/Search.js b/03-rmtDev/video 28/src/components/Search.js similarity index 100% rename from rmtDev/video 28/src/components/Search.js rename to 03-rmtDev/video 28/src/components/Search.js diff --git a/rmtDev/video 28/src/components/Sorting.js b/03-rmtDev/video 28/src/components/Sorting.js similarity index 100% rename from rmtDev/video 28/src/components/Sorting.js rename to 03-rmtDev/video 28/src/components/Sorting.js diff --git a/rmtDev/video 26/src/components/Spinner.js b/03-rmtDev/video 28/src/components/Spinner.js similarity index 100% rename from rmtDev/video 26/src/components/Spinner.js rename to 03-rmtDev/video 28/src/components/Spinner.js diff --git a/rmtDev/video 27/index.css b/03-rmtDev/video 29/index.css similarity index 100% rename from rmtDev/video 27/index.css rename to 03-rmtDev/video 29/index.css diff --git a/rmtDev/video 27/index.html b/03-rmtDev/video 29/index.html similarity index 100% rename from rmtDev/video 27/index.html rename to 03-rmtDev/video 29/index.html diff --git a/rmtDev/video 29/index.js b/03-rmtDev/video 29/index.js similarity index 100% rename from rmtDev/video 29/index.js rename to 03-rmtDev/video 29/index.js diff --git a/rmtDev/video 29/src/common.js b/03-rmtDev/video 29/src/common.js similarity index 100% rename from rmtDev/video 29/src/common.js rename to 03-rmtDev/video 29/src/common.js diff --git a/rmtDev/video 27/src/components/Error.js b/03-rmtDev/video 29/src/components/Error.js similarity index 100% rename from rmtDev/video 27/src/components/Error.js rename to 03-rmtDev/video 29/src/components/Error.js diff --git a/rmtDev/video 29/src/components/JobDetails.js b/03-rmtDev/video 29/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 29/src/components/JobDetails.js rename to 03-rmtDev/video 29/src/components/JobDetails.js diff --git a/rmtDev/video 29/src/components/JobList.js b/03-rmtDev/video 29/src/components/JobList.js similarity index 100% rename from rmtDev/video 29/src/components/JobList.js rename to 03-rmtDev/video 29/src/components/JobList.js diff --git a/Supplemental/video 36/src/components/Pagination.js b/03-rmtDev/video 29/src/components/Pagination.js similarity index 100% rename from Supplemental/video 36/src/components/Pagination.js rename to 03-rmtDev/video 29/src/components/Pagination.js diff --git a/Supplemental/video 36/src/components/Search.js b/03-rmtDev/video 29/src/components/Search.js similarity index 100% rename from Supplemental/video 36/src/components/Search.js rename to 03-rmtDev/video 29/src/components/Search.js diff --git a/Supplemental/video 36/src/components/Sorting.js b/03-rmtDev/video 29/src/components/Sorting.js similarity index 100% rename from Supplemental/video 36/src/components/Sorting.js rename to 03-rmtDev/video 29/src/components/Sorting.js diff --git a/rmtDev/video 27/src/components/Spinner.js b/03-rmtDev/video 29/src/components/Spinner.js similarity index 100% rename from rmtDev/video 27/src/components/Spinner.js rename to 03-rmtDev/video 29/src/components/Spinner.js diff --git a/rmtDev/video 28/index.css b/03-rmtDev/video 30/index.css similarity index 100% rename from rmtDev/video 28/index.css rename to 03-rmtDev/video 30/index.css diff --git a/rmtDev/video 28/index.html b/03-rmtDev/video 30/index.html similarity index 100% rename from rmtDev/video 28/index.html rename to 03-rmtDev/video 30/index.html diff --git a/rmtDev/video 30/index.js b/03-rmtDev/video 30/index.js similarity index 100% rename from rmtDev/video 30/index.js rename to 03-rmtDev/video 30/index.js diff --git a/rmtDev/video 30/src/common.js b/03-rmtDev/video 30/src/common.js similarity index 100% rename from rmtDev/video 30/src/common.js rename to 03-rmtDev/video 30/src/common.js diff --git a/rmtDev/video 28/src/components/Error.js b/03-rmtDev/video 30/src/components/Error.js similarity index 100% rename from rmtDev/video 28/src/components/Error.js rename to 03-rmtDev/video 30/src/components/Error.js diff --git a/rmtDev/video 30/src/components/JobDetails.js b/03-rmtDev/video 30/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 30/src/components/JobDetails.js rename to 03-rmtDev/video 30/src/components/JobDetails.js diff --git a/rmtDev/video 30/src/components/JobList.js b/03-rmtDev/video 30/src/components/JobList.js similarity index 100% rename from rmtDev/video 30/src/components/JobList.js rename to 03-rmtDev/video 30/src/components/JobList.js diff --git a/Supplemental/video 38/src/components/Pagination.js b/03-rmtDev/video 30/src/components/Pagination.js similarity index 100% rename from Supplemental/video 38/src/components/Pagination.js rename to 03-rmtDev/video 30/src/components/Pagination.js diff --git a/rmtDev/video 30/src/components/Router.js b/03-rmtDev/video 30/src/components/Router.js similarity index 100% rename from rmtDev/video 30/src/components/Router.js rename to 03-rmtDev/video 30/src/components/Router.js diff --git a/rmtDev/video 29/src/components/Search.js b/03-rmtDev/video 30/src/components/Search.js similarity index 100% rename from rmtDev/video 29/src/components/Search.js rename to 03-rmtDev/video 30/src/components/Search.js diff --git a/Supplemental/video 38/src/components/Sorting.js b/03-rmtDev/video 30/src/components/Sorting.js similarity index 100% rename from Supplemental/video 38/src/components/Sorting.js rename to 03-rmtDev/video 30/src/components/Sorting.js diff --git a/rmtDev/video 28/src/components/Spinner.js b/03-rmtDev/video 30/src/components/Spinner.js similarity index 100% rename from rmtDev/video 28/src/components/Spinner.js rename to 03-rmtDev/video 30/src/components/Spinner.js diff --git a/rmtDev/video 29/index.css b/03-rmtDev/video 31/index.css similarity index 100% rename from rmtDev/video 29/index.css rename to 03-rmtDev/video 31/index.css diff --git a/rmtDev/video 29/index.html b/03-rmtDev/video 31/index.html similarity index 100% rename from rmtDev/video 29/index.html rename to 03-rmtDev/video 31/index.html diff --git a/rmtDev/video 31/index.js b/03-rmtDev/video 31/index.js similarity index 100% rename from rmtDev/video 31/index.js rename to 03-rmtDev/video 31/index.js diff --git a/rmtDev/video 31/src/common.js b/03-rmtDev/video 31/src/common.js similarity index 100% rename from rmtDev/video 31/src/common.js rename to 03-rmtDev/video 31/src/common.js diff --git a/rmtDev/video 29/src/components/Error.js b/03-rmtDev/video 31/src/components/Error.js similarity index 100% rename from rmtDev/video 29/src/components/Error.js rename to 03-rmtDev/video 31/src/components/Error.js diff --git a/rmtDev/video 31/src/components/JobDetails.js b/03-rmtDev/video 31/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 31/src/components/JobDetails.js rename to 03-rmtDev/video 31/src/components/JobDetails.js diff --git a/rmtDev/video 31/src/components/JobList.js b/03-rmtDev/video 31/src/components/JobList.js similarity index 100% rename from rmtDev/video 31/src/components/JobList.js rename to 03-rmtDev/video 31/src/components/JobList.js diff --git a/rmtDev/video 29/src/components/Pagination.js b/03-rmtDev/video 31/src/components/Pagination.js similarity index 100% rename from rmtDev/video 29/src/components/Pagination.js rename to 03-rmtDev/video 31/src/components/Pagination.js diff --git a/rmtDev/video 31/src/components/Router.js b/03-rmtDev/video 31/src/components/Router.js similarity index 100% rename from rmtDev/video 31/src/components/Router.js rename to 03-rmtDev/video 31/src/components/Router.js diff --git a/rmtDev/video 30/src/components/Search.js b/03-rmtDev/video 31/src/components/Search.js similarity index 100% rename from rmtDev/video 30/src/components/Search.js rename to 03-rmtDev/video 31/src/components/Search.js diff --git a/rmtDev/video 29/src/components/Sorting.js b/03-rmtDev/video 31/src/components/Sorting.js similarity index 100% rename from rmtDev/video 29/src/components/Sorting.js rename to 03-rmtDev/video 31/src/components/Sorting.js diff --git a/rmtDev/video 29/src/components/Spinner.js b/03-rmtDev/video 31/src/components/Spinner.js similarity index 100% rename from rmtDev/video 29/src/components/Spinner.js rename to 03-rmtDev/video 31/src/components/Spinner.js diff --git a/rmtDev/video 30/index.css b/03-rmtDev/video 32/index.css similarity index 100% rename from rmtDev/video 30/index.css rename to 03-rmtDev/video 32/index.css diff --git a/rmtDev/video 30/index.html b/03-rmtDev/video 32/index.html similarity index 100% rename from rmtDev/video 30/index.html rename to 03-rmtDev/video 32/index.html diff --git a/rmtDev/video 32/index.js b/03-rmtDev/video 32/index.js similarity index 100% rename from rmtDev/video 32/index.js rename to 03-rmtDev/video 32/index.js diff --git a/Supplemental/video 36/src/common.js b/03-rmtDev/video 32/src/common.js similarity index 100% rename from Supplemental/video 36/src/common.js rename to 03-rmtDev/video 32/src/common.js diff --git a/rmtDev/video 32/src/components/Bookmarks.js b/03-rmtDev/video 32/src/components/Bookmarks.js similarity index 100% rename from rmtDev/video 32/src/components/Bookmarks.js rename to 03-rmtDev/video 32/src/components/Bookmarks.js diff --git a/rmtDev/video 30/src/components/Error.js b/03-rmtDev/video 32/src/components/Error.js similarity index 100% rename from rmtDev/video 30/src/components/Error.js rename to 03-rmtDev/video 32/src/components/Error.js diff --git a/rmtDev/video 32/src/components/JobDetails.js b/03-rmtDev/video 32/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 32/src/components/JobDetails.js rename to 03-rmtDev/video 32/src/components/JobDetails.js diff --git a/rmtDev/video 32/src/components/JobList.js b/03-rmtDev/video 32/src/components/JobList.js similarity index 100% rename from rmtDev/video 32/src/components/JobList.js rename to 03-rmtDev/video 32/src/components/JobList.js diff --git a/rmtDev/video 30/src/components/Pagination.js b/03-rmtDev/video 32/src/components/Pagination.js similarity index 100% rename from rmtDev/video 30/src/components/Pagination.js rename to 03-rmtDev/video 32/src/components/Pagination.js diff --git a/rmtDev/video 32/src/components/Router.js b/03-rmtDev/video 32/src/components/Router.js similarity index 100% rename from rmtDev/video 32/src/components/Router.js rename to 03-rmtDev/video 32/src/components/Router.js diff --git a/rmtDev/video 31/src/components/Search.js b/03-rmtDev/video 32/src/components/Search.js similarity index 100% rename from rmtDev/video 31/src/components/Search.js rename to 03-rmtDev/video 32/src/components/Search.js diff --git a/rmtDev/video 30/src/components/Sorting.js b/03-rmtDev/video 32/src/components/Sorting.js similarity index 100% rename from rmtDev/video 30/src/components/Sorting.js rename to 03-rmtDev/video 32/src/components/Sorting.js diff --git a/rmtDev/video 30/src/components/Spinner.js b/03-rmtDev/video 32/src/components/Spinner.js similarity index 100% rename from rmtDev/video 30/src/components/Spinner.js rename to 03-rmtDev/video 32/src/components/Spinner.js diff --git a/rmtDev/video 31/index.css b/03-rmtDev/video 33/index.css similarity index 100% rename from rmtDev/video 31/index.css rename to 03-rmtDev/video 33/index.css diff --git a/rmtDev/video 31/index.html b/03-rmtDev/video 33/index.html similarity index 100% rename from rmtDev/video 31/index.html rename to 03-rmtDev/video 33/index.html diff --git a/rmtDev/video 33/index.js b/03-rmtDev/video 33/index.js similarity index 100% rename from rmtDev/video 33/index.js rename to 03-rmtDev/video 33/index.js diff --git a/rmtDev/video 32/src/common.js b/03-rmtDev/video 33/src/common.js similarity index 100% rename from rmtDev/video 32/src/common.js rename to 03-rmtDev/video 33/src/common.js diff --git a/rmtDev/video 33/src/components/Bookmarks.js b/03-rmtDev/video 33/src/components/Bookmarks.js similarity index 100% rename from rmtDev/video 33/src/components/Bookmarks.js rename to 03-rmtDev/video 33/src/components/Bookmarks.js diff --git a/rmtDev/video 31/src/components/Error.js b/03-rmtDev/video 33/src/components/Error.js similarity index 100% rename from rmtDev/video 31/src/components/Error.js rename to 03-rmtDev/video 33/src/components/Error.js diff --git a/rmtDev/video 33/src/components/JobDetails.js b/03-rmtDev/video 33/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 33/src/components/JobDetails.js rename to 03-rmtDev/video 33/src/components/JobDetails.js diff --git a/rmtDev/video 33/src/components/JobList.js b/03-rmtDev/video 33/src/components/JobList.js similarity index 100% rename from rmtDev/video 33/src/components/JobList.js rename to 03-rmtDev/video 33/src/components/JobList.js diff --git a/rmtDev/video 31/src/components/Pagination.js b/03-rmtDev/video 33/src/components/Pagination.js similarity index 100% rename from rmtDev/video 31/src/components/Pagination.js rename to 03-rmtDev/video 33/src/components/Pagination.js diff --git a/rmtDev/video 33/src/components/Router.js b/03-rmtDev/video 33/src/components/Router.js similarity index 100% rename from rmtDev/video 33/src/components/Router.js rename to 03-rmtDev/video 33/src/components/Router.js diff --git a/rmtDev/video 32/src/components/Search.js b/03-rmtDev/video 33/src/components/Search.js similarity index 100% rename from rmtDev/video 32/src/components/Search.js rename to 03-rmtDev/video 33/src/components/Search.js diff --git a/rmtDev/video 31/src/components/Sorting.js b/03-rmtDev/video 33/src/components/Sorting.js similarity index 100% rename from rmtDev/video 31/src/components/Sorting.js rename to 03-rmtDev/video 33/src/components/Sorting.js diff --git a/rmtDev/video 31/src/components/Spinner.js b/03-rmtDev/video 33/src/components/Spinner.js similarity index 100% rename from rmtDev/video 31/src/components/Spinner.js rename to 03-rmtDev/video 33/src/components/Spinner.js diff --git a/Supplemental/video 36/src/components/Storage.js b/03-rmtDev/video 33/src/components/Storage.js similarity index 100% rename from Supplemental/video 36/src/components/Storage.js rename to 03-rmtDev/video 33/src/components/Storage.js diff --git a/rmtDev/video 32/index.css b/03-rmtDev/video 34/index.css similarity index 100% rename from rmtDev/video 32/index.css rename to 03-rmtDev/video 34/index.css diff --git a/rmtDev/video 32/index.html b/03-rmtDev/video 34/index.html similarity index 100% rename from rmtDev/video 32/index.html rename to 03-rmtDev/video 34/index.html diff --git a/rmtDev/video 34/index.js b/03-rmtDev/video 34/index.js similarity index 100% rename from rmtDev/video 34/index.js rename to 03-rmtDev/video 34/index.js diff --git a/rmtDev/video 33/src/common.js b/03-rmtDev/video 34/src/common.js similarity index 100% rename from rmtDev/video 33/src/common.js rename to 03-rmtDev/video 34/src/common.js diff --git a/Supplemental/video 36/src/components/Bookmarks.js b/03-rmtDev/video 34/src/components/Bookmarks.js similarity index 100% rename from Supplemental/video 36/src/components/Bookmarks.js rename to 03-rmtDev/video 34/src/components/Bookmarks.js diff --git a/rmtDev/video 32/src/components/Error.js b/03-rmtDev/video 34/src/components/Error.js similarity index 100% rename from rmtDev/video 32/src/components/Error.js rename to 03-rmtDev/video 34/src/components/Error.js diff --git a/Supplemental/video 36/src/components/JobDetails.js b/03-rmtDev/video 34/src/components/JobDetails.js similarity index 100% rename from Supplemental/video 36/src/components/JobDetails.js rename to 03-rmtDev/video 34/src/components/JobDetails.js diff --git a/Supplemental/video 36/src/components/JobList.js b/03-rmtDev/video 34/src/components/JobList.js similarity index 100% rename from Supplemental/video 36/src/components/JobList.js rename to 03-rmtDev/video 34/src/components/JobList.js diff --git a/rmtDev/video 32/src/components/Pagination.js b/03-rmtDev/video 34/src/components/Pagination.js similarity index 100% rename from rmtDev/video 32/src/components/Pagination.js rename to 03-rmtDev/video 34/src/components/Pagination.js diff --git a/Supplemental/video 36/src/components/Router.js b/03-rmtDev/video 34/src/components/Router.js similarity index 100% rename from Supplemental/video 36/src/components/Router.js rename to 03-rmtDev/video 34/src/components/Router.js diff --git a/rmtDev/video 33/src/components/Search.js b/03-rmtDev/video 34/src/components/Search.js similarity index 100% rename from rmtDev/video 33/src/components/Search.js rename to 03-rmtDev/video 34/src/components/Search.js diff --git a/rmtDev/video 32/src/components/Sorting.js b/03-rmtDev/video 34/src/components/Sorting.js similarity index 100% rename from rmtDev/video 32/src/components/Sorting.js rename to 03-rmtDev/video 34/src/components/Sorting.js diff --git a/rmtDev/video 32/src/components/Spinner.js b/03-rmtDev/video 34/src/components/Spinner.js similarity index 100% rename from rmtDev/video 32/src/components/Spinner.js rename to 03-rmtDev/video 34/src/components/Spinner.js diff --git a/Supplemental/video 38/src/components/Storage.js b/03-rmtDev/video 34/src/components/Storage.js similarity index 100% rename from Supplemental/video 38/src/components/Storage.js rename to 03-rmtDev/video 34/src/components/Storage.js diff --git a/Supplemental/video 36/babel.config.js b/03-rmtDev/video 35/babel.config.js similarity index 100% rename from Supplemental/video 36/babel.config.js rename to 03-rmtDev/video 35/babel.config.js diff --git a/Supplemental/video 36/dist/index.html b/03-rmtDev/video 35/dist/index.html similarity index 100% rename from Supplemental/video 36/dist/index.html rename to 03-rmtDev/video 35/dist/index.html diff --git a/Supplemental/video 36/dist/main.css b/03-rmtDev/video 35/dist/main.css similarity index 100% rename from Supplemental/video 36/dist/main.css rename to 03-rmtDev/video 35/dist/main.css diff --git a/Supplemental/video 36/dist/main.js b/03-rmtDev/video 35/dist/main.js similarity index 100% rename from Supplemental/video 36/dist/main.js rename to 03-rmtDev/video 35/dist/main.js diff --git a/Supplemental/video 36/dist/main.js.LICENSE.txt b/03-rmtDev/video 35/dist/main.js.LICENSE.txt similarity index 100% rename from Supplemental/video 36/dist/main.js.LICENSE.txt rename to 03-rmtDev/video 35/dist/main.js.LICENSE.txt diff --git a/Supplemental/video 36/package-lock.json b/03-rmtDev/video 35/package-lock.json similarity index 100% rename from Supplemental/video 36/package-lock.json rename to 03-rmtDev/video 35/package-lock.json diff --git a/Supplemental/video 36/package.json b/03-rmtDev/video 35/package.json similarity index 100% rename from Supplemental/video 36/package.json rename to 03-rmtDev/video 35/package.json diff --git a/Supplemental/video 36/postcss.config.js b/03-rmtDev/video 35/postcss.config.js similarity index 100% rename from Supplemental/video 36/postcss.config.js rename to 03-rmtDev/video 35/postcss.config.js diff --git a/rmtDev/video 34/src/common.js b/03-rmtDev/video 35/src/common.js similarity index 100% rename from rmtDev/video 34/src/common.js rename to 03-rmtDev/video 35/src/common.js diff --git a/Supplemental/video 38/src/components/Bookmarks.js b/03-rmtDev/video 35/src/components/Bookmarks.js similarity index 100% rename from Supplemental/video 38/src/components/Bookmarks.js rename to 03-rmtDev/video 35/src/components/Bookmarks.js diff --git a/rmtDev/video 33/src/components/Error.js b/03-rmtDev/video 35/src/components/Error.js similarity index 100% rename from rmtDev/video 33/src/components/Error.js rename to 03-rmtDev/video 35/src/components/Error.js diff --git a/Supplemental/video 38/src/components/JobDetails.js b/03-rmtDev/video 35/src/components/JobDetails.js similarity index 100% rename from Supplemental/video 38/src/components/JobDetails.js rename to 03-rmtDev/video 35/src/components/JobDetails.js diff --git a/rmtDev/video 34/src/components/JobList.js b/03-rmtDev/video 35/src/components/JobList.js similarity index 100% rename from rmtDev/video 34/src/components/JobList.js rename to 03-rmtDev/video 35/src/components/JobList.js diff --git a/rmtDev/video 33/src/components/Pagination.js b/03-rmtDev/video 35/src/components/Pagination.js similarity index 100% rename from rmtDev/video 33/src/components/Pagination.js rename to 03-rmtDev/video 35/src/components/Pagination.js diff --git a/Supplemental/video 38/src/components/Router.js b/03-rmtDev/video 35/src/components/Router.js similarity index 100% rename from Supplemental/video 38/src/components/Router.js rename to 03-rmtDev/video 35/src/components/Router.js diff --git a/rmtDev/video 34/src/components/Search.js b/03-rmtDev/video 35/src/components/Search.js similarity index 100% rename from rmtDev/video 34/src/components/Search.js rename to 03-rmtDev/video 35/src/components/Search.js diff --git a/rmtDev/video 33/src/components/Sorting.js b/03-rmtDev/video 35/src/components/Sorting.js similarity index 100% rename from rmtDev/video 33/src/components/Sorting.js rename to 03-rmtDev/video 35/src/components/Sorting.js diff --git a/rmtDev/video 33/src/components/Spinner.js b/03-rmtDev/video 35/src/components/Spinner.js similarity index 100% rename from rmtDev/video 33/src/components/Spinner.js rename to 03-rmtDev/video 35/src/components/Spinner.js diff --git a/rmtDev/video 33/src/components/Storage.js b/03-rmtDev/video 35/src/components/Storage.js similarity index 100% rename from rmtDev/video 33/src/components/Storage.js rename to 03-rmtDev/video 35/src/components/Storage.js diff --git a/rmtDev/video 33/index.css b/03-rmtDev/video 35/src/index.css similarity index 100% rename from rmtDev/video 33/index.css rename to 03-rmtDev/video 35/src/index.css diff --git a/rmtDev/video 33/index.html b/03-rmtDev/video 35/src/index.html similarity index 100% rename from rmtDev/video 33/index.html rename to 03-rmtDev/video 35/src/index.html diff --git a/Supplemental/video 36/src/index.js b/03-rmtDev/video 35/src/index.js similarity index 100% rename from Supplemental/video 36/src/index.js rename to 03-rmtDev/video 35/src/index.js diff --git a/Supplemental/video 36/webpack.config.js b/03-rmtDev/video 35/webpack.config.js similarity index 100% rename from Supplemental/video 36/webpack.config.js rename to 03-rmtDev/video 35/webpack.config.js diff --git a/Supplemental/video 38/babel.config.js b/04-Supplemental/video 36/babel.config.js similarity index 100% rename from Supplemental/video 38/babel.config.js rename to 04-Supplemental/video 36/babel.config.js diff --git a/Supplemental/video 38/dist/index.html b/04-Supplemental/video 36/dist/index.html similarity index 100% rename from Supplemental/video 38/dist/index.html rename to 04-Supplemental/video 36/dist/index.html diff --git a/Supplemental/video 38/dist/main.css b/04-Supplemental/video 36/dist/main.css similarity index 100% rename from Supplemental/video 38/dist/main.css rename to 04-Supplemental/video 36/dist/main.css diff --git a/Supplemental/video 38/dist/main.js b/04-Supplemental/video 36/dist/main.js similarity index 100% rename from Supplemental/video 38/dist/main.js rename to 04-Supplemental/video 36/dist/main.js diff --git a/Supplemental/video 38/dist/main.js.LICENSE.txt b/04-Supplemental/video 36/dist/main.js.LICENSE.txt similarity index 100% rename from Supplemental/video 38/dist/main.js.LICENSE.txt rename to 04-Supplemental/video 36/dist/main.js.LICENSE.txt diff --git a/Supplemental/video 38/package-lock.json b/04-Supplemental/video 36/package-lock.json similarity index 100% rename from Supplemental/video 38/package-lock.json rename to 04-Supplemental/video 36/package-lock.json diff --git a/Supplemental/video 38/package.json b/04-Supplemental/video 36/package.json similarity index 100% rename from Supplemental/video 38/package.json rename to 04-Supplemental/video 36/package.json diff --git a/Supplemental/video 38/postcss.config.js b/04-Supplemental/video 36/postcss.config.js similarity index 100% rename from Supplemental/video 38/postcss.config.js rename to 04-Supplemental/video 36/postcss.config.js diff --git a/rmtDev/video 35/src/common.js b/04-Supplemental/video 36/src/common.js similarity index 100% rename from rmtDev/video 35/src/common.js rename to 04-Supplemental/video 36/src/common.js diff --git a/rmtDev/video 34/src/components/Bookmarks.js b/04-Supplemental/video 36/src/components/Bookmarks.js similarity index 100% rename from rmtDev/video 34/src/components/Bookmarks.js rename to 04-Supplemental/video 36/src/components/Bookmarks.js diff --git a/rmtDev/video 34/src/components/Error.js b/04-Supplemental/video 36/src/components/Error.js similarity index 100% rename from rmtDev/video 34/src/components/Error.js rename to 04-Supplemental/video 36/src/components/Error.js diff --git a/rmtDev/video 34/src/components/JobDetails.js b/04-Supplemental/video 36/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 34/src/components/JobDetails.js rename to 04-Supplemental/video 36/src/components/JobDetails.js diff --git a/rmtDev/video 35/src/components/JobList.js b/04-Supplemental/video 36/src/components/JobList.js similarity index 100% rename from rmtDev/video 35/src/components/JobList.js rename to 04-Supplemental/video 36/src/components/JobList.js diff --git a/rmtDev/video 34/src/components/Pagination.js b/04-Supplemental/video 36/src/components/Pagination.js similarity index 100% rename from rmtDev/video 34/src/components/Pagination.js rename to 04-Supplemental/video 36/src/components/Pagination.js diff --git a/rmtDev/video 34/src/components/Router.js b/04-Supplemental/video 36/src/components/Router.js similarity index 100% rename from rmtDev/video 34/src/components/Router.js rename to 04-Supplemental/video 36/src/components/Router.js diff --git a/rmtDev/video 35/src/components/Search.js b/04-Supplemental/video 36/src/components/Search.js similarity index 100% rename from rmtDev/video 35/src/components/Search.js rename to 04-Supplemental/video 36/src/components/Search.js diff --git a/rmtDev/video 34/src/components/Sorting.js b/04-Supplemental/video 36/src/components/Sorting.js similarity index 100% rename from rmtDev/video 34/src/components/Sorting.js rename to 04-Supplemental/video 36/src/components/Sorting.js diff --git a/rmtDev/video 34/src/components/Spinner.js b/04-Supplemental/video 36/src/components/Spinner.js similarity index 100% rename from rmtDev/video 34/src/components/Spinner.js rename to 04-Supplemental/video 36/src/components/Spinner.js diff --git a/rmtDev/video 34/src/components/Storage.js b/04-Supplemental/video 36/src/components/Storage.js similarity index 100% rename from rmtDev/video 34/src/components/Storage.js rename to 04-Supplemental/video 36/src/components/Storage.js diff --git a/rmtDev/video 34/index.css b/04-Supplemental/video 36/src/index.css similarity index 100% rename from rmtDev/video 34/index.css rename to 04-Supplemental/video 36/src/index.css diff --git a/rmtDev/video 34/index.html b/04-Supplemental/video 36/src/index.html similarity index 100% rename from rmtDev/video 34/index.html rename to 04-Supplemental/video 36/src/index.html diff --git a/rmtDev/video 35/src/index.js b/04-Supplemental/video 36/src/index.js similarity index 100% rename from rmtDev/video 35/src/index.js rename to 04-Supplemental/video 36/src/index.js diff --git a/Supplemental/video 38/webpack.config.js b/04-Supplemental/video 36/webpack.config.js similarity index 100% rename from Supplemental/video 38/webpack.config.js rename to 04-Supplemental/video 36/webpack.config.js diff --git a/Supplemental/video 37/index.html b/04-Supplemental/video 37/index.html similarity index 100% rename from Supplemental/video 37/index.html rename to 04-Supplemental/video 37/index.html diff --git a/Supplemental/video 37/script.js b/04-Supplemental/video 37/script.js similarity index 100% rename from Supplemental/video 37/script.js rename to 04-Supplemental/video 37/script.js diff --git a/Supplemental/video 37/style.css b/04-Supplemental/video 37/style.css similarity index 100% rename from Supplemental/video 37/style.css rename to 04-Supplemental/video 37/style.css diff --git a/rmtDev/video 35/babel.config.js b/04-Supplemental/video 38/babel.config.js similarity index 100% rename from rmtDev/video 35/babel.config.js rename to 04-Supplemental/video 38/babel.config.js diff --git a/rmtDev/video 35/dist/index.html b/04-Supplemental/video 38/dist/index.html similarity index 100% rename from rmtDev/video 35/dist/index.html rename to 04-Supplemental/video 38/dist/index.html diff --git a/rmtDev/video 35/dist/main.css b/04-Supplemental/video 38/dist/main.css similarity index 100% rename from rmtDev/video 35/dist/main.css rename to 04-Supplemental/video 38/dist/main.css diff --git a/rmtDev/video 35/dist/main.js b/04-Supplemental/video 38/dist/main.js similarity index 100% rename from rmtDev/video 35/dist/main.js rename to 04-Supplemental/video 38/dist/main.js diff --git a/rmtDev/video 35/dist/main.js.LICENSE.txt b/04-Supplemental/video 38/dist/main.js.LICENSE.txt similarity index 100% rename from rmtDev/video 35/dist/main.js.LICENSE.txt rename to 04-Supplemental/video 38/dist/main.js.LICENSE.txt diff --git a/rmtDev/video 35/package-lock.json b/04-Supplemental/video 38/package-lock.json similarity index 100% rename from rmtDev/video 35/package-lock.json rename to 04-Supplemental/video 38/package-lock.json diff --git a/rmtDev/video 35/package.json b/04-Supplemental/video 38/package.json similarity index 100% rename from rmtDev/video 35/package.json rename to 04-Supplemental/video 38/package.json diff --git a/rmtDev/video 35/postcss.config.js b/04-Supplemental/video 38/postcss.config.js similarity index 100% rename from rmtDev/video 35/postcss.config.js rename to 04-Supplemental/video 38/postcss.config.js diff --git a/Supplemental/video 38/src/common.js b/04-Supplemental/video 38/src/common.js similarity index 100% rename from Supplemental/video 38/src/common.js rename to 04-Supplemental/video 38/src/common.js diff --git a/rmtDev/video 35/src/components/Bookmarks.js b/04-Supplemental/video 38/src/components/Bookmarks.js similarity index 100% rename from rmtDev/video 35/src/components/Bookmarks.js rename to 04-Supplemental/video 38/src/components/Bookmarks.js diff --git a/rmtDev/video 35/src/components/Error.js b/04-Supplemental/video 38/src/components/Error.js similarity index 100% rename from rmtDev/video 35/src/components/Error.js rename to 04-Supplemental/video 38/src/components/Error.js diff --git a/rmtDev/video 35/src/components/JobDetails.js b/04-Supplemental/video 38/src/components/JobDetails.js similarity index 100% rename from rmtDev/video 35/src/components/JobDetails.js rename to 04-Supplemental/video 38/src/components/JobDetails.js diff --git a/Supplemental/video 38/src/components/JobList.js b/04-Supplemental/video 38/src/components/JobList.js similarity index 100% rename from Supplemental/video 38/src/components/JobList.js rename to 04-Supplemental/video 38/src/components/JobList.js diff --git a/rmtDev/video 35/src/components/Pagination.js b/04-Supplemental/video 38/src/components/Pagination.js similarity index 100% rename from rmtDev/video 35/src/components/Pagination.js rename to 04-Supplemental/video 38/src/components/Pagination.js diff --git a/rmtDev/video 35/src/components/Router.js b/04-Supplemental/video 38/src/components/Router.js similarity index 100% rename from rmtDev/video 35/src/components/Router.js rename to 04-Supplemental/video 38/src/components/Router.js diff --git a/Supplemental/video 38/src/components/Search.js b/04-Supplemental/video 38/src/components/Search.js similarity index 100% rename from Supplemental/video 38/src/components/Search.js rename to 04-Supplemental/video 38/src/components/Search.js diff --git a/rmtDev/video 35/src/components/Sorting.js b/04-Supplemental/video 38/src/components/Sorting.js similarity index 100% rename from rmtDev/video 35/src/components/Sorting.js rename to 04-Supplemental/video 38/src/components/Sorting.js diff --git a/rmtDev/video 35/src/components/Spinner.js b/04-Supplemental/video 38/src/components/Spinner.js similarity index 100% rename from rmtDev/video 35/src/components/Spinner.js rename to 04-Supplemental/video 38/src/components/Spinner.js diff --git a/rmtDev/video 35/src/components/Storage.js b/04-Supplemental/video 38/src/components/Storage.js similarity index 100% rename from rmtDev/video 35/src/components/Storage.js rename to 04-Supplemental/video 38/src/components/Storage.js diff --git a/rmtDev/video 35/src/index.css b/04-Supplemental/video 38/src/index.css similarity index 100% rename from rmtDev/video 35/src/index.css rename to 04-Supplemental/video 38/src/index.css diff --git a/rmtDev/video 35/src/index.html b/04-Supplemental/video 38/src/index.html similarity index 100% rename from rmtDev/video 35/src/index.html rename to 04-Supplemental/video 38/src/index.html diff --git a/Supplemental/video 38/src/index.js b/04-Supplemental/video 38/src/index.js similarity index 100% rename from Supplemental/video 38/src/index.js rename to 04-Supplemental/video 38/src/index.js diff --git a/rmtDev/video 35/webpack.config.js b/04-Supplemental/video 38/webpack.config.js similarity index 100% rename from rmtDev/video 35/webpack.config.js rename to 04-Supplemental/video 38/webpack.config.js From 0ba3f8ac7d8d87f07ef701a78b4479efd2cff6c6 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Fri, 10 Jun 2022 12:43:19 +0200 Subject: [PATCH 09/40] Format folder names --- .../index.html | 0 .../script.js | 0 .../style.css | 0 {02-CorpComment => 02 - CorpComment}/video 04/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 04/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 04/style.css | 0 {02-CorpComment => 02 - CorpComment}/video 05/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 05/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 05/style.css | 0 {02-CorpComment => 02 - CorpComment}/video 06/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 06/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 06/style.css | 0 {02-CorpComment => 02 - CorpComment}/video 07/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 07/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 07/style.css | 0 {02-CorpComment => 02 - CorpComment}/video 08/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 08/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 08/style.css | 0 {02-CorpComment => 02 - CorpComment}/video 09/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 09/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 09/style.css | 0 {02-CorpComment => 02 - CorpComment}/video 10/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 10/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 10/style.css | 0 {02-CorpComment => 02 - CorpComment}/video 11/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 11/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 11/style.css | 0 {02-CorpComment => 02 - CorpComment}/video 12/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 12/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 12/style.css | 0 {02-CorpComment => 02 - CorpComment}/video 13/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 13/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 13/style.css | 0 {02-CorpComment => 02 - CorpComment}/video 14/index.html | 0 {02-CorpComment => 02 - CorpComment}/video 14/script.js | 0 {02-CorpComment => 02 - CorpComment}/video 14/style.css | 0 {03-rmtDev => 03 - rmtDev}/video 16/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 16/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 16/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 17/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 17/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 17/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 18/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 18/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 18/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 19/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 19/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 19/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 19/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 19/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 19/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 19/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 19/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 20/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 20/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 20/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 20/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 20/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 20/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 20/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 20/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 21/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 21/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 21/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 21/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 21/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 21/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 21/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 21/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 22/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 22/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 22/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 22/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 22/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 22/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 22/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 22/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 22/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 23/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 23/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 23/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 23/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 23/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 23/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 23/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 23/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 23/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 24/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 24/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 24/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 24/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 24/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 24/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 24/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 24/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 24/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 25/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 25/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 25/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 25/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 25/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 25/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 25/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 25/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 25/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 26/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 26/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 26/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 26/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 26/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 26/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 26/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 26/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 26/src/components/Sorting.js | 0 {03-rmtDev => 03 - rmtDev}/video 26/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 27/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 27/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 27/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 27/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 27/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 27/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 27/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 27/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 27/src/components/Sorting.js | 0 {03-rmtDev => 03 - rmtDev}/video 27/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 28/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 28/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 28/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 28/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 28/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 28/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 28/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 28/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 28/src/components/Sorting.js | 0 {03-rmtDev => 03 - rmtDev}/video 28/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 29/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 29/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 29/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 29/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 29/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 29/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 29/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 29/src/components/Pagination.js | 0 {03-rmtDev => 03 - rmtDev}/video 29/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 29/src/components/Sorting.js | 0 {03-rmtDev => 03 - rmtDev}/video 29/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 30/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 30/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 30/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 30/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 30/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 30/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 30/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 30/src/components/Pagination.js | 0 {03-rmtDev => 03 - rmtDev}/video 30/src/components/Router.js | 0 {03-rmtDev => 03 - rmtDev}/video 30/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 30/src/components/Sorting.js | 0 {03-rmtDev => 03 - rmtDev}/video 30/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 31/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 31/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 31/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 31/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 31/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 31/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 31/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 31/src/components/Pagination.js | 0 {03-rmtDev => 03 - rmtDev}/video 31/src/components/Router.js | 0 {03-rmtDev => 03 - rmtDev}/video 31/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 31/src/components/Sorting.js | 0 {03-rmtDev => 03 - rmtDev}/video 31/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 32/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 32/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/src/components/Bookmarks.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/src/components/Pagination.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/src/components/Router.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/src/components/Sorting.js | 0 {03-rmtDev => 03 - rmtDev}/video 32/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 33/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 33/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/components/Bookmarks.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/components/Pagination.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/components/Router.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/components/Sorting.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 33/src/components/Storage.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 34/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 34/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/components/Bookmarks.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/components/Pagination.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/components/Router.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/components/Sorting.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 34/src/components/Storage.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/babel.config.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/dist/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 35/dist/main.css | 0 {03-rmtDev => 03 - rmtDev}/video 35/dist/main.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/dist/main.js.LICENSE.txt | 0 {03-rmtDev => 03 - rmtDev}/video 35/package-lock.json | 0 {03-rmtDev => 03 - rmtDev}/video 35/package.json | 0 {03-rmtDev => 03 - rmtDev}/video 35/postcss.config.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/common.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/components/Bookmarks.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/components/Error.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/components/JobDetails.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/components/JobList.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/components/Pagination.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/components/Router.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/components/Search.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/components/Sorting.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/components/Spinner.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/components/Storage.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/index.css | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/index.html | 0 {03-rmtDev => 03 - rmtDev}/video 35/src/index.js | 0 {03-rmtDev => 03 - rmtDev}/video 35/webpack.config.js | 0 {04-Supplemental => 04 - Supplemental}/video 36/babel.config.js | 0 {04-Supplemental => 04 - Supplemental}/video 36/dist/index.html | 0 {04-Supplemental => 04 - Supplemental}/video 36/dist/main.css | 0 {04-Supplemental => 04 - Supplemental}/video 36/dist/main.js | 0 .../video 36/dist/main.js.LICENSE.txt | 0 {04-Supplemental => 04 - Supplemental}/video 36/package-lock.json | 0 {04-Supplemental => 04 - Supplemental}/video 36/package.json | 0 {04-Supplemental => 04 - Supplemental}/video 36/postcss.config.js | 0 {04-Supplemental => 04 - Supplemental}/video 36/src/common.js | 0 .../video 36/src/components/Bookmarks.js | 0 .../video 36/src/components/Error.js | 0 .../video 36/src/components/JobDetails.js | 0 .../video 36/src/components/JobList.js | 0 .../video 36/src/components/Pagination.js | 0 .../video 36/src/components/Router.js | 0 .../video 36/src/components/Search.js | 0 .../video 36/src/components/Sorting.js | 0 .../video 36/src/components/Spinner.js | 0 .../video 36/src/components/Storage.js | 0 {04-Supplemental => 04 - Supplemental}/video 36/src/index.css | 0 {04-Supplemental => 04 - Supplemental}/video 36/src/index.html | 0 {04-Supplemental => 04 - Supplemental}/video 36/src/index.js | 0 {04-Supplemental => 04 - Supplemental}/video 36/webpack.config.js | 0 {04-Supplemental => 04 - Supplemental}/video 37/index.html | 0 {04-Supplemental => 04 - Supplemental}/video 37/script.js | 0 {04-Supplemental => 04 - Supplemental}/video 37/style.css | 0 {04-Supplemental => 04 - Supplemental}/video 38/babel.config.js | 0 {04-Supplemental => 04 - Supplemental}/video 38/dist/index.html | 0 {04-Supplemental => 04 - Supplemental}/video 38/dist/main.css | 0 {04-Supplemental => 04 - Supplemental}/video 38/dist/main.js | 0 .../video 38/dist/main.js.LICENSE.txt | 0 {04-Supplemental => 04 - Supplemental}/video 38/package-lock.json | 0 {04-Supplemental => 04 - Supplemental}/video 38/package.json | 0 {04-Supplemental => 04 - Supplemental}/video 38/postcss.config.js | 0 {04-Supplemental => 04 - Supplemental}/video 38/src/common.js | 0 .../video 38/src/components/Bookmarks.js | 0 .../video 38/src/components/Error.js | 0 .../video 38/src/components/JobDetails.js | 0 .../video 38/src/components/JobList.js | 0 .../video 38/src/components/Pagination.js | 0 .../video 38/src/components/Router.js | 0 .../video 38/src/components/Search.js | 0 .../video 38/src/components/Sorting.js | 0 .../video 38/src/components/Spinner.js | 0 .../video 38/src/components/Storage.js | 0 {04-Supplemental => 04 - Supplemental}/video 38/src/index.css | 0 {04-Supplemental => 04 - Supplemental}/video 38/src/index.html | 0 {04-Supplemental => 04 - Supplemental}/video 38/src/index.js | 0 {04-Supplemental => 04 - Supplemental}/video 38/webpack.config.js | 0 283 files changed, 0 insertions(+), 0 deletions(-) rename {01-Modern JS Fundamentals => 01 - Modern JS Fundamentals}/index.html (100%) rename {01-Modern JS Fundamentals => 01 - Modern JS Fundamentals}/script.js (100%) rename {01-Modern JS Fundamentals => 01 - Modern JS Fundamentals}/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 04/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 04/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 04/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 05/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 05/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 05/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 06/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 06/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 06/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 07/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 07/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 07/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 08/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 08/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 08/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 09/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 09/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 09/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 10/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 10/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 10/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 11/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 11/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 11/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 12/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 12/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 12/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 13/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 13/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 13/style.css (100%) rename {02-CorpComment => 02 - CorpComment}/video 14/index.html (100%) rename {02-CorpComment => 02 - CorpComment}/video 14/script.js (100%) rename {02-CorpComment => 02 - CorpComment}/video 14/style.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 16/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 16/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 16/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 17/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 17/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 17/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 18/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 18/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 18/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 19/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 19/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 19/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 19/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 19/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 19/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 19/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 19/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 20/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 20/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 20/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 20/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 20/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 20/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 20/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 20/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 21/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 21/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 21/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 21/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 21/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 21/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 21/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 21/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 22/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 22/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 22/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 22/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 22/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 22/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 22/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 22/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 22/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 23/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 23/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 23/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 23/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 23/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 23/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 23/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 23/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 23/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 24/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 24/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 24/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 24/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 24/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 24/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 24/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 24/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 24/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 25/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 25/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 25/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 25/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 25/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 25/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 25/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 25/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 25/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 26/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 26/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 26/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 26/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 26/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 26/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 26/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 26/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 26/src/components/Sorting.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 26/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 27/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 27/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 27/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 27/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 27/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 27/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 27/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 27/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 27/src/components/Sorting.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 27/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 28/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 28/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 28/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 28/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 28/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 28/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 28/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 28/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 28/src/components/Sorting.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 28/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/src/components/Pagination.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/src/components/Sorting.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 29/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/src/components/Pagination.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/src/components/Router.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/src/components/Sorting.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 30/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/src/components/Pagination.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/src/components/Router.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/src/components/Sorting.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 31/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/src/components/Bookmarks.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/src/components/Pagination.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/src/components/Router.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/src/components/Sorting.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 32/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/components/Bookmarks.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/components/Pagination.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/components/Router.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/components/Sorting.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 33/src/components/Storage.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/components/Bookmarks.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/components/Pagination.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/components/Router.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/components/Sorting.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 34/src/components/Storage.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/babel.config.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/dist/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/dist/main.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/dist/main.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/dist/main.js.LICENSE.txt (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/package-lock.json (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/package.json (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/postcss.config.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/common.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/components/Bookmarks.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/components/Error.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/components/JobDetails.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/components/JobList.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/components/Pagination.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/components/Router.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/components/Search.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/components/Sorting.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/components/Spinner.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/components/Storage.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/index.css (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/index.html (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/src/index.js (100%) rename {03-rmtDev => 03 - rmtDev}/video 35/webpack.config.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/babel.config.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/dist/index.html (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/dist/main.css (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/dist/main.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/dist/main.js.LICENSE.txt (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/package-lock.json (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/package.json (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/postcss.config.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/common.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/components/Bookmarks.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/components/Error.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/components/JobDetails.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/components/JobList.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/components/Pagination.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/components/Router.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/components/Search.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/components/Sorting.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/components/Spinner.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/components/Storage.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/index.css (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/index.html (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/src/index.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 36/webpack.config.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 37/index.html (100%) rename {04-Supplemental => 04 - Supplemental}/video 37/script.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 37/style.css (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/babel.config.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/dist/index.html (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/dist/main.css (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/dist/main.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/dist/main.js.LICENSE.txt (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/package-lock.json (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/package.json (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/postcss.config.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/common.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/components/Bookmarks.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/components/Error.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/components/JobDetails.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/components/JobList.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/components/Pagination.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/components/Router.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/components/Search.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/components/Sorting.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/components/Spinner.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/components/Storage.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/index.css (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/index.html (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/src/index.js (100%) rename {04-Supplemental => 04 - Supplemental}/video 38/webpack.config.js (100%) diff --git a/01-Modern JS Fundamentals/index.html b/01 - Modern JS Fundamentals/index.html similarity index 100% rename from 01-Modern JS Fundamentals/index.html rename to 01 - Modern JS Fundamentals/index.html diff --git a/01-Modern JS Fundamentals/script.js b/01 - Modern JS Fundamentals/script.js similarity index 100% rename from 01-Modern JS Fundamentals/script.js rename to 01 - Modern JS Fundamentals/script.js diff --git a/01-Modern JS Fundamentals/style.css b/01 - Modern JS Fundamentals/style.css similarity index 100% rename from 01-Modern JS Fundamentals/style.css rename to 01 - Modern JS Fundamentals/style.css diff --git a/02-CorpComment/video 04/index.html b/02 - CorpComment/video 04/index.html similarity index 100% rename from 02-CorpComment/video 04/index.html rename to 02 - CorpComment/video 04/index.html diff --git a/02-CorpComment/video 04/script.js b/02 - CorpComment/video 04/script.js similarity index 100% rename from 02-CorpComment/video 04/script.js rename to 02 - CorpComment/video 04/script.js diff --git a/02-CorpComment/video 04/style.css b/02 - CorpComment/video 04/style.css similarity index 100% rename from 02-CorpComment/video 04/style.css rename to 02 - CorpComment/video 04/style.css diff --git a/02-CorpComment/video 05/index.html b/02 - CorpComment/video 05/index.html similarity index 100% rename from 02-CorpComment/video 05/index.html rename to 02 - CorpComment/video 05/index.html diff --git a/02-CorpComment/video 05/script.js b/02 - CorpComment/video 05/script.js similarity index 100% rename from 02-CorpComment/video 05/script.js rename to 02 - CorpComment/video 05/script.js diff --git a/02-CorpComment/video 05/style.css b/02 - CorpComment/video 05/style.css similarity index 100% rename from 02-CorpComment/video 05/style.css rename to 02 - CorpComment/video 05/style.css diff --git a/02-CorpComment/video 06/index.html b/02 - CorpComment/video 06/index.html similarity index 100% rename from 02-CorpComment/video 06/index.html rename to 02 - CorpComment/video 06/index.html diff --git a/02-CorpComment/video 06/script.js b/02 - CorpComment/video 06/script.js similarity index 100% rename from 02-CorpComment/video 06/script.js rename to 02 - CorpComment/video 06/script.js diff --git a/02-CorpComment/video 06/style.css b/02 - CorpComment/video 06/style.css similarity index 100% rename from 02-CorpComment/video 06/style.css rename to 02 - CorpComment/video 06/style.css diff --git a/02-CorpComment/video 07/index.html b/02 - CorpComment/video 07/index.html similarity index 100% rename from 02-CorpComment/video 07/index.html rename to 02 - CorpComment/video 07/index.html diff --git a/02-CorpComment/video 07/script.js b/02 - CorpComment/video 07/script.js similarity index 100% rename from 02-CorpComment/video 07/script.js rename to 02 - CorpComment/video 07/script.js diff --git a/02-CorpComment/video 07/style.css b/02 - CorpComment/video 07/style.css similarity index 100% rename from 02-CorpComment/video 07/style.css rename to 02 - CorpComment/video 07/style.css diff --git a/02-CorpComment/video 08/index.html b/02 - CorpComment/video 08/index.html similarity index 100% rename from 02-CorpComment/video 08/index.html rename to 02 - CorpComment/video 08/index.html diff --git a/02-CorpComment/video 08/script.js b/02 - CorpComment/video 08/script.js similarity index 100% rename from 02-CorpComment/video 08/script.js rename to 02 - CorpComment/video 08/script.js diff --git a/02-CorpComment/video 08/style.css b/02 - CorpComment/video 08/style.css similarity index 100% rename from 02-CorpComment/video 08/style.css rename to 02 - CorpComment/video 08/style.css diff --git a/02-CorpComment/video 09/index.html b/02 - CorpComment/video 09/index.html similarity index 100% rename from 02-CorpComment/video 09/index.html rename to 02 - CorpComment/video 09/index.html diff --git a/02-CorpComment/video 09/script.js b/02 - CorpComment/video 09/script.js similarity index 100% rename from 02-CorpComment/video 09/script.js rename to 02 - CorpComment/video 09/script.js diff --git a/02-CorpComment/video 09/style.css b/02 - CorpComment/video 09/style.css similarity index 100% rename from 02-CorpComment/video 09/style.css rename to 02 - CorpComment/video 09/style.css diff --git a/02-CorpComment/video 10/index.html b/02 - CorpComment/video 10/index.html similarity index 100% rename from 02-CorpComment/video 10/index.html rename to 02 - CorpComment/video 10/index.html diff --git a/02-CorpComment/video 10/script.js b/02 - CorpComment/video 10/script.js similarity index 100% rename from 02-CorpComment/video 10/script.js rename to 02 - CorpComment/video 10/script.js diff --git a/02-CorpComment/video 10/style.css b/02 - CorpComment/video 10/style.css similarity index 100% rename from 02-CorpComment/video 10/style.css rename to 02 - CorpComment/video 10/style.css diff --git a/02-CorpComment/video 11/index.html b/02 - CorpComment/video 11/index.html similarity index 100% rename from 02-CorpComment/video 11/index.html rename to 02 - CorpComment/video 11/index.html diff --git a/02-CorpComment/video 11/script.js b/02 - CorpComment/video 11/script.js similarity index 100% rename from 02-CorpComment/video 11/script.js rename to 02 - CorpComment/video 11/script.js diff --git a/02-CorpComment/video 11/style.css b/02 - CorpComment/video 11/style.css similarity index 100% rename from 02-CorpComment/video 11/style.css rename to 02 - CorpComment/video 11/style.css diff --git a/02-CorpComment/video 12/index.html b/02 - CorpComment/video 12/index.html similarity index 100% rename from 02-CorpComment/video 12/index.html rename to 02 - CorpComment/video 12/index.html diff --git a/02-CorpComment/video 12/script.js b/02 - CorpComment/video 12/script.js similarity index 100% rename from 02-CorpComment/video 12/script.js rename to 02 - CorpComment/video 12/script.js diff --git a/02-CorpComment/video 12/style.css b/02 - CorpComment/video 12/style.css similarity index 100% rename from 02-CorpComment/video 12/style.css rename to 02 - CorpComment/video 12/style.css diff --git a/02-CorpComment/video 13/index.html b/02 - CorpComment/video 13/index.html similarity index 100% rename from 02-CorpComment/video 13/index.html rename to 02 - CorpComment/video 13/index.html diff --git a/02-CorpComment/video 13/script.js b/02 - CorpComment/video 13/script.js similarity index 100% rename from 02-CorpComment/video 13/script.js rename to 02 - CorpComment/video 13/script.js diff --git a/02-CorpComment/video 13/style.css b/02 - CorpComment/video 13/style.css similarity index 100% rename from 02-CorpComment/video 13/style.css rename to 02 - CorpComment/video 13/style.css diff --git a/02-CorpComment/video 14/index.html b/02 - CorpComment/video 14/index.html similarity index 100% rename from 02-CorpComment/video 14/index.html rename to 02 - CorpComment/video 14/index.html diff --git a/02-CorpComment/video 14/script.js b/02 - CorpComment/video 14/script.js similarity index 100% rename from 02-CorpComment/video 14/script.js rename to 02 - CorpComment/video 14/script.js diff --git a/02-CorpComment/video 14/style.css b/02 - CorpComment/video 14/style.css similarity index 100% rename from 02-CorpComment/video 14/style.css rename to 02 - CorpComment/video 14/style.css diff --git a/03-rmtDev/video 16/index.css b/03 - rmtDev/video 16/index.css similarity index 100% rename from 03-rmtDev/video 16/index.css rename to 03 - rmtDev/video 16/index.css diff --git a/03-rmtDev/video 16/index.html b/03 - rmtDev/video 16/index.html similarity index 100% rename from 03-rmtDev/video 16/index.html rename to 03 - rmtDev/video 16/index.html diff --git a/03-rmtDev/video 16/index.js b/03 - rmtDev/video 16/index.js similarity index 100% rename from 03-rmtDev/video 16/index.js rename to 03 - rmtDev/video 16/index.js diff --git a/03-rmtDev/video 17/index.css b/03 - rmtDev/video 17/index.css similarity index 100% rename from 03-rmtDev/video 17/index.css rename to 03 - rmtDev/video 17/index.css diff --git a/03-rmtDev/video 17/index.html b/03 - rmtDev/video 17/index.html similarity index 100% rename from 03-rmtDev/video 17/index.html rename to 03 - rmtDev/video 17/index.html diff --git a/03-rmtDev/video 17/index.js b/03 - rmtDev/video 17/index.js similarity index 100% rename from 03-rmtDev/video 17/index.js rename to 03 - rmtDev/video 17/index.js diff --git a/03-rmtDev/video 18/index.css b/03 - rmtDev/video 18/index.css similarity index 100% rename from 03-rmtDev/video 18/index.css rename to 03 - rmtDev/video 18/index.css diff --git a/03-rmtDev/video 18/index.html b/03 - rmtDev/video 18/index.html similarity index 100% rename from 03-rmtDev/video 18/index.html rename to 03 - rmtDev/video 18/index.html diff --git a/03-rmtDev/video 18/index.js b/03 - rmtDev/video 18/index.js similarity index 100% rename from 03-rmtDev/video 18/index.js rename to 03 - rmtDev/video 18/index.js diff --git a/03-rmtDev/video 19/index.css b/03 - rmtDev/video 19/index.css similarity index 100% rename from 03-rmtDev/video 19/index.css rename to 03 - rmtDev/video 19/index.css diff --git a/03-rmtDev/video 19/index.html b/03 - rmtDev/video 19/index.html similarity index 100% rename from 03-rmtDev/video 19/index.html rename to 03 - rmtDev/video 19/index.html diff --git a/03-rmtDev/video 19/index.js b/03 - rmtDev/video 19/index.js similarity index 100% rename from 03-rmtDev/video 19/index.js rename to 03 - rmtDev/video 19/index.js diff --git a/03-rmtDev/video 19/src/common.js b/03 - rmtDev/video 19/src/common.js similarity index 100% rename from 03-rmtDev/video 19/src/common.js rename to 03 - rmtDev/video 19/src/common.js diff --git a/03-rmtDev/video 19/src/components/Error.js b/03 - rmtDev/video 19/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 19/src/components/Error.js rename to 03 - rmtDev/video 19/src/components/Error.js diff --git a/03-rmtDev/video 19/src/components/JobList.js b/03 - rmtDev/video 19/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 19/src/components/JobList.js rename to 03 - rmtDev/video 19/src/components/JobList.js diff --git a/03-rmtDev/video 19/src/components/Search.js b/03 - rmtDev/video 19/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 19/src/components/Search.js rename to 03 - rmtDev/video 19/src/components/Search.js diff --git a/03-rmtDev/video 19/src/components/Spinner.js b/03 - rmtDev/video 19/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 19/src/components/Spinner.js rename to 03 - rmtDev/video 19/src/components/Spinner.js diff --git a/03-rmtDev/video 20/index.css b/03 - rmtDev/video 20/index.css similarity index 100% rename from 03-rmtDev/video 20/index.css rename to 03 - rmtDev/video 20/index.css diff --git a/03-rmtDev/video 20/index.html b/03 - rmtDev/video 20/index.html similarity index 100% rename from 03-rmtDev/video 20/index.html rename to 03 - rmtDev/video 20/index.html diff --git a/03-rmtDev/video 20/index.js b/03 - rmtDev/video 20/index.js similarity index 100% rename from 03-rmtDev/video 20/index.js rename to 03 - rmtDev/video 20/index.js diff --git a/03-rmtDev/video 20/src/common.js b/03 - rmtDev/video 20/src/common.js similarity index 100% rename from 03-rmtDev/video 20/src/common.js rename to 03 - rmtDev/video 20/src/common.js diff --git a/03-rmtDev/video 20/src/components/Error.js b/03 - rmtDev/video 20/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 20/src/components/Error.js rename to 03 - rmtDev/video 20/src/components/Error.js diff --git a/03-rmtDev/video 20/src/components/JobList.js b/03 - rmtDev/video 20/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 20/src/components/JobList.js rename to 03 - rmtDev/video 20/src/components/JobList.js diff --git a/03-rmtDev/video 20/src/components/Search.js b/03 - rmtDev/video 20/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 20/src/components/Search.js rename to 03 - rmtDev/video 20/src/components/Search.js diff --git a/03-rmtDev/video 20/src/components/Spinner.js b/03 - rmtDev/video 20/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 20/src/components/Spinner.js rename to 03 - rmtDev/video 20/src/components/Spinner.js diff --git a/03-rmtDev/video 21/index.css b/03 - rmtDev/video 21/index.css similarity index 100% rename from 03-rmtDev/video 21/index.css rename to 03 - rmtDev/video 21/index.css diff --git a/03-rmtDev/video 21/index.html b/03 - rmtDev/video 21/index.html similarity index 100% rename from 03-rmtDev/video 21/index.html rename to 03 - rmtDev/video 21/index.html diff --git a/03-rmtDev/video 21/index.js b/03 - rmtDev/video 21/index.js similarity index 100% rename from 03-rmtDev/video 21/index.js rename to 03 - rmtDev/video 21/index.js diff --git a/03-rmtDev/video 21/src/common.js b/03 - rmtDev/video 21/src/common.js similarity index 100% rename from 03-rmtDev/video 21/src/common.js rename to 03 - rmtDev/video 21/src/common.js diff --git a/03-rmtDev/video 21/src/components/Error.js b/03 - rmtDev/video 21/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 21/src/components/Error.js rename to 03 - rmtDev/video 21/src/components/Error.js diff --git a/03-rmtDev/video 21/src/components/JobList.js b/03 - rmtDev/video 21/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 21/src/components/JobList.js rename to 03 - rmtDev/video 21/src/components/JobList.js diff --git a/03-rmtDev/video 21/src/components/Search.js b/03 - rmtDev/video 21/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 21/src/components/Search.js rename to 03 - rmtDev/video 21/src/components/Search.js diff --git a/03-rmtDev/video 21/src/components/Spinner.js b/03 - rmtDev/video 21/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 21/src/components/Spinner.js rename to 03 - rmtDev/video 21/src/components/Spinner.js diff --git a/03-rmtDev/video 22/index.css b/03 - rmtDev/video 22/index.css similarity index 100% rename from 03-rmtDev/video 22/index.css rename to 03 - rmtDev/video 22/index.css diff --git a/03-rmtDev/video 22/index.html b/03 - rmtDev/video 22/index.html similarity index 100% rename from 03-rmtDev/video 22/index.html rename to 03 - rmtDev/video 22/index.html diff --git a/03-rmtDev/video 22/index.js b/03 - rmtDev/video 22/index.js similarity index 100% rename from 03-rmtDev/video 22/index.js rename to 03 - rmtDev/video 22/index.js diff --git a/03-rmtDev/video 22/src/common.js b/03 - rmtDev/video 22/src/common.js similarity index 100% rename from 03-rmtDev/video 22/src/common.js rename to 03 - rmtDev/video 22/src/common.js diff --git a/03-rmtDev/video 22/src/components/Error.js b/03 - rmtDev/video 22/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 22/src/components/Error.js rename to 03 - rmtDev/video 22/src/components/Error.js diff --git a/03-rmtDev/video 22/src/components/JobDetails.js b/03 - rmtDev/video 22/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 22/src/components/JobDetails.js rename to 03 - rmtDev/video 22/src/components/JobDetails.js diff --git a/03-rmtDev/video 22/src/components/JobList.js b/03 - rmtDev/video 22/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 22/src/components/JobList.js rename to 03 - rmtDev/video 22/src/components/JobList.js diff --git a/03-rmtDev/video 22/src/components/Search.js b/03 - rmtDev/video 22/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 22/src/components/Search.js rename to 03 - rmtDev/video 22/src/components/Search.js diff --git a/03-rmtDev/video 22/src/components/Spinner.js b/03 - rmtDev/video 22/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 22/src/components/Spinner.js rename to 03 - rmtDev/video 22/src/components/Spinner.js diff --git a/03-rmtDev/video 23/index.css b/03 - rmtDev/video 23/index.css similarity index 100% rename from 03-rmtDev/video 23/index.css rename to 03 - rmtDev/video 23/index.css diff --git a/03-rmtDev/video 23/index.html b/03 - rmtDev/video 23/index.html similarity index 100% rename from 03-rmtDev/video 23/index.html rename to 03 - rmtDev/video 23/index.html diff --git a/03-rmtDev/video 23/index.js b/03 - rmtDev/video 23/index.js similarity index 100% rename from 03-rmtDev/video 23/index.js rename to 03 - rmtDev/video 23/index.js diff --git a/03-rmtDev/video 23/src/common.js b/03 - rmtDev/video 23/src/common.js similarity index 100% rename from 03-rmtDev/video 23/src/common.js rename to 03 - rmtDev/video 23/src/common.js diff --git a/03-rmtDev/video 23/src/components/Error.js b/03 - rmtDev/video 23/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 23/src/components/Error.js rename to 03 - rmtDev/video 23/src/components/Error.js diff --git a/03-rmtDev/video 23/src/components/JobDetails.js b/03 - rmtDev/video 23/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 23/src/components/JobDetails.js rename to 03 - rmtDev/video 23/src/components/JobDetails.js diff --git a/03-rmtDev/video 23/src/components/JobList.js b/03 - rmtDev/video 23/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 23/src/components/JobList.js rename to 03 - rmtDev/video 23/src/components/JobList.js diff --git a/03-rmtDev/video 23/src/components/Search.js b/03 - rmtDev/video 23/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 23/src/components/Search.js rename to 03 - rmtDev/video 23/src/components/Search.js diff --git a/03-rmtDev/video 23/src/components/Spinner.js b/03 - rmtDev/video 23/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 23/src/components/Spinner.js rename to 03 - rmtDev/video 23/src/components/Spinner.js diff --git a/03-rmtDev/video 24/index.css b/03 - rmtDev/video 24/index.css similarity index 100% rename from 03-rmtDev/video 24/index.css rename to 03 - rmtDev/video 24/index.css diff --git a/03-rmtDev/video 24/index.html b/03 - rmtDev/video 24/index.html similarity index 100% rename from 03-rmtDev/video 24/index.html rename to 03 - rmtDev/video 24/index.html diff --git a/03-rmtDev/video 24/index.js b/03 - rmtDev/video 24/index.js similarity index 100% rename from 03-rmtDev/video 24/index.js rename to 03 - rmtDev/video 24/index.js diff --git a/03-rmtDev/video 24/src/common.js b/03 - rmtDev/video 24/src/common.js similarity index 100% rename from 03-rmtDev/video 24/src/common.js rename to 03 - rmtDev/video 24/src/common.js diff --git a/03-rmtDev/video 24/src/components/Error.js b/03 - rmtDev/video 24/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 24/src/components/Error.js rename to 03 - rmtDev/video 24/src/components/Error.js diff --git a/03-rmtDev/video 24/src/components/JobDetails.js b/03 - rmtDev/video 24/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 24/src/components/JobDetails.js rename to 03 - rmtDev/video 24/src/components/JobDetails.js diff --git a/03-rmtDev/video 24/src/components/JobList.js b/03 - rmtDev/video 24/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 24/src/components/JobList.js rename to 03 - rmtDev/video 24/src/components/JobList.js diff --git a/03-rmtDev/video 24/src/components/Search.js b/03 - rmtDev/video 24/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 24/src/components/Search.js rename to 03 - rmtDev/video 24/src/components/Search.js diff --git a/03-rmtDev/video 24/src/components/Spinner.js b/03 - rmtDev/video 24/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 24/src/components/Spinner.js rename to 03 - rmtDev/video 24/src/components/Spinner.js diff --git a/03-rmtDev/video 25/index.css b/03 - rmtDev/video 25/index.css similarity index 100% rename from 03-rmtDev/video 25/index.css rename to 03 - rmtDev/video 25/index.css diff --git a/03-rmtDev/video 25/index.html b/03 - rmtDev/video 25/index.html similarity index 100% rename from 03-rmtDev/video 25/index.html rename to 03 - rmtDev/video 25/index.html diff --git a/03-rmtDev/video 25/index.js b/03 - rmtDev/video 25/index.js similarity index 100% rename from 03-rmtDev/video 25/index.js rename to 03 - rmtDev/video 25/index.js diff --git a/03-rmtDev/video 25/src/common.js b/03 - rmtDev/video 25/src/common.js similarity index 100% rename from 03-rmtDev/video 25/src/common.js rename to 03 - rmtDev/video 25/src/common.js diff --git a/03-rmtDev/video 25/src/components/Error.js b/03 - rmtDev/video 25/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 25/src/components/Error.js rename to 03 - rmtDev/video 25/src/components/Error.js diff --git a/03-rmtDev/video 25/src/components/JobDetails.js b/03 - rmtDev/video 25/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 25/src/components/JobDetails.js rename to 03 - rmtDev/video 25/src/components/JobDetails.js diff --git a/03-rmtDev/video 25/src/components/JobList.js b/03 - rmtDev/video 25/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 25/src/components/JobList.js rename to 03 - rmtDev/video 25/src/components/JobList.js diff --git a/03-rmtDev/video 25/src/components/Search.js b/03 - rmtDev/video 25/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 25/src/components/Search.js rename to 03 - rmtDev/video 25/src/components/Search.js diff --git a/03-rmtDev/video 25/src/components/Spinner.js b/03 - rmtDev/video 25/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 25/src/components/Spinner.js rename to 03 - rmtDev/video 25/src/components/Spinner.js diff --git a/03-rmtDev/video 26/index.css b/03 - rmtDev/video 26/index.css similarity index 100% rename from 03-rmtDev/video 26/index.css rename to 03 - rmtDev/video 26/index.css diff --git a/03-rmtDev/video 26/index.html b/03 - rmtDev/video 26/index.html similarity index 100% rename from 03-rmtDev/video 26/index.html rename to 03 - rmtDev/video 26/index.html diff --git a/03-rmtDev/video 26/index.js b/03 - rmtDev/video 26/index.js similarity index 100% rename from 03-rmtDev/video 26/index.js rename to 03 - rmtDev/video 26/index.js diff --git a/03-rmtDev/video 26/src/common.js b/03 - rmtDev/video 26/src/common.js similarity index 100% rename from 03-rmtDev/video 26/src/common.js rename to 03 - rmtDev/video 26/src/common.js diff --git a/03-rmtDev/video 26/src/components/Error.js b/03 - rmtDev/video 26/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 26/src/components/Error.js rename to 03 - rmtDev/video 26/src/components/Error.js diff --git a/03-rmtDev/video 26/src/components/JobDetails.js b/03 - rmtDev/video 26/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 26/src/components/JobDetails.js rename to 03 - rmtDev/video 26/src/components/JobDetails.js diff --git a/03-rmtDev/video 26/src/components/JobList.js b/03 - rmtDev/video 26/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 26/src/components/JobList.js rename to 03 - rmtDev/video 26/src/components/JobList.js diff --git a/03-rmtDev/video 26/src/components/Search.js b/03 - rmtDev/video 26/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 26/src/components/Search.js rename to 03 - rmtDev/video 26/src/components/Search.js diff --git a/03-rmtDev/video 26/src/components/Sorting.js b/03 - rmtDev/video 26/src/components/Sorting.js similarity index 100% rename from 03-rmtDev/video 26/src/components/Sorting.js rename to 03 - rmtDev/video 26/src/components/Sorting.js diff --git a/03-rmtDev/video 26/src/components/Spinner.js b/03 - rmtDev/video 26/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 26/src/components/Spinner.js rename to 03 - rmtDev/video 26/src/components/Spinner.js diff --git a/03-rmtDev/video 27/index.css b/03 - rmtDev/video 27/index.css similarity index 100% rename from 03-rmtDev/video 27/index.css rename to 03 - rmtDev/video 27/index.css diff --git a/03-rmtDev/video 27/index.html b/03 - rmtDev/video 27/index.html similarity index 100% rename from 03-rmtDev/video 27/index.html rename to 03 - rmtDev/video 27/index.html diff --git a/03-rmtDev/video 27/index.js b/03 - rmtDev/video 27/index.js similarity index 100% rename from 03-rmtDev/video 27/index.js rename to 03 - rmtDev/video 27/index.js diff --git a/03-rmtDev/video 27/src/common.js b/03 - rmtDev/video 27/src/common.js similarity index 100% rename from 03-rmtDev/video 27/src/common.js rename to 03 - rmtDev/video 27/src/common.js diff --git a/03-rmtDev/video 27/src/components/Error.js b/03 - rmtDev/video 27/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 27/src/components/Error.js rename to 03 - rmtDev/video 27/src/components/Error.js diff --git a/03-rmtDev/video 27/src/components/JobDetails.js b/03 - rmtDev/video 27/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 27/src/components/JobDetails.js rename to 03 - rmtDev/video 27/src/components/JobDetails.js diff --git a/03-rmtDev/video 27/src/components/JobList.js b/03 - rmtDev/video 27/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 27/src/components/JobList.js rename to 03 - rmtDev/video 27/src/components/JobList.js diff --git a/03-rmtDev/video 27/src/components/Search.js b/03 - rmtDev/video 27/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 27/src/components/Search.js rename to 03 - rmtDev/video 27/src/components/Search.js diff --git a/03-rmtDev/video 27/src/components/Sorting.js b/03 - rmtDev/video 27/src/components/Sorting.js similarity index 100% rename from 03-rmtDev/video 27/src/components/Sorting.js rename to 03 - rmtDev/video 27/src/components/Sorting.js diff --git a/03-rmtDev/video 27/src/components/Spinner.js b/03 - rmtDev/video 27/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 27/src/components/Spinner.js rename to 03 - rmtDev/video 27/src/components/Spinner.js diff --git a/03-rmtDev/video 28/index.css b/03 - rmtDev/video 28/index.css similarity index 100% rename from 03-rmtDev/video 28/index.css rename to 03 - rmtDev/video 28/index.css diff --git a/03-rmtDev/video 28/index.html b/03 - rmtDev/video 28/index.html similarity index 100% rename from 03-rmtDev/video 28/index.html rename to 03 - rmtDev/video 28/index.html diff --git a/03-rmtDev/video 28/index.js b/03 - rmtDev/video 28/index.js similarity index 100% rename from 03-rmtDev/video 28/index.js rename to 03 - rmtDev/video 28/index.js diff --git a/03-rmtDev/video 28/src/common.js b/03 - rmtDev/video 28/src/common.js similarity index 100% rename from 03-rmtDev/video 28/src/common.js rename to 03 - rmtDev/video 28/src/common.js diff --git a/03-rmtDev/video 28/src/components/Error.js b/03 - rmtDev/video 28/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 28/src/components/Error.js rename to 03 - rmtDev/video 28/src/components/Error.js diff --git a/03-rmtDev/video 28/src/components/JobDetails.js b/03 - rmtDev/video 28/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 28/src/components/JobDetails.js rename to 03 - rmtDev/video 28/src/components/JobDetails.js diff --git a/03-rmtDev/video 28/src/components/JobList.js b/03 - rmtDev/video 28/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 28/src/components/JobList.js rename to 03 - rmtDev/video 28/src/components/JobList.js diff --git a/03-rmtDev/video 28/src/components/Search.js b/03 - rmtDev/video 28/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 28/src/components/Search.js rename to 03 - rmtDev/video 28/src/components/Search.js diff --git a/03-rmtDev/video 28/src/components/Sorting.js b/03 - rmtDev/video 28/src/components/Sorting.js similarity index 100% rename from 03-rmtDev/video 28/src/components/Sorting.js rename to 03 - rmtDev/video 28/src/components/Sorting.js diff --git a/03-rmtDev/video 28/src/components/Spinner.js b/03 - rmtDev/video 28/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 28/src/components/Spinner.js rename to 03 - rmtDev/video 28/src/components/Spinner.js diff --git a/03-rmtDev/video 29/index.css b/03 - rmtDev/video 29/index.css similarity index 100% rename from 03-rmtDev/video 29/index.css rename to 03 - rmtDev/video 29/index.css diff --git a/03-rmtDev/video 29/index.html b/03 - rmtDev/video 29/index.html similarity index 100% rename from 03-rmtDev/video 29/index.html rename to 03 - rmtDev/video 29/index.html diff --git a/03-rmtDev/video 29/index.js b/03 - rmtDev/video 29/index.js similarity index 100% rename from 03-rmtDev/video 29/index.js rename to 03 - rmtDev/video 29/index.js diff --git a/03-rmtDev/video 29/src/common.js b/03 - rmtDev/video 29/src/common.js similarity index 100% rename from 03-rmtDev/video 29/src/common.js rename to 03 - rmtDev/video 29/src/common.js diff --git a/03-rmtDev/video 29/src/components/Error.js b/03 - rmtDev/video 29/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 29/src/components/Error.js rename to 03 - rmtDev/video 29/src/components/Error.js diff --git a/03-rmtDev/video 29/src/components/JobDetails.js b/03 - rmtDev/video 29/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 29/src/components/JobDetails.js rename to 03 - rmtDev/video 29/src/components/JobDetails.js diff --git a/03-rmtDev/video 29/src/components/JobList.js b/03 - rmtDev/video 29/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 29/src/components/JobList.js rename to 03 - rmtDev/video 29/src/components/JobList.js diff --git a/03-rmtDev/video 29/src/components/Pagination.js b/03 - rmtDev/video 29/src/components/Pagination.js similarity index 100% rename from 03-rmtDev/video 29/src/components/Pagination.js rename to 03 - rmtDev/video 29/src/components/Pagination.js diff --git a/03-rmtDev/video 29/src/components/Search.js b/03 - rmtDev/video 29/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 29/src/components/Search.js rename to 03 - rmtDev/video 29/src/components/Search.js diff --git a/03-rmtDev/video 29/src/components/Sorting.js b/03 - rmtDev/video 29/src/components/Sorting.js similarity index 100% rename from 03-rmtDev/video 29/src/components/Sorting.js rename to 03 - rmtDev/video 29/src/components/Sorting.js diff --git a/03-rmtDev/video 29/src/components/Spinner.js b/03 - rmtDev/video 29/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 29/src/components/Spinner.js rename to 03 - rmtDev/video 29/src/components/Spinner.js diff --git a/03-rmtDev/video 30/index.css b/03 - rmtDev/video 30/index.css similarity index 100% rename from 03-rmtDev/video 30/index.css rename to 03 - rmtDev/video 30/index.css diff --git a/03-rmtDev/video 30/index.html b/03 - rmtDev/video 30/index.html similarity index 100% rename from 03-rmtDev/video 30/index.html rename to 03 - rmtDev/video 30/index.html diff --git a/03-rmtDev/video 30/index.js b/03 - rmtDev/video 30/index.js similarity index 100% rename from 03-rmtDev/video 30/index.js rename to 03 - rmtDev/video 30/index.js diff --git a/03-rmtDev/video 30/src/common.js b/03 - rmtDev/video 30/src/common.js similarity index 100% rename from 03-rmtDev/video 30/src/common.js rename to 03 - rmtDev/video 30/src/common.js diff --git a/03-rmtDev/video 30/src/components/Error.js b/03 - rmtDev/video 30/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 30/src/components/Error.js rename to 03 - rmtDev/video 30/src/components/Error.js diff --git a/03-rmtDev/video 30/src/components/JobDetails.js b/03 - rmtDev/video 30/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 30/src/components/JobDetails.js rename to 03 - rmtDev/video 30/src/components/JobDetails.js diff --git a/03-rmtDev/video 30/src/components/JobList.js b/03 - rmtDev/video 30/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 30/src/components/JobList.js rename to 03 - rmtDev/video 30/src/components/JobList.js diff --git a/03-rmtDev/video 30/src/components/Pagination.js b/03 - rmtDev/video 30/src/components/Pagination.js similarity index 100% rename from 03-rmtDev/video 30/src/components/Pagination.js rename to 03 - rmtDev/video 30/src/components/Pagination.js diff --git a/03-rmtDev/video 30/src/components/Router.js b/03 - rmtDev/video 30/src/components/Router.js similarity index 100% rename from 03-rmtDev/video 30/src/components/Router.js rename to 03 - rmtDev/video 30/src/components/Router.js diff --git a/03-rmtDev/video 30/src/components/Search.js b/03 - rmtDev/video 30/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 30/src/components/Search.js rename to 03 - rmtDev/video 30/src/components/Search.js diff --git a/03-rmtDev/video 30/src/components/Sorting.js b/03 - rmtDev/video 30/src/components/Sorting.js similarity index 100% rename from 03-rmtDev/video 30/src/components/Sorting.js rename to 03 - rmtDev/video 30/src/components/Sorting.js diff --git a/03-rmtDev/video 30/src/components/Spinner.js b/03 - rmtDev/video 30/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 30/src/components/Spinner.js rename to 03 - rmtDev/video 30/src/components/Spinner.js diff --git a/03-rmtDev/video 31/index.css b/03 - rmtDev/video 31/index.css similarity index 100% rename from 03-rmtDev/video 31/index.css rename to 03 - rmtDev/video 31/index.css diff --git a/03-rmtDev/video 31/index.html b/03 - rmtDev/video 31/index.html similarity index 100% rename from 03-rmtDev/video 31/index.html rename to 03 - rmtDev/video 31/index.html diff --git a/03-rmtDev/video 31/index.js b/03 - rmtDev/video 31/index.js similarity index 100% rename from 03-rmtDev/video 31/index.js rename to 03 - rmtDev/video 31/index.js diff --git a/03-rmtDev/video 31/src/common.js b/03 - rmtDev/video 31/src/common.js similarity index 100% rename from 03-rmtDev/video 31/src/common.js rename to 03 - rmtDev/video 31/src/common.js diff --git a/03-rmtDev/video 31/src/components/Error.js b/03 - rmtDev/video 31/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 31/src/components/Error.js rename to 03 - rmtDev/video 31/src/components/Error.js diff --git a/03-rmtDev/video 31/src/components/JobDetails.js b/03 - rmtDev/video 31/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 31/src/components/JobDetails.js rename to 03 - rmtDev/video 31/src/components/JobDetails.js diff --git a/03-rmtDev/video 31/src/components/JobList.js b/03 - rmtDev/video 31/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 31/src/components/JobList.js rename to 03 - rmtDev/video 31/src/components/JobList.js diff --git a/03-rmtDev/video 31/src/components/Pagination.js b/03 - rmtDev/video 31/src/components/Pagination.js similarity index 100% rename from 03-rmtDev/video 31/src/components/Pagination.js rename to 03 - rmtDev/video 31/src/components/Pagination.js diff --git a/03-rmtDev/video 31/src/components/Router.js b/03 - rmtDev/video 31/src/components/Router.js similarity index 100% rename from 03-rmtDev/video 31/src/components/Router.js rename to 03 - rmtDev/video 31/src/components/Router.js diff --git a/03-rmtDev/video 31/src/components/Search.js b/03 - rmtDev/video 31/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 31/src/components/Search.js rename to 03 - rmtDev/video 31/src/components/Search.js diff --git a/03-rmtDev/video 31/src/components/Sorting.js b/03 - rmtDev/video 31/src/components/Sorting.js similarity index 100% rename from 03-rmtDev/video 31/src/components/Sorting.js rename to 03 - rmtDev/video 31/src/components/Sorting.js diff --git a/03-rmtDev/video 31/src/components/Spinner.js b/03 - rmtDev/video 31/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 31/src/components/Spinner.js rename to 03 - rmtDev/video 31/src/components/Spinner.js diff --git a/03-rmtDev/video 32/index.css b/03 - rmtDev/video 32/index.css similarity index 100% rename from 03-rmtDev/video 32/index.css rename to 03 - rmtDev/video 32/index.css diff --git a/03-rmtDev/video 32/index.html b/03 - rmtDev/video 32/index.html similarity index 100% rename from 03-rmtDev/video 32/index.html rename to 03 - rmtDev/video 32/index.html diff --git a/03-rmtDev/video 32/index.js b/03 - rmtDev/video 32/index.js similarity index 100% rename from 03-rmtDev/video 32/index.js rename to 03 - rmtDev/video 32/index.js diff --git a/03-rmtDev/video 32/src/common.js b/03 - rmtDev/video 32/src/common.js similarity index 100% rename from 03-rmtDev/video 32/src/common.js rename to 03 - rmtDev/video 32/src/common.js diff --git a/03-rmtDev/video 32/src/components/Bookmarks.js b/03 - rmtDev/video 32/src/components/Bookmarks.js similarity index 100% rename from 03-rmtDev/video 32/src/components/Bookmarks.js rename to 03 - rmtDev/video 32/src/components/Bookmarks.js diff --git a/03-rmtDev/video 32/src/components/Error.js b/03 - rmtDev/video 32/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 32/src/components/Error.js rename to 03 - rmtDev/video 32/src/components/Error.js diff --git a/03-rmtDev/video 32/src/components/JobDetails.js b/03 - rmtDev/video 32/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 32/src/components/JobDetails.js rename to 03 - rmtDev/video 32/src/components/JobDetails.js diff --git a/03-rmtDev/video 32/src/components/JobList.js b/03 - rmtDev/video 32/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 32/src/components/JobList.js rename to 03 - rmtDev/video 32/src/components/JobList.js diff --git a/03-rmtDev/video 32/src/components/Pagination.js b/03 - rmtDev/video 32/src/components/Pagination.js similarity index 100% rename from 03-rmtDev/video 32/src/components/Pagination.js rename to 03 - rmtDev/video 32/src/components/Pagination.js diff --git a/03-rmtDev/video 32/src/components/Router.js b/03 - rmtDev/video 32/src/components/Router.js similarity index 100% rename from 03-rmtDev/video 32/src/components/Router.js rename to 03 - rmtDev/video 32/src/components/Router.js diff --git a/03-rmtDev/video 32/src/components/Search.js b/03 - rmtDev/video 32/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 32/src/components/Search.js rename to 03 - rmtDev/video 32/src/components/Search.js diff --git a/03-rmtDev/video 32/src/components/Sorting.js b/03 - rmtDev/video 32/src/components/Sorting.js similarity index 100% rename from 03-rmtDev/video 32/src/components/Sorting.js rename to 03 - rmtDev/video 32/src/components/Sorting.js diff --git a/03-rmtDev/video 32/src/components/Spinner.js b/03 - rmtDev/video 32/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 32/src/components/Spinner.js rename to 03 - rmtDev/video 32/src/components/Spinner.js diff --git a/03-rmtDev/video 33/index.css b/03 - rmtDev/video 33/index.css similarity index 100% rename from 03-rmtDev/video 33/index.css rename to 03 - rmtDev/video 33/index.css diff --git a/03-rmtDev/video 33/index.html b/03 - rmtDev/video 33/index.html similarity index 100% rename from 03-rmtDev/video 33/index.html rename to 03 - rmtDev/video 33/index.html diff --git a/03-rmtDev/video 33/index.js b/03 - rmtDev/video 33/index.js similarity index 100% rename from 03-rmtDev/video 33/index.js rename to 03 - rmtDev/video 33/index.js diff --git a/03-rmtDev/video 33/src/common.js b/03 - rmtDev/video 33/src/common.js similarity index 100% rename from 03-rmtDev/video 33/src/common.js rename to 03 - rmtDev/video 33/src/common.js diff --git a/03-rmtDev/video 33/src/components/Bookmarks.js b/03 - rmtDev/video 33/src/components/Bookmarks.js similarity index 100% rename from 03-rmtDev/video 33/src/components/Bookmarks.js rename to 03 - rmtDev/video 33/src/components/Bookmarks.js diff --git a/03-rmtDev/video 33/src/components/Error.js b/03 - rmtDev/video 33/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 33/src/components/Error.js rename to 03 - rmtDev/video 33/src/components/Error.js diff --git a/03-rmtDev/video 33/src/components/JobDetails.js b/03 - rmtDev/video 33/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 33/src/components/JobDetails.js rename to 03 - rmtDev/video 33/src/components/JobDetails.js diff --git a/03-rmtDev/video 33/src/components/JobList.js b/03 - rmtDev/video 33/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 33/src/components/JobList.js rename to 03 - rmtDev/video 33/src/components/JobList.js diff --git a/03-rmtDev/video 33/src/components/Pagination.js b/03 - rmtDev/video 33/src/components/Pagination.js similarity index 100% rename from 03-rmtDev/video 33/src/components/Pagination.js rename to 03 - rmtDev/video 33/src/components/Pagination.js diff --git a/03-rmtDev/video 33/src/components/Router.js b/03 - rmtDev/video 33/src/components/Router.js similarity index 100% rename from 03-rmtDev/video 33/src/components/Router.js rename to 03 - rmtDev/video 33/src/components/Router.js diff --git a/03-rmtDev/video 33/src/components/Search.js b/03 - rmtDev/video 33/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 33/src/components/Search.js rename to 03 - rmtDev/video 33/src/components/Search.js diff --git a/03-rmtDev/video 33/src/components/Sorting.js b/03 - rmtDev/video 33/src/components/Sorting.js similarity index 100% rename from 03-rmtDev/video 33/src/components/Sorting.js rename to 03 - rmtDev/video 33/src/components/Sorting.js diff --git a/03-rmtDev/video 33/src/components/Spinner.js b/03 - rmtDev/video 33/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 33/src/components/Spinner.js rename to 03 - rmtDev/video 33/src/components/Spinner.js diff --git a/03-rmtDev/video 33/src/components/Storage.js b/03 - rmtDev/video 33/src/components/Storage.js similarity index 100% rename from 03-rmtDev/video 33/src/components/Storage.js rename to 03 - rmtDev/video 33/src/components/Storage.js diff --git a/03-rmtDev/video 34/index.css b/03 - rmtDev/video 34/index.css similarity index 100% rename from 03-rmtDev/video 34/index.css rename to 03 - rmtDev/video 34/index.css diff --git a/03-rmtDev/video 34/index.html b/03 - rmtDev/video 34/index.html similarity index 100% rename from 03-rmtDev/video 34/index.html rename to 03 - rmtDev/video 34/index.html diff --git a/03-rmtDev/video 34/index.js b/03 - rmtDev/video 34/index.js similarity index 100% rename from 03-rmtDev/video 34/index.js rename to 03 - rmtDev/video 34/index.js diff --git a/03-rmtDev/video 34/src/common.js b/03 - rmtDev/video 34/src/common.js similarity index 100% rename from 03-rmtDev/video 34/src/common.js rename to 03 - rmtDev/video 34/src/common.js diff --git a/03-rmtDev/video 34/src/components/Bookmarks.js b/03 - rmtDev/video 34/src/components/Bookmarks.js similarity index 100% rename from 03-rmtDev/video 34/src/components/Bookmarks.js rename to 03 - rmtDev/video 34/src/components/Bookmarks.js diff --git a/03-rmtDev/video 34/src/components/Error.js b/03 - rmtDev/video 34/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 34/src/components/Error.js rename to 03 - rmtDev/video 34/src/components/Error.js diff --git a/03-rmtDev/video 34/src/components/JobDetails.js b/03 - rmtDev/video 34/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 34/src/components/JobDetails.js rename to 03 - rmtDev/video 34/src/components/JobDetails.js diff --git a/03-rmtDev/video 34/src/components/JobList.js b/03 - rmtDev/video 34/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 34/src/components/JobList.js rename to 03 - rmtDev/video 34/src/components/JobList.js diff --git a/03-rmtDev/video 34/src/components/Pagination.js b/03 - rmtDev/video 34/src/components/Pagination.js similarity index 100% rename from 03-rmtDev/video 34/src/components/Pagination.js rename to 03 - rmtDev/video 34/src/components/Pagination.js diff --git a/03-rmtDev/video 34/src/components/Router.js b/03 - rmtDev/video 34/src/components/Router.js similarity index 100% rename from 03-rmtDev/video 34/src/components/Router.js rename to 03 - rmtDev/video 34/src/components/Router.js diff --git a/03-rmtDev/video 34/src/components/Search.js b/03 - rmtDev/video 34/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 34/src/components/Search.js rename to 03 - rmtDev/video 34/src/components/Search.js diff --git a/03-rmtDev/video 34/src/components/Sorting.js b/03 - rmtDev/video 34/src/components/Sorting.js similarity index 100% rename from 03-rmtDev/video 34/src/components/Sorting.js rename to 03 - rmtDev/video 34/src/components/Sorting.js diff --git a/03-rmtDev/video 34/src/components/Spinner.js b/03 - rmtDev/video 34/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 34/src/components/Spinner.js rename to 03 - rmtDev/video 34/src/components/Spinner.js diff --git a/03-rmtDev/video 34/src/components/Storage.js b/03 - rmtDev/video 34/src/components/Storage.js similarity index 100% rename from 03-rmtDev/video 34/src/components/Storage.js rename to 03 - rmtDev/video 34/src/components/Storage.js diff --git a/03-rmtDev/video 35/babel.config.js b/03 - rmtDev/video 35/babel.config.js similarity index 100% rename from 03-rmtDev/video 35/babel.config.js rename to 03 - rmtDev/video 35/babel.config.js diff --git a/03-rmtDev/video 35/dist/index.html b/03 - rmtDev/video 35/dist/index.html similarity index 100% rename from 03-rmtDev/video 35/dist/index.html rename to 03 - rmtDev/video 35/dist/index.html diff --git a/03-rmtDev/video 35/dist/main.css b/03 - rmtDev/video 35/dist/main.css similarity index 100% rename from 03-rmtDev/video 35/dist/main.css rename to 03 - rmtDev/video 35/dist/main.css diff --git a/03-rmtDev/video 35/dist/main.js b/03 - rmtDev/video 35/dist/main.js similarity index 100% rename from 03-rmtDev/video 35/dist/main.js rename to 03 - rmtDev/video 35/dist/main.js diff --git a/03-rmtDev/video 35/dist/main.js.LICENSE.txt b/03 - rmtDev/video 35/dist/main.js.LICENSE.txt similarity index 100% rename from 03-rmtDev/video 35/dist/main.js.LICENSE.txt rename to 03 - rmtDev/video 35/dist/main.js.LICENSE.txt diff --git a/03-rmtDev/video 35/package-lock.json b/03 - rmtDev/video 35/package-lock.json similarity index 100% rename from 03-rmtDev/video 35/package-lock.json rename to 03 - rmtDev/video 35/package-lock.json diff --git a/03-rmtDev/video 35/package.json b/03 - rmtDev/video 35/package.json similarity index 100% rename from 03-rmtDev/video 35/package.json rename to 03 - rmtDev/video 35/package.json diff --git a/03-rmtDev/video 35/postcss.config.js b/03 - rmtDev/video 35/postcss.config.js similarity index 100% rename from 03-rmtDev/video 35/postcss.config.js rename to 03 - rmtDev/video 35/postcss.config.js diff --git a/03-rmtDev/video 35/src/common.js b/03 - rmtDev/video 35/src/common.js similarity index 100% rename from 03-rmtDev/video 35/src/common.js rename to 03 - rmtDev/video 35/src/common.js diff --git a/03-rmtDev/video 35/src/components/Bookmarks.js b/03 - rmtDev/video 35/src/components/Bookmarks.js similarity index 100% rename from 03-rmtDev/video 35/src/components/Bookmarks.js rename to 03 - rmtDev/video 35/src/components/Bookmarks.js diff --git a/03-rmtDev/video 35/src/components/Error.js b/03 - rmtDev/video 35/src/components/Error.js similarity index 100% rename from 03-rmtDev/video 35/src/components/Error.js rename to 03 - rmtDev/video 35/src/components/Error.js diff --git a/03-rmtDev/video 35/src/components/JobDetails.js b/03 - rmtDev/video 35/src/components/JobDetails.js similarity index 100% rename from 03-rmtDev/video 35/src/components/JobDetails.js rename to 03 - rmtDev/video 35/src/components/JobDetails.js diff --git a/03-rmtDev/video 35/src/components/JobList.js b/03 - rmtDev/video 35/src/components/JobList.js similarity index 100% rename from 03-rmtDev/video 35/src/components/JobList.js rename to 03 - rmtDev/video 35/src/components/JobList.js diff --git a/03-rmtDev/video 35/src/components/Pagination.js b/03 - rmtDev/video 35/src/components/Pagination.js similarity index 100% rename from 03-rmtDev/video 35/src/components/Pagination.js rename to 03 - rmtDev/video 35/src/components/Pagination.js diff --git a/03-rmtDev/video 35/src/components/Router.js b/03 - rmtDev/video 35/src/components/Router.js similarity index 100% rename from 03-rmtDev/video 35/src/components/Router.js rename to 03 - rmtDev/video 35/src/components/Router.js diff --git a/03-rmtDev/video 35/src/components/Search.js b/03 - rmtDev/video 35/src/components/Search.js similarity index 100% rename from 03-rmtDev/video 35/src/components/Search.js rename to 03 - rmtDev/video 35/src/components/Search.js diff --git a/03-rmtDev/video 35/src/components/Sorting.js b/03 - rmtDev/video 35/src/components/Sorting.js similarity index 100% rename from 03-rmtDev/video 35/src/components/Sorting.js rename to 03 - rmtDev/video 35/src/components/Sorting.js diff --git a/03-rmtDev/video 35/src/components/Spinner.js b/03 - rmtDev/video 35/src/components/Spinner.js similarity index 100% rename from 03-rmtDev/video 35/src/components/Spinner.js rename to 03 - rmtDev/video 35/src/components/Spinner.js diff --git a/03-rmtDev/video 35/src/components/Storage.js b/03 - rmtDev/video 35/src/components/Storage.js similarity index 100% rename from 03-rmtDev/video 35/src/components/Storage.js rename to 03 - rmtDev/video 35/src/components/Storage.js diff --git a/03-rmtDev/video 35/src/index.css b/03 - rmtDev/video 35/src/index.css similarity index 100% rename from 03-rmtDev/video 35/src/index.css rename to 03 - rmtDev/video 35/src/index.css diff --git a/03-rmtDev/video 35/src/index.html b/03 - rmtDev/video 35/src/index.html similarity index 100% rename from 03-rmtDev/video 35/src/index.html rename to 03 - rmtDev/video 35/src/index.html diff --git a/03-rmtDev/video 35/src/index.js b/03 - rmtDev/video 35/src/index.js similarity index 100% rename from 03-rmtDev/video 35/src/index.js rename to 03 - rmtDev/video 35/src/index.js diff --git a/03-rmtDev/video 35/webpack.config.js b/03 - rmtDev/video 35/webpack.config.js similarity index 100% rename from 03-rmtDev/video 35/webpack.config.js rename to 03 - rmtDev/video 35/webpack.config.js diff --git a/04-Supplemental/video 36/babel.config.js b/04 - Supplemental/video 36/babel.config.js similarity index 100% rename from 04-Supplemental/video 36/babel.config.js rename to 04 - Supplemental/video 36/babel.config.js diff --git a/04-Supplemental/video 36/dist/index.html b/04 - Supplemental/video 36/dist/index.html similarity index 100% rename from 04-Supplemental/video 36/dist/index.html rename to 04 - Supplemental/video 36/dist/index.html diff --git a/04-Supplemental/video 36/dist/main.css b/04 - Supplemental/video 36/dist/main.css similarity index 100% rename from 04-Supplemental/video 36/dist/main.css rename to 04 - Supplemental/video 36/dist/main.css diff --git a/04-Supplemental/video 36/dist/main.js b/04 - Supplemental/video 36/dist/main.js similarity index 100% rename from 04-Supplemental/video 36/dist/main.js rename to 04 - Supplemental/video 36/dist/main.js diff --git a/04-Supplemental/video 36/dist/main.js.LICENSE.txt b/04 - Supplemental/video 36/dist/main.js.LICENSE.txt similarity index 100% rename from 04-Supplemental/video 36/dist/main.js.LICENSE.txt rename to 04 - Supplemental/video 36/dist/main.js.LICENSE.txt diff --git a/04-Supplemental/video 36/package-lock.json b/04 - Supplemental/video 36/package-lock.json similarity index 100% rename from 04-Supplemental/video 36/package-lock.json rename to 04 - Supplemental/video 36/package-lock.json diff --git a/04-Supplemental/video 36/package.json b/04 - Supplemental/video 36/package.json similarity index 100% rename from 04-Supplemental/video 36/package.json rename to 04 - Supplemental/video 36/package.json diff --git a/04-Supplemental/video 36/postcss.config.js b/04 - Supplemental/video 36/postcss.config.js similarity index 100% rename from 04-Supplemental/video 36/postcss.config.js rename to 04 - Supplemental/video 36/postcss.config.js diff --git a/04-Supplemental/video 36/src/common.js b/04 - Supplemental/video 36/src/common.js similarity index 100% rename from 04-Supplemental/video 36/src/common.js rename to 04 - Supplemental/video 36/src/common.js diff --git a/04-Supplemental/video 36/src/components/Bookmarks.js b/04 - Supplemental/video 36/src/components/Bookmarks.js similarity index 100% rename from 04-Supplemental/video 36/src/components/Bookmarks.js rename to 04 - Supplemental/video 36/src/components/Bookmarks.js diff --git a/04-Supplemental/video 36/src/components/Error.js b/04 - Supplemental/video 36/src/components/Error.js similarity index 100% rename from 04-Supplemental/video 36/src/components/Error.js rename to 04 - Supplemental/video 36/src/components/Error.js diff --git a/04-Supplemental/video 36/src/components/JobDetails.js b/04 - Supplemental/video 36/src/components/JobDetails.js similarity index 100% rename from 04-Supplemental/video 36/src/components/JobDetails.js rename to 04 - Supplemental/video 36/src/components/JobDetails.js diff --git a/04-Supplemental/video 36/src/components/JobList.js b/04 - Supplemental/video 36/src/components/JobList.js similarity index 100% rename from 04-Supplemental/video 36/src/components/JobList.js rename to 04 - Supplemental/video 36/src/components/JobList.js diff --git a/04-Supplemental/video 36/src/components/Pagination.js b/04 - Supplemental/video 36/src/components/Pagination.js similarity index 100% rename from 04-Supplemental/video 36/src/components/Pagination.js rename to 04 - Supplemental/video 36/src/components/Pagination.js diff --git a/04-Supplemental/video 36/src/components/Router.js b/04 - Supplemental/video 36/src/components/Router.js similarity index 100% rename from 04-Supplemental/video 36/src/components/Router.js rename to 04 - Supplemental/video 36/src/components/Router.js diff --git a/04-Supplemental/video 36/src/components/Search.js b/04 - Supplemental/video 36/src/components/Search.js similarity index 100% rename from 04-Supplemental/video 36/src/components/Search.js rename to 04 - Supplemental/video 36/src/components/Search.js diff --git a/04-Supplemental/video 36/src/components/Sorting.js b/04 - Supplemental/video 36/src/components/Sorting.js similarity index 100% rename from 04-Supplemental/video 36/src/components/Sorting.js rename to 04 - Supplemental/video 36/src/components/Sorting.js diff --git a/04-Supplemental/video 36/src/components/Spinner.js b/04 - Supplemental/video 36/src/components/Spinner.js similarity index 100% rename from 04-Supplemental/video 36/src/components/Spinner.js rename to 04 - Supplemental/video 36/src/components/Spinner.js diff --git a/04-Supplemental/video 36/src/components/Storage.js b/04 - Supplemental/video 36/src/components/Storage.js similarity index 100% rename from 04-Supplemental/video 36/src/components/Storage.js rename to 04 - Supplemental/video 36/src/components/Storage.js diff --git a/04-Supplemental/video 36/src/index.css b/04 - Supplemental/video 36/src/index.css similarity index 100% rename from 04-Supplemental/video 36/src/index.css rename to 04 - Supplemental/video 36/src/index.css diff --git a/04-Supplemental/video 36/src/index.html b/04 - Supplemental/video 36/src/index.html similarity index 100% rename from 04-Supplemental/video 36/src/index.html rename to 04 - Supplemental/video 36/src/index.html diff --git a/04-Supplemental/video 36/src/index.js b/04 - Supplemental/video 36/src/index.js similarity index 100% rename from 04-Supplemental/video 36/src/index.js rename to 04 - Supplemental/video 36/src/index.js diff --git a/04-Supplemental/video 36/webpack.config.js b/04 - Supplemental/video 36/webpack.config.js similarity index 100% rename from 04-Supplemental/video 36/webpack.config.js rename to 04 - Supplemental/video 36/webpack.config.js diff --git a/04-Supplemental/video 37/index.html b/04 - Supplemental/video 37/index.html similarity index 100% rename from 04-Supplemental/video 37/index.html rename to 04 - Supplemental/video 37/index.html diff --git a/04-Supplemental/video 37/script.js b/04 - Supplemental/video 37/script.js similarity index 100% rename from 04-Supplemental/video 37/script.js rename to 04 - Supplemental/video 37/script.js diff --git a/04-Supplemental/video 37/style.css b/04 - Supplemental/video 37/style.css similarity index 100% rename from 04-Supplemental/video 37/style.css rename to 04 - Supplemental/video 37/style.css diff --git a/04-Supplemental/video 38/babel.config.js b/04 - Supplemental/video 38/babel.config.js similarity index 100% rename from 04-Supplemental/video 38/babel.config.js rename to 04 - Supplemental/video 38/babel.config.js diff --git a/04-Supplemental/video 38/dist/index.html b/04 - Supplemental/video 38/dist/index.html similarity index 100% rename from 04-Supplemental/video 38/dist/index.html rename to 04 - Supplemental/video 38/dist/index.html diff --git a/04-Supplemental/video 38/dist/main.css b/04 - Supplemental/video 38/dist/main.css similarity index 100% rename from 04-Supplemental/video 38/dist/main.css rename to 04 - Supplemental/video 38/dist/main.css diff --git a/04-Supplemental/video 38/dist/main.js b/04 - Supplemental/video 38/dist/main.js similarity index 100% rename from 04-Supplemental/video 38/dist/main.js rename to 04 - Supplemental/video 38/dist/main.js diff --git a/04-Supplemental/video 38/dist/main.js.LICENSE.txt b/04 - Supplemental/video 38/dist/main.js.LICENSE.txt similarity index 100% rename from 04-Supplemental/video 38/dist/main.js.LICENSE.txt rename to 04 - Supplemental/video 38/dist/main.js.LICENSE.txt diff --git a/04-Supplemental/video 38/package-lock.json b/04 - Supplemental/video 38/package-lock.json similarity index 100% rename from 04-Supplemental/video 38/package-lock.json rename to 04 - Supplemental/video 38/package-lock.json diff --git a/04-Supplemental/video 38/package.json b/04 - Supplemental/video 38/package.json similarity index 100% rename from 04-Supplemental/video 38/package.json rename to 04 - Supplemental/video 38/package.json diff --git a/04-Supplemental/video 38/postcss.config.js b/04 - Supplemental/video 38/postcss.config.js similarity index 100% rename from 04-Supplemental/video 38/postcss.config.js rename to 04 - Supplemental/video 38/postcss.config.js diff --git a/04-Supplemental/video 38/src/common.js b/04 - Supplemental/video 38/src/common.js similarity index 100% rename from 04-Supplemental/video 38/src/common.js rename to 04 - Supplemental/video 38/src/common.js diff --git a/04-Supplemental/video 38/src/components/Bookmarks.js b/04 - Supplemental/video 38/src/components/Bookmarks.js similarity index 100% rename from 04-Supplemental/video 38/src/components/Bookmarks.js rename to 04 - Supplemental/video 38/src/components/Bookmarks.js diff --git a/04-Supplemental/video 38/src/components/Error.js b/04 - Supplemental/video 38/src/components/Error.js similarity index 100% rename from 04-Supplemental/video 38/src/components/Error.js rename to 04 - Supplemental/video 38/src/components/Error.js diff --git a/04-Supplemental/video 38/src/components/JobDetails.js b/04 - Supplemental/video 38/src/components/JobDetails.js similarity index 100% rename from 04-Supplemental/video 38/src/components/JobDetails.js rename to 04 - Supplemental/video 38/src/components/JobDetails.js diff --git a/04-Supplemental/video 38/src/components/JobList.js b/04 - Supplemental/video 38/src/components/JobList.js similarity index 100% rename from 04-Supplemental/video 38/src/components/JobList.js rename to 04 - Supplemental/video 38/src/components/JobList.js diff --git a/04-Supplemental/video 38/src/components/Pagination.js b/04 - Supplemental/video 38/src/components/Pagination.js similarity index 100% rename from 04-Supplemental/video 38/src/components/Pagination.js rename to 04 - Supplemental/video 38/src/components/Pagination.js diff --git a/04-Supplemental/video 38/src/components/Router.js b/04 - Supplemental/video 38/src/components/Router.js similarity index 100% rename from 04-Supplemental/video 38/src/components/Router.js rename to 04 - Supplemental/video 38/src/components/Router.js diff --git a/04-Supplemental/video 38/src/components/Search.js b/04 - Supplemental/video 38/src/components/Search.js similarity index 100% rename from 04-Supplemental/video 38/src/components/Search.js rename to 04 - Supplemental/video 38/src/components/Search.js diff --git a/04-Supplemental/video 38/src/components/Sorting.js b/04 - Supplemental/video 38/src/components/Sorting.js similarity index 100% rename from 04-Supplemental/video 38/src/components/Sorting.js rename to 04 - Supplemental/video 38/src/components/Sorting.js diff --git a/04-Supplemental/video 38/src/components/Spinner.js b/04 - Supplemental/video 38/src/components/Spinner.js similarity index 100% rename from 04-Supplemental/video 38/src/components/Spinner.js rename to 04 - Supplemental/video 38/src/components/Spinner.js diff --git a/04-Supplemental/video 38/src/components/Storage.js b/04 - Supplemental/video 38/src/components/Storage.js similarity index 100% rename from 04-Supplemental/video 38/src/components/Storage.js rename to 04 - Supplemental/video 38/src/components/Storage.js diff --git a/04-Supplemental/video 38/src/index.css b/04 - Supplemental/video 38/src/index.css similarity index 100% rename from 04-Supplemental/video 38/src/index.css rename to 04 - Supplemental/video 38/src/index.css diff --git a/04-Supplemental/video 38/src/index.html b/04 - Supplemental/video 38/src/index.html similarity index 100% rename from 04-Supplemental/video 38/src/index.html rename to 04 - Supplemental/video 38/src/index.html diff --git a/04-Supplemental/video 38/src/index.js b/04 - Supplemental/video 38/src/index.js similarity index 100% rename from 04-Supplemental/video 38/src/index.js rename to 04 - Supplemental/video 38/src/index.js diff --git a/04-Supplemental/video 38/webpack.config.js b/04 - Supplemental/video 38/webpack.config.js similarity index 100% rename from 04-Supplemental/video 38/webpack.config.js rename to 04 - Supplemental/video 38/webpack.config.js From 2ff3f15c4c4b512b781466215483ea2c91baaf4e Mon Sep 17 00:00:00 2001 From: bytegrad Date: Mon, 13 Jun 2022 12:31:25 +0200 Subject: [PATCH 10/40] Add slides --- slides.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/slides.md b/slides.md index 1c4cd5d..0894e47 100644 --- a/slides.md +++ b/slides.md @@ -1,8 +1,2 @@ -![](https://bytegrad.com/course-assets/css/slides/1.png) -![](https://bytegrad.com/course-assets/css/slides/2.png) -![](https://bytegrad.com/course-assets/css/slides/3.png) -![](https://bytegrad.com/course-assets/css/slides/4.png) -![](https://bytegrad.com/course-assets/css/slides/5.png) -![](https://bytegrad.com/course-assets/css/slides/6.png) -![](https://bytegrad.com/course-assets/css/slides/7.png?v=2) -![](https://bytegrad.com/course-assets/css/slides/8.png) \ No newline at end of file +![](https://bytegrad.com/course-assets/js/slides/1.png) +![](https://bytegrad.com/course-assets/js/slides/2.png) \ No newline at end of file From 03b29d6e71e9fd3358dc86dde11a2ef6fc10d5f6 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Mon, 13 Jun 2022 13:21:40 +0200 Subject: [PATCH 11/40] Add more slides --- slides.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/slides.md b/slides.md index 0894e47..3e4bbd6 100644 --- a/slides.md +++ b/slides.md @@ -1,2 +1,4 @@ ![](https://bytegrad.com/course-assets/js/slides/1.png) -![](https://bytegrad.com/course-assets/js/slides/2.png) \ No newline at end of file +![](https://bytegrad.com/course-assets/js/slides/2.png) +![](https://bytegrad.com/course-assets/js/slides/3.png) +![](https://bytegrad.com/course-assets/js/slides/4.png) \ No newline at end of file From ee258e67f3fb949700de64ab57f17a20d3d53774 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Mon, 13 Jun 2022 13:22:52 +0200 Subject: [PATCH 12/40] Bust GitHub image cache --- slides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides.md b/slides.md index 3e4bbd6..6c33145 100644 --- a/slides.md +++ b/slides.md @@ -1,4 +1,4 @@ ![](https://bytegrad.com/course-assets/js/slides/1.png) -![](https://bytegrad.com/course-assets/js/slides/2.png) +![](https://bytegrad.com/course-assets/js/slides/2.png?v=2) ![](https://bytegrad.com/course-assets/js/slides/3.png) ![](https://bytegrad.com/course-assets/js/slides/4.png) \ No newline at end of file From 353c475c0a5dc2a9b4733f1b829ac92119e61d06 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Wed, 15 Jun 2022 20:00:10 +0200 Subject: [PATCH 13/40] Fix title mistake --- 02 - CorpComment/video 04/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/02 - CorpComment/video 04/index.html b/02 - CorpComment/video 04/index.html index 816df8b..a7a9d00 100644 --- a/02 - CorpComment/video 04/index.html +++ b/02 - CorpComment/video 04/index.html @@ -17,7 +17,7 @@ - afdasdf CorpComment -- Give Feedback, Publicly + CorpComment -- Give Feedback, Publicly From 24ee88a87790ebdf36a816c6cc7a722c3e3ce67d Mon Sep 17 00:00:00 2001 From: bytegrad Date: Wed, 22 Jun 2022 18:32:54 +0200 Subject: [PATCH 14/40] add alignment example --- 01 - Modern JS Fundamentals/script.js | 3 ++- 04 - Supplemental/video 36/src/components/Error.js | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/01 - Modern JS Fundamentals/script.js b/01 - Modern JS Fundamentals/script.js index 0382f0c..e5115f7 100644 --- a/01 - Modern JS Fundamentals/script.js +++ b/01 - Modern JS Fundamentals/script.js @@ -12,6 +12,7 @@ // -- traditional functions vs arrow functions -- + // function calculatePrice(sqMeters) { // return 1000 + sqMeters; // } @@ -42,7 +43,7 @@ // console.log('blabla'); // } -// price > 3000 ? console.log('hello') : console.log('blabla'); +// price > 3000 ? console.log('expensive') : console.log('cheap'); // -- manipulating HTML and CSS -- diff --git a/04 - Supplemental/video 36/src/components/Error.js b/04 - Supplemental/video 36/src/components/Error.js index 80177ed..61349e9 100644 --- a/04 - Supplemental/video 36/src/components/Error.js +++ b/04 - Supplemental/video 36/src/components/Error.js @@ -12,4 +12,9 @@ const renderError = (message = 'Something went wrong') => { }, DEFAULT_DISPLAY_TIME); }; -export default renderError; \ No newline at end of file +export default renderError; + +let a = 10, + b = 20, + c = 30, + d = 40; \ No newline at end of file From 768f5b602f3275d8bed82167a475f8174c46ac7c Mon Sep 17 00:00:00 2001 From: ByteGrad Date: Fri, 20 Jan 2023 21:03:32 +0200 Subject: [PATCH 15/40] update --- 02 - CorpComment/resources.txt | 20 +++++++ 03 - rmtDev/resources.txt | 101 +++++++++++++++++++++++++++++++++ readme.md | 7 ++- 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 02 - CorpComment/resources.txt create mode 100644 03 - rmtDev/resources.txt diff --git a/02 - CorpComment/resources.txt b/02 - CorpComment/resources.txt new file mode 100644 index 0000000..f8c41e9 --- /dev/null +++ b/02 - CorpComment/resources.txt @@ -0,0 +1,20 @@ +####### Feedback Item HTML Template + + + +####### fetch URL + +https://bytegrad.com/course-assets/js/1/api/feedbacks \ No newline at end of file diff --git a/03 - rmtDev/resources.txt b/03 - rmtDev/resources.txt new file mode 100644 index 0000000..a7e7bd4 --- /dev/null +++ b/03 - rmtDev/resources.txt @@ -0,0 +1,101 @@ +####### Selectors + +const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); +const errorEl = document.querySelector('.error'); +const errorTextEl = document.querySelector('.error__text'); +const jobDetailsEl = document.querySelector('.job-details'); +const jobDetailsContentEl = document.querySelector(".job-details__content"); +const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); +const jobListSearchEl = document.querySelector(".job-list--search"); +const numberEl = document.querySelector(".count__number"); +const paginationEl = document.querySelector(".pagination"); +const paginationBtnNextEl = document.querySelector(".pagination__button--next"); +const paginationBtnBackEl = document.querySelector(".pagination__button--back"); +const paginationNumberNextEl = document.querySelector(".pagination__number--next"); +const paginationNumberBackEl = document.querySelector(".pagination__number--back"); +const searchFormEl = document.querySelector(".search"); +const searchInputEl = document.querySelector(".search__input"); +const sortingEl = document.querySelector(".sorting"); +const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); +const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); +const spinnerSearchEl = document.querySelector(".spinner--search"); +const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); + +####### fetch URL + +https://bytegrad.com/course-assets/js/2/api/jobs + +####### Job Item HTML Template + +
  • + +
    LO
    +
    +

    Full-Stack Developer

    +

    LakeOperations

    +
    +

    Full-Time

    +

    $80,000+

    +

    Global

    +
    +
    +
    + + +
    +
    +
  • + +####### Job Details HTML Template + +# + +Apply + +
    +
    +
    VN
    +
    + + +
    +
    +
    +

    JS Developer

    +

    Verso Networks

    +

    Developers are responsible for developing and designing front end web architecture, ensuring the responsiveness of applications, and working alongside graphic designers for web design features, among other duties.

    +
    +

    Full-Time

    +

    $80,000+

    +

    Global

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    Other qualifications may apply

    +
    +
      +
    • Node.js
    • +
    +
    + +
    +
    +

    Company reviews

    +

    Recent things people are saying

    +
    +
      +
    • Only job I liked going to.
    • +
    +
    +
    + +
    + +
    \ No newline at end of file diff --git a/readme.md b/readme.md index 176ff8c..db91ecc 100644 --- a/readme.md +++ b/readme.md @@ -3,13 +3,16 @@ **You can find the final code for each video in the project folders here**. Use that if you get stuck. There is also a bugs file, a slides file and a readme file (you're reading it right now). ## Something is not working! What should I do? + Follow these steps: + 1. Check if you made any typos. Also, check your terminals (if we're using those in the videos) to see if there are errors, fix those and try again. This will fix your issue 99% of the time so make sure you've done this thoroughly. 2. Still not working? Check the file in this repo called 'bugs' and see if your problem is present and follow the steps there. 3. Still not working? Copy and paste the final code of the video you're stuck at (you can find the final code in the project folders here). If you change the **package.json** file, run the command `npm install` to make sure you have the correct dependencies installed. -4. Still not working? Please open an issue for this GitHub repository with a clear description of what's not working. Please also include copy or a screenshot of your code. This makes it faster for me to help you. +4. Still not working? Please ask the question in the Q&A section with a clear description of what's not working. I will help you as quickly as I can. But from my experience: the vast majority of your problems will be because of typos / simple mistakes. I'm online throughout most of the day and will help you as quickly as I can! In the meantime, I apologize for the inconvenience. Sometimes things break after recording the videos, but I try to be quick with any bugs that come up. ## How can I contact you? -You can always contact me by email: support@bytegrad.com, but please open an issue for this GitHub repository to submit problems with the course. + +Please use the Udemy Q&A section to ask questions. From ec0bf0d922771e0f0cea6349b82356a3c725a36b Mon Sep 17 00:00:00 2001 From: ByteGrad Date: Fri, 20 Jan 2023 22:46:54 +0200 Subject: [PATCH 16/40] change folder names to lecture --- 02 - CorpComment/{video 04 => lecture 05}/index.html | 0 02 - CorpComment/{video 04 => lecture 05}/script.js | 0 02 - CorpComment/{video 04 => lecture 05}/style.css | 0 02 - CorpComment/{video 05 => lecture 06}/index.html | 0 02 - CorpComment/{video 05 => lecture 06}/script.js | 0 02 - CorpComment/{video 05 => lecture 06}/style.css | 0 02 - CorpComment/{video 06 => lecture 07}/index.html | 0 02 - CorpComment/{video 06 => lecture 07}/script.js | 0 02 - CorpComment/{video 06 => lecture 07}/style.css | 0 02 - CorpComment/{video 07 => lecture 08}/index.html | 0 02 - CorpComment/{video 07 => lecture 08}/script.js | 0 02 - CorpComment/{video 07 => lecture 08}/style.css | 0 02 - CorpComment/{video 08 => lecture 09}/index.html | 0 02 - CorpComment/{video 08 => lecture 09}/script.js | 0 02 - CorpComment/{video 08 => lecture 09}/style.css | 0 02 - CorpComment/{video 09 => lecture 10}/index.html | 0 02 - CorpComment/{video 09 => lecture 10}/script.js | 0 02 - CorpComment/{video 09 => lecture 10}/style.css | 0 02 - CorpComment/{video 10 => lecture 11}/index.html | 0 02 - CorpComment/{video 10 => lecture 11}/script.js | 0 02 - CorpComment/{video 10 => lecture 11}/style.css | 0 02 - CorpComment/{video 11 => lecture 12}/index.html | 0 02 - CorpComment/{video 11 => lecture 12}/script.js | 0 02 - CorpComment/{video 11 => lecture 12}/style.css | 0 02 - CorpComment/{video 12 => lecture 13}/index.html | 0 02 - CorpComment/{video 12 => lecture 13}/script.js | 0 02 - CorpComment/{video 12 => lecture 13}/style.css | 0 02 - CorpComment/{video 13 => lecture 14}/index.html | 0 02 - CorpComment/{video 13 => lecture 14}/script.js | 0 02 - CorpComment/{video 13 => lecture 14}/style.css | 0 02 - CorpComment/{video 14 => lecture 15}/index.html | 0 02 - CorpComment/{video 14 => lecture 15}/script.js | 0 02 - CorpComment/{video 14 => lecture 15}/style.css | 0 03 - rmtDev/{video 16 => lecture 17}/index.css | 0 03 - rmtDev/{video 16 => lecture 17}/index.html | 0 03 - rmtDev/{video 16 => lecture 17}/index.js | 0 03 - rmtDev/{video 17 => lecture 18}/index.css | 0 03 - rmtDev/{video 17 => lecture 18}/index.html | 0 03 - rmtDev/{video 17 => lecture 18}/index.js | 0 03 - rmtDev/{video 18 => lecture 19}/index.css | 0 03 - rmtDev/{video 18 => lecture 19}/index.html | 0 03 - rmtDev/{video 18 => lecture 19}/index.js | 0 03 - rmtDev/{video 19 => lecture 20}/index.css | 0 03 - rmtDev/{video 19 => lecture 20}/index.html | 0 03 - rmtDev/{video 19 => lecture 20}/index.js | 0 03 - rmtDev/{video 19 => lecture 20}/src/common.js | 0 03 - rmtDev/{video 19 => lecture 20}/src/components/Error.js | 0 03 - rmtDev/{video 19 => lecture 20}/src/components/JobList.js | 0 03 - rmtDev/{video 19 => lecture 20}/src/components/Search.js | 0 03 - rmtDev/{video 19 => lecture 20}/src/components/Spinner.js | 0 03 - rmtDev/{video 20 => lecture 21}/index.css | 0 03 - rmtDev/{video 20 => lecture 21}/index.html | 0 03 - rmtDev/{video 20 => lecture 21}/index.js | 0 03 - rmtDev/{video 20 => lecture 21}/src/common.js | 0 03 - rmtDev/{video 20 => lecture 21}/src/components/Error.js | 0 03 - rmtDev/{video 20 => lecture 21}/src/components/JobList.js | 0 03 - rmtDev/{video 20 => lecture 21}/src/components/Search.js | 0 03 - rmtDev/{video 20 => lecture 21}/src/components/Spinner.js | 0 03 - rmtDev/{video 21 => lecture 22}/index.css | 0 03 - rmtDev/{video 21 => lecture 22}/index.html | 0 03 - rmtDev/{video 21 => lecture 22}/index.js | 0 03 - rmtDev/{video 21 => lecture 22}/src/common.js | 0 03 - rmtDev/{video 21 => lecture 22}/src/components/Error.js | 0 03 - rmtDev/{video 21 => lecture 22}/src/components/JobList.js | 0 03 - rmtDev/{video 21 => lecture 22}/src/components/Search.js | 0 03 - rmtDev/{video 21 => lecture 22}/src/components/Spinner.js | 0 03 - rmtDev/{video 22 => lecture 23}/index.css | 0 03 - rmtDev/{video 22 => lecture 23}/index.html | 0 03 - rmtDev/{video 22 => lecture 23}/index.js | 0 03 - rmtDev/{video 22 => lecture 23}/src/common.js | 0 03 - rmtDev/{video 22 => lecture 23}/src/components/Error.js | 0 03 - rmtDev/{video 22 => lecture 23}/src/components/JobDetails.js | 0 03 - rmtDev/{video 22 => lecture 23}/src/components/JobList.js | 0 03 - rmtDev/{video 22 => lecture 23}/src/components/Search.js | 0 03 - rmtDev/{video 22 => lecture 23}/src/components/Spinner.js | 0 03 - rmtDev/{video 23 => lecture 24}/index.css | 0 03 - rmtDev/{video 23 => lecture 24}/index.html | 0 03 - rmtDev/{video 23 => lecture 24}/index.js | 0 03 - rmtDev/{video 23 => lecture 24}/src/common.js | 0 03 - rmtDev/{video 23 => lecture 24}/src/components/Error.js | 0 03 - rmtDev/{video 23 => lecture 24}/src/components/JobDetails.js | 0 03 - rmtDev/{video 23 => lecture 24}/src/components/JobList.js | 0 03 - rmtDev/{video 23 => lecture 24}/src/components/Search.js | 0 03 - rmtDev/{video 23 => lecture 24}/src/components/Spinner.js | 0 03 - rmtDev/{video 24 => lecture 25}/index.css | 0 03 - rmtDev/{video 24 => lecture 25}/index.html | 0 03 - rmtDev/{video 24 => lecture 25}/index.js | 0 03 - rmtDev/{video 24 => lecture 25}/src/common.js | 0 03 - rmtDev/{video 24 => lecture 25}/src/components/Error.js | 0 03 - rmtDev/{video 24 => lecture 25}/src/components/JobDetails.js | 0 03 - rmtDev/{video 24 => lecture 25}/src/components/JobList.js | 0 03 - rmtDev/{video 24 => lecture 25}/src/components/Search.js | 0 03 - rmtDev/{video 24 => lecture 25}/src/components/Spinner.js | 0 03 - rmtDev/{video 25 => lecture 26}/index.css | 0 03 - rmtDev/{video 25 => lecture 26}/index.html | 0 03 - rmtDev/{video 25 => lecture 26}/index.js | 0 03 - rmtDev/{video 25 => lecture 26}/src/common.js | 0 03 - rmtDev/{video 25 => lecture 26}/src/components/Error.js | 0 03 - rmtDev/{video 25 => lecture 26}/src/components/JobDetails.js | 0 03 - rmtDev/{video 25 => lecture 26}/src/components/JobList.js | 0 03 - rmtDev/{video 25 => lecture 26}/src/components/Search.js | 0 03 - rmtDev/{video 25 => lecture 26}/src/components/Spinner.js | 0 03 - rmtDev/{video 26 => lecture 27}/index.css | 0 03 - rmtDev/{video 26 => lecture 27}/index.html | 0 03 - rmtDev/{video 26 => lecture 27}/index.js | 0 03 - rmtDev/{video 26 => lecture 27}/src/common.js | 0 03 - rmtDev/{video 26 => lecture 27}/src/components/Error.js | 0 03 - rmtDev/{video 26 => lecture 27}/src/components/JobDetails.js | 0 03 - rmtDev/{video 26 => lecture 27}/src/components/JobList.js | 0 03 - rmtDev/{video 26 => lecture 27}/src/components/Search.js | 0 03 - rmtDev/{video 26 => lecture 27}/src/components/Sorting.js | 0 03 - rmtDev/{video 26 => lecture 27}/src/components/Spinner.js | 0 03 - rmtDev/{video 27 => lecture 28}/index.css | 0 03 - rmtDev/{video 27 => lecture 28}/index.html | 0 03 - rmtDev/{video 27 => lecture 28}/index.js | 0 03 - rmtDev/{video 27 => lecture 28}/src/common.js | 0 03 - rmtDev/{video 27 => lecture 28}/src/components/Error.js | 0 03 - rmtDev/{video 27 => lecture 28}/src/components/JobDetails.js | 0 03 - rmtDev/{video 27 => lecture 28}/src/components/JobList.js | 0 03 - rmtDev/{video 27 => lecture 28}/src/components/Search.js | 0 03 - rmtDev/{video 27 => lecture 28}/src/components/Sorting.js | 0 03 - rmtDev/{video 27 => lecture 28}/src/components/Spinner.js | 0 03 - rmtDev/{video 28 => lecture 29}/index.css | 0 03 - rmtDev/{video 28 => lecture 29}/index.html | 0 03 - rmtDev/{video 28 => lecture 29}/index.js | 0 03 - rmtDev/{video 28 => lecture 29}/src/common.js | 0 03 - rmtDev/{video 28 => lecture 29}/src/components/Error.js | 0 03 - rmtDev/{video 28 => lecture 29}/src/components/JobDetails.js | 0 03 - rmtDev/{video 28 => lecture 29}/src/components/JobList.js | 0 03 - rmtDev/{video 28 => lecture 29}/src/components/Search.js | 0 03 - rmtDev/{video 28 => lecture 29}/src/components/Sorting.js | 0 03 - rmtDev/{video 28 => lecture 29}/src/components/Spinner.js | 0 03 - rmtDev/{video 29 => lecture 30}/index.css | 0 03 - rmtDev/{video 29 => lecture 30}/index.html | 0 03 - rmtDev/{video 29 => lecture 30}/index.js | 0 03 - rmtDev/{video 29 => lecture 30}/src/common.js | 0 03 - rmtDev/{video 29 => lecture 30}/src/components/Error.js | 0 03 - rmtDev/{video 29 => lecture 30}/src/components/JobDetails.js | 0 03 - rmtDev/{video 29 => lecture 30}/src/components/JobList.js | 0 03 - rmtDev/{video 29 => lecture 30}/src/components/Pagination.js | 0 03 - rmtDev/{video 29 => lecture 30}/src/components/Search.js | 0 03 - rmtDev/{video 29 => lecture 30}/src/components/Sorting.js | 0 03 - rmtDev/{video 29 => lecture 30}/src/components/Spinner.js | 0 03 - rmtDev/{video 30 => lecture 31}/index.css | 0 03 - rmtDev/{video 30 => lecture 31}/index.html | 0 03 - rmtDev/{video 30 => lecture 31}/index.js | 0 03 - rmtDev/{video 30 => lecture 31}/src/common.js | 0 03 - rmtDev/{video 30 => lecture 31}/src/components/Error.js | 0 03 - rmtDev/{video 30 => lecture 31}/src/components/JobDetails.js | 0 03 - rmtDev/{video 30 => lecture 31}/src/components/JobList.js | 0 03 - rmtDev/{video 30 => lecture 31}/src/components/Pagination.js | 0 03 - rmtDev/{video 30 => lecture 31}/src/components/Router.js | 0 03 - rmtDev/{video 30 => lecture 31}/src/components/Search.js | 0 03 - rmtDev/{video 30 => lecture 31}/src/components/Sorting.js | 0 03 - rmtDev/{video 30 => lecture 31}/src/components/Spinner.js | 0 03 - rmtDev/{video 31 => lecture 32}/index.css | 0 03 - rmtDev/{video 31 => lecture 32}/index.html | 0 03 - rmtDev/{video 31 => lecture 32}/index.js | 0 03 - rmtDev/{video 31 => lecture 32}/src/common.js | 0 03 - rmtDev/{video 31 => lecture 32}/src/components/Error.js | 0 03 - rmtDev/{video 31 => lecture 32}/src/components/JobDetails.js | 0 03 - rmtDev/{video 31 => lecture 32}/src/components/JobList.js | 0 03 - rmtDev/{video 31 => lecture 32}/src/components/Pagination.js | 0 03 - rmtDev/{video 31 => lecture 32}/src/components/Router.js | 0 03 - rmtDev/{video 31 => lecture 32}/src/components/Search.js | 0 03 - rmtDev/{video 31 => lecture 32}/src/components/Sorting.js | 0 03 - rmtDev/{video 31 => lecture 32}/src/components/Spinner.js | 0 03 - rmtDev/{video 32 => lecture 33}/index.css | 0 03 - rmtDev/{video 32 => lecture 33}/index.html | 0 03 - rmtDev/{video 32 => lecture 33}/index.js | 0 03 - rmtDev/{video 32 => lecture 33}/src/common.js | 0 03 - rmtDev/{video 32 => lecture 33}/src/components/Bookmarks.js | 0 03 - rmtDev/{video 32 => lecture 33}/src/components/Error.js | 0 03 - rmtDev/{video 32 => lecture 33}/src/components/JobDetails.js | 0 03 - rmtDev/{video 32 => lecture 33}/src/components/JobList.js | 0 03 - rmtDev/{video 32 => lecture 33}/src/components/Pagination.js | 0 03 - rmtDev/{video 32 => lecture 33}/src/components/Router.js | 0 03 - rmtDev/{video 32 => lecture 33}/src/components/Search.js | 0 03 - rmtDev/{video 32 => lecture 33}/src/components/Sorting.js | 0 03 - rmtDev/{video 32 => lecture 33}/src/components/Spinner.js | 0 03 - rmtDev/{video 33 => lecture 34}/index.css | 0 03 - rmtDev/{video 33 => lecture 34}/index.html | 0 03 - rmtDev/{video 33 => lecture 34}/index.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/common.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/components/Bookmarks.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/components/Error.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/components/JobDetails.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/components/JobList.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/components/Pagination.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/components/Router.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/components/Search.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/components/Sorting.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/components/Spinner.js | 0 03 - rmtDev/{video 33 => lecture 34}/src/components/Storage.js | 0 03 - rmtDev/{video 34 => lecture 35}/index.css | 0 03 - rmtDev/{video 34 => lecture 35}/index.html | 0 03 - rmtDev/{video 34 => lecture 35}/index.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/common.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/components/Bookmarks.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/components/Error.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/components/JobDetails.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/components/JobList.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/components/Pagination.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/components/Router.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/components/Search.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/components/Sorting.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/components/Spinner.js | 0 03 - rmtDev/{video 34 => lecture 35}/src/components/Storage.js | 0 03 - rmtDev/{video 35 => lecture 36}/babel.config.js | 0 03 - rmtDev/{video 35 => lecture 36}/dist/index.html | 0 03 - rmtDev/{video 35 => lecture 36}/dist/main.css | 0 03 - rmtDev/{video 35 => lecture 36}/dist/main.js | 0 03 - rmtDev/{video 35 => lecture 36}/dist/main.js.LICENSE.txt | 0 03 - rmtDev/{video 35 => lecture 36}/package-lock.json | 0 03 - rmtDev/{video 35 => lecture 36}/package.json | 0 03 - rmtDev/{video 35 => lecture 36}/postcss.config.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/common.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/components/Bookmarks.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/components/Error.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/components/JobDetails.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/components/JobList.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/components/Pagination.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/components/Router.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/components/Search.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/components/Sorting.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/components/Spinner.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/components/Storage.js | 0 03 - rmtDev/{video 35 => lecture 36}/src/index.css | 0 03 - rmtDev/{video 35 => lecture 36}/src/index.html | 0 03 - rmtDev/{video 35 => lecture 36}/src/index.js | 0 03 - rmtDev/{video 35 => lecture 36}/webpack.config.js | 0 231 files changed, 0 insertions(+), 0 deletions(-) rename 02 - CorpComment/{video 04 => lecture 05}/index.html (100%) rename 02 - CorpComment/{video 04 => lecture 05}/script.js (100%) rename 02 - CorpComment/{video 04 => lecture 05}/style.css (100%) rename 02 - CorpComment/{video 05 => lecture 06}/index.html (100%) rename 02 - CorpComment/{video 05 => lecture 06}/script.js (100%) rename 02 - CorpComment/{video 05 => lecture 06}/style.css (100%) rename 02 - CorpComment/{video 06 => lecture 07}/index.html (100%) rename 02 - CorpComment/{video 06 => lecture 07}/script.js (100%) rename 02 - CorpComment/{video 06 => lecture 07}/style.css (100%) rename 02 - CorpComment/{video 07 => lecture 08}/index.html (100%) rename 02 - CorpComment/{video 07 => lecture 08}/script.js (100%) rename 02 - CorpComment/{video 07 => lecture 08}/style.css (100%) rename 02 - CorpComment/{video 08 => lecture 09}/index.html (100%) rename 02 - CorpComment/{video 08 => lecture 09}/script.js (100%) rename 02 - CorpComment/{video 08 => lecture 09}/style.css (100%) rename 02 - CorpComment/{video 09 => lecture 10}/index.html (100%) rename 02 - CorpComment/{video 09 => lecture 10}/script.js (100%) rename 02 - CorpComment/{video 09 => lecture 10}/style.css (100%) rename 02 - CorpComment/{video 10 => lecture 11}/index.html (100%) rename 02 - CorpComment/{video 10 => lecture 11}/script.js (100%) rename 02 - CorpComment/{video 10 => lecture 11}/style.css (100%) rename 02 - CorpComment/{video 11 => lecture 12}/index.html (100%) rename 02 - CorpComment/{video 11 => lecture 12}/script.js (100%) rename 02 - CorpComment/{video 11 => lecture 12}/style.css (100%) rename 02 - CorpComment/{video 12 => lecture 13}/index.html (100%) rename 02 - CorpComment/{video 12 => lecture 13}/script.js (100%) rename 02 - CorpComment/{video 12 => lecture 13}/style.css (100%) rename 02 - CorpComment/{video 13 => lecture 14}/index.html (100%) rename 02 - CorpComment/{video 13 => lecture 14}/script.js (100%) rename 02 - CorpComment/{video 13 => lecture 14}/style.css (100%) rename 02 - CorpComment/{video 14 => lecture 15}/index.html (100%) rename 02 - CorpComment/{video 14 => lecture 15}/script.js (100%) rename 02 - CorpComment/{video 14 => lecture 15}/style.css (100%) rename 03 - rmtDev/{video 16 => lecture 17}/index.css (100%) rename 03 - rmtDev/{video 16 => lecture 17}/index.html (100%) rename 03 - rmtDev/{video 16 => lecture 17}/index.js (100%) rename 03 - rmtDev/{video 17 => lecture 18}/index.css (100%) rename 03 - rmtDev/{video 17 => lecture 18}/index.html (100%) rename 03 - rmtDev/{video 17 => lecture 18}/index.js (100%) rename 03 - rmtDev/{video 18 => lecture 19}/index.css (100%) rename 03 - rmtDev/{video 18 => lecture 19}/index.html (100%) rename 03 - rmtDev/{video 18 => lecture 19}/index.js (100%) rename 03 - rmtDev/{video 19 => lecture 20}/index.css (100%) rename 03 - rmtDev/{video 19 => lecture 20}/index.html (100%) rename 03 - rmtDev/{video 19 => lecture 20}/index.js (100%) rename 03 - rmtDev/{video 19 => lecture 20}/src/common.js (100%) rename 03 - rmtDev/{video 19 => lecture 20}/src/components/Error.js (100%) rename 03 - rmtDev/{video 19 => lecture 20}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 19 => lecture 20}/src/components/Search.js (100%) rename 03 - rmtDev/{video 19 => lecture 20}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 20 => lecture 21}/index.css (100%) rename 03 - rmtDev/{video 20 => lecture 21}/index.html (100%) rename 03 - rmtDev/{video 20 => lecture 21}/index.js (100%) rename 03 - rmtDev/{video 20 => lecture 21}/src/common.js (100%) rename 03 - rmtDev/{video 20 => lecture 21}/src/components/Error.js (100%) rename 03 - rmtDev/{video 20 => lecture 21}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 20 => lecture 21}/src/components/Search.js (100%) rename 03 - rmtDev/{video 20 => lecture 21}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 21 => lecture 22}/index.css (100%) rename 03 - rmtDev/{video 21 => lecture 22}/index.html (100%) rename 03 - rmtDev/{video 21 => lecture 22}/index.js (100%) rename 03 - rmtDev/{video 21 => lecture 22}/src/common.js (100%) rename 03 - rmtDev/{video 21 => lecture 22}/src/components/Error.js (100%) rename 03 - rmtDev/{video 21 => lecture 22}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 21 => lecture 22}/src/components/Search.js (100%) rename 03 - rmtDev/{video 21 => lecture 22}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 22 => lecture 23}/index.css (100%) rename 03 - rmtDev/{video 22 => lecture 23}/index.html (100%) rename 03 - rmtDev/{video 22 => lecture 23}/index.js (100%) rename 03 - rmtDev/{video 22 => lecture 23}/src/common.js (100%) rename 03 - rmtDev/{video 22 => lecture 23}/src/components/Error.js (100%) rename 03 - rmtDev/{video 22 => lecture 23}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 22 => lecture 23}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 22 => lecture 23}/src/components/Search.js (100%) rename 03 - rmtDev/{video 22 => lecture 23}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 23 => lecture 24}/index.css (100%) rename 03 - rmtDev/{video 23 => lecture 24}/index.html (100%) rename 03 - rmtDev/{video 23 => lecture 24}/index.js (100%) rename 03 - rmtDev/{video 23 => lecture 24}/src/common.js (100%) rename 03 - rmtDev/{video 23 => lecture 24}/src/components/Error.js (100%) rename 03 - rmtDev/{video 23 => lecture 24}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 23 => lecture 24}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 23 => lecture 24}/src/components/Search.js (100%) rename 03 - rmtDev/{video 23 => lecture 24}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 24 => lecture 25}/index.css (100%) rename 03 - rmtDev/{video 24 => lecture 25}/index.html (100%) rename 03 - rmtDev/{video 24 => lecture 25}/index.js (100%) rename 03 - rmtDev/{video 24 => lecture 25}/src/common.js (100%) rename 03 - rmtDev/{video 24 => lecture 25}/src/components/Error.js (100%) rename 03 - rmtDev/{video 24 => lecture 25}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 24 => lecture 25}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 24 => lecture 25}/src/components/Search.js (100%) rename 03 - rmtDev/{video 24 => lecture 25}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 25 => lecture 26}/index.css (100%) rename 03 - rmtDev/{video 25 => lecture 26}/index.html (100%) rename 03 - rmtDev/{video 25 => lecture 26}/index.js (100%) rename 03 - rmtDev/{video 25 => lecture 26}/src/common.js (100%) rename 03 - rmtDev/{video 25 => lecture 26}/src/components/Error.js (100%) rename 03 - rmtDev/{video 25 => lecture 26}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 25 => lecture 26}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 25 => lecture 26}/src/components/Search.js (100%) rename 03 - rmtDev/{video 25 => lecture 26}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 26 => lecture 27}/index.css (100%) rename 03 - rmtDev/{video 26 => lecture 27}/index.html (100%) rename 03 - rmtDev/{video 26 => lecture 27}/index.js (100%) rename 03 - rmtDev/{video 26 => lecture 27}/src/common.js (100%) rename 03 - rmtDev/{video 26 => lecture 27}/src/components/Error.js (100%) rename 03 - rmtDev/{video 26 => lecture 27}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 26 => lecture 27}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 26 => lecture 27}/src/components/Search.js (100%) rename 03 - rmtDev/{video 26 => lecture 27}/src/components/Sorting.js (100%) rename 03 - rmtDev/{video 26 => lecture 27}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 27 => lecture 28}/index.css (100%) rename 03 - rmtDev/{video 27 => lecture 28}/index.html (100%) rename 03 - rmtDev/{video 27 => lecture 28}/index.js (100%) rename 03 - rmtDev/{video 27 => lecture 28}/src/common.js (100%) rename 03 - rmtDev/{video 27 => lecture 28}/src/components/Error.js (100%) rename 03 - rmtDev/{video 27 => lecture 28}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 27 => lecture 28}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 27 => lecture 28}/src/components/Search.js (100%) rename 03 - rmtDev/{video 27 => lecture 28}/src/components/Sorting.js (100%) rename 03 - rmtDev/{video 27 => lecture 28}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 28 => lecture 29}/index.css (100%) rename 03 - rmtDev/{video 28 => lecture 29}/index.html (100%) rename 03 - rmtDev/{video 28 => lecture 29}/index.js (100%) rename 03 - rmtDev/{video 28 => lecture 29}/src/common.js (100%) rename 03 - rmtDev/{video 28 => lecture 29}/src/components/Error.js (100%) rename 03 - rmtDev/{video 28 => lecture 29}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 28 => lecture 29}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 28 => lecture 29}/src/components/Search.js (100%) rename 03 - rmtDev/{video 28 => lecture 29}/src/components/Sorting.js (100%) rename 03 - rmtDev/{video 28 => lecture 29}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 29 => lecture 30}/index.css (100%) rename 03 - rmtDev/{video 29 => lecture 30}/index.html (100%) rename 03 - rmtDev/{video 29 => lecture 30}/index.js (100%) rename 03 - rmtDev/{video 29 => lecture 30}/src/common.js (100%) rename 03 - rmtDev/{video 29 => lecture 30}/src/components/Error.js (100%) rename 03 - rmtDev/{video 29 => lecture 30}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 29 => lecture 30}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 29 => lecture 30}/src/components/Pagination.js (100%) rename 03 - rmtDev/{video 29 => lecture 30}/src/components/Search.js (100%) rename 03 - rmtDev/{video 29 => lecture 30}/src/components/Sorting.js (100%) rename 03 - rmtDev/{video 29 => lecture 30}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 30 => lecture 31}/index.css (100%) rename 03 - rmtDev/{video 30 => lecture 31}/index.html (100%) rename 03 - rmtDev/{video 30 => lecture 31}/index.js (100%) rename 03 - rmtDev/{video 30 => lecture 31}/src/common.js (100%) rename 03 - rmtDev/{video 30 => lecture 31}/src/components/Error.js (100%) rename 03 - rmtDev/{video 30 => lecture 31}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 30 => lecture 31}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 30 => lecture 31}/src/components/Pagination.js (100%) rename 03 - rmtDev/{video 30 => lecture 31}/src/components/Router.js (100%) rename 03 - rmtDev/{video 30 => lecture 31}/src/components/Search.js (100%) rename 03 - rmtDev/{video 30 => lecture 31}/src/components/Sorting.js (100%) rename 03 - rmtDev/{video 30 => lecture 31}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 31 => lecture 32}/index.css (100%) rename 03 - rmtDev/{video 31 => lecture 32}/index.html (100%) rename 03 - rmtDev/{video 31 => lecture 32}/index.js (100%) rename 03 - rmtDev/{video 31 => lecture 32}/src/common.js (100%) rename 03 - rmtDev/{video 31 => lecture 32}/src/components/Error.js (100%) rename 03 - rmtDev/{video 31 => lecture 32}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 31 => lecture 32}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 31 => lecture 32}/src/components/Pagination.js (100%) rename 03 - rmtDev/{video 31 => lecture 32}/src/components/Router.js (100%) rename 03 - rmtDev/{video 31 => lecture 32}/src/components/Search.js (100%) rename 03 - rmtDev/{video 31 => lecture 32}/src/components/Sorting.js (100%) rename 03 - rmtDev/{video 31 => lecture 32}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/index.css (100%) rename 03 - rmtDev/{video 32 => lecture 33}/index.html (100%) rename 03 - rmtDev/{video 32 => lecture 33}/index.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/src/common.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/src/components/Bookmarks.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/src/components/Error.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/src/components/Pagination.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/src/components/Router.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/src/components/Search.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/src/components/Sorting.js (100%) rename 03 - rmtDev/{video 32 => lecture 33}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/index.css (100%) rename 03 - rmtDev/{video 33 => lecture 34}/index.html (100%) rename 03 - rmtDev/{video 33 => lecture 34}/index.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/common.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/components/Bookmarks.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/components/Error.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/components/Pagination.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/components/Router.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/components/Search.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/components/Sorting.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 33 => lecture 34}/src/components/Storage.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/index.css (100%) rename 03 - rmtDev/{video 34 => lecture 35}/index.html (100%) rename 03 - rmtDev/{video 34 => lecture 35}/index.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/common.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/components/Bookmarks.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/components/Error.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/components/Pagination.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/components/Router.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/components/Search.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/components/Sorting.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 34 => lecture 35}/src/components/Storage.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/babel.config.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/dist/index.html (100%) rename 03 - rmtDev/{video 35 => lecture 36}/dist/main.css (100%) rename 03 - rmtDev/{video 35 => lecture 36}/dist/main.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/dist/main.js.LICENSE.txt (100%) rename 03 - rmtDev/{video 35 => lecture 36}/package-lock.json (100%) rename 03 - rmtDev/{video 35 => lecture 36}/package.json (100%) rename 03 - rmtDev/{video 35 => lecture 36}/postcss.config.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/common.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/components/Bookmarks.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/components/Error.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/components/JobDetails.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/components/JobList.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/components/Pagination.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/components/Router.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/components/Search.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/components/Sorting.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/components/Spinner.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/components/Storage.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/index.css (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/index.html (100%) rename 03 - rmtDev/{video 35 => lecture 36}/src/index.js (100%) rename 03 - rmtDev/{video 35 => lecture 36}/webpack.config.js (100%) diff --git a/02 - CorpComment/video 04/index.html b/02 - CorpComment/lecture 05/index.html similarity index 100% rename from 02 - CorpComment/video 04/index.html rename to 02 - CorpComment/lecture 05/index.html diff --git a/02 - CorpComment/video 04/script.js b/02 - CorpComment/lecture 05/script.js similarity index 100% rename from 02 - CorpComment/video 04/script.js rename to 02 - CorpComment/lecture 05/script.js diff --git a/02 - CorpComment/video 04/style.css b/02 - CorpComment/lecture 05/style.css similarity index 100% rename from 02 - CorpComment/video 04/style.css rename to 02 - CorpComment/lecture 05/style.css diff --git a/02 - CorpComment/video 05/index.html b/02 - CorpComment/lecture 06/index.html similarity index 100% rename from 02 - CorpComment/video 05/index.html rename to 02 - CorpComment/lecture 06/index.html diff --git a/02 - CorpComment/video 05/script.js b/02 - CorpComment/lecture 06/script.js similarity index 100% rename from 02 - CorpComment/video 05/script.js rename to 02 - CorpComment/lecture 06/script.js diff --git a/02 - CorpComment/video 05/style.css b/02 - CorpComment/lecture 06/style.css similarity index 100% rename from 02 - CorpComment/video 05/style.css rename to 02 - CorpComment/lecture 06/style.css diff --git a/02 - CorpComment/video 06/index.html b/02 - CorpComment/lecture 07/index.html similarity index 100% rename from 02 - CorpComment/video 06/index.html rename to 02 - CorpComment/lecture 07/index.html diff --git a/02 - CorpComment/video 06/script.js b/02 - CorpComment/lecture 07/script.js similarity index 100% rename from 02 - CorpComment/video 06/script.js rename to 02 - CorpComment/lecture 07/script.js diff --git a/02 - CorpComment/video 06/style.css b/02 - CorpComment/lecture 07/style.css similarity index 100% rename from 02 - CorpComment/video 06/style.css rename to 02 - CorpComment/lecture 07/style.css diff --git a/02 - CorpComment/video 07/index.html b/02 - CorpComment/lecture 08/index.html similarity index 100% rename from 02 - CorpComment/video 07/index.html rename to 02 - CorpComment/lecture 08/index.html diff --git a/02 - CorpComment/video 07/script.js b/02 - CorpComment/lecture 08/script.js similarity index 100% rename from 02 - CorpComment/video 07/script.js rename to 02 - CorpComment/lecture 08/script.js diff --git a/02 - CorpComment/video 07/style.css b/02 - CorpComment/lecture 08/style.css similarity index 100% rename from 02 - CorpComment/video 07/style.css rename to 02 - CorpComment/lecture 08/style.css diff --git a/02 - CorpComment/video 08/index.html b/02 - CorpComment/lecture 09/index.html similarity index 100% rename from 02 - CorpComment/video 08/index.html rename to 02 - CorpComment/lecture 09/index.html diff --git a/02 - CorpComment/video 08/script.js b/02 - CorpComment/lecture 09/script.js similarity index 100% rename from 02 - CorpComment/video 08/script.js rename to 02 - CorpComment/lecture 09/script.js diff --git a/02 - CorpComment/video 08/style.css b/02 - CorpComment/lecture 09/style.css similarity index 100% rename from 02 - CorpComment/video 08/style.css rename to 02 - CorpComment/lecture 09/style.css diff --git a/02 - CorpComment/video 09/index.html b/02 - CorpComment/lecture 10/index.html similarity index 100% rename from 02 - CorpComment/video 09/index.html rename to 02 - CorpComment/lecture 10/index.html diff --git a/02 - CorpComment/video 09/script.js b/02 - CorpComment/lecture 10/script.js similarity index 100% rename from 02 - CorpComment/video 09/script.js rename to 02 - CorpComment/lecture 10/script.js diff --git a/02 - CorpComment/video 09/style.css b/02 - CorpComment/lecture 10/style.css similarity index 100% rename from 02 - CorpComment/video 09/style.css rename to 02 - CorpComment/lecture 10/style.css diff --git a/02 - CorpComment/video 10/index.html b/02 - CorpComment/lecture 11/index.html similarity index 100% rename from 02 - CorpComment/video 10/index.html rename to 02 - CorpComment/lecture 11/index.html diff --git a/02 - CorpComment/video 10/script.js b/02 - CorpComment/lecture 11/script.js similarity index 100% rename from 02 - CorpComment/video 10/script.js rename to 02 - CorpComment/lecture 11/script.js diff --git a/02 - CorpComment/video 10/style.css b/02 - CorpComment/lecture 11/style.css similarity index 100% rename from 02 - CorpComment/video 10/style.css rename to 02 - CorpComment/lecture 11/style.css diff --git a/02 - CorpComment/video 11/index.html b/02 - CorpComment/lecture 12/index.html similarity index 100% rename from 02 - CorpComment/video 11/index.html rename to 02 - CorpComment/lecture 12/index.html diff --git a/02 - CorpComment/video 11/script.js b/02 - CorpComment/lecture 12/script.js similarity index 100% rename from 02 - CorpComment/video 11/script.js rename to 02 - CorpComment/lecture 12/script.js diff --git a/02 - CorpComment/video 11/style.css b/02 - CorpComment/lecture 12/style.css similarity index 100% rename from 02 - CorpComment/video 11/style.css rename to 02 - CorpComment/lecture 12/style.css diff --git a/02 - CorpComment/video 12/index.html b/02 - CorpComment/lecture 13/index.html similarity index 100% rename from 02 - CorpComment/video 12/index.html rename to 02 - CorpComment/lecture 13/index.html diff --git a/02 - CorpComment/video 12/script.js b/02 - CorpComment/lecture 13/script.js similarity index 100% rename from 02 - CorpComment/video 12/script.js rename to 02 - CorpComment/lecture 13/script.js diff --git a/02 - CorpComment/video 12/style.css b/02 - CorpComment/lecture 13/style.css similarity index 100% rename from 02 - CorpComment/video 12/style.css rename to 02 - CorpComment/lecture 13/style.css diff --git a/02 - CorpComment/video 13/index.html b/02 - CorpComment/lecture 14/index.html similarity index 100% rename from 02 - CorpComment/video 13/index.html rename to 02 - CorpComment/lecture 14/index.html diff --git a/02 - CorpComment/video 13/script.js b/02 - CorpComment/lecture 14/script.js similarity index 100% rename from 02 - CorpComment/video 13/script.js rename to 02 - CorpComment/lecture 14/script.js diff --git a/02 - CorpComment/video 13/style.css b/02 - CorpComment/lecture 14/style.css similarity index 100% rename from 02 - CorpComment/video 13/style.css rename to 02 - CorpComment/lecture 14/style.css diff --git a/02 - CorpComment/video 14/index.html b/02 - CorpComment/lecture 15/index.html similarity index 100% rename from 02 - CorpComment/video 14/index.html rename to 02 - CorpComment/lecture 15/index.html diff --git a/02 - CorpComment/video 14/script.js b/02 - CorpComment/lecture 15/script.js similarity index 100% rename from 02 - CorpComment/video 14/script.js rename to 02 - CorpComment/lecture 15/script.js diff --git a/02 - CorpComment/video 14/style.css b/02 - CorpComment/lecture 15/style.css similarity index 100% rename from 02 - CorpComment/video 14/style.css rename to 02 - CorpComment/lecture 15/style.css diff --git a/03 - rmtDev/video 16/index.css b/03 - rmtDev/lecture 17/index.css similarity index 100% rename from 03 - rmtDev/video 16/index.css rename to 03 - rmtDev/lecture 17/index.css diff --git a/03 - rmtDev/video 16/index.html b/03 - rmtDev/lecture 17/index.html similarity index 100% rename from 03 - rmtDev/video 16/index.html rename to 03 - rmtDev/lecture 17/index.html diff --git a/03 - rmtDev/video 16/index.js b/03 - rmtDev/lecture 17/index.js similarity index 100% rename from 03 - rmtDev/video 16/index.js rename to 03 - rmtDev/lecture 17/index.js diff --git a/03 - rmtDev/video 17/index.css b/03 - rmtDev/lecture 18/index.css similarity index 100% rename from 03 - rmtDev/video 17/index.css rename to 03 - rmtDev/lecture 18/index.css diff --git a/03 - rmtDev/video 17/index.html b/03 - rmtDev/lecture 18/index.html similarity index 100% rename from 03 - rmtDev/video 17/index.html rename to 03 - rmtDev/lecture 18/index.html diff --git a/03 - rmtDev/video 17/index.js b/03 - rmtDev/lecture 18/index.js similarity index 100% rename from 03 - rmtDev/video 17/index.js rename to 03 - rmtDev/lecture 18/index.js diff --git a/03 - rmtDev/video 18/index.css b/03 - rmtDev/lecture 19/index.css similarity index 100% rename from 03 - rmtDev/video 18/index.css rename to 03 - rmtDev/lecture 19/index.css diff --git a/03 - rmtDev/video 18/index.html b/03 - rmtDev/lecture 19/index.html similarity index 100% rename from 03 - rmtDev/video 18/index.html rename to 03 - rmtDev/lecture 19/index.html diff --git a/03 - rmtDev/video 18/index.js b/03 - rmtDev/lecture 19/index.js similarity index 100% rename from 03 - rmtDev/video 18/index.js rename to 03 - rmtDev/lecture 19/index.js diff --git a/03 - rmtDev/video 19/index.css b/03 - rmtDev/lecture 20/index.css similarity index 100% rename from 03 - rmtDev/video 19/index.css rename to 03 - rmtDev/lecture 20/index.css diff --git a/03 - rmtDev/video 19/index.html b/03 - rmtDev/lecture 20/index.html similarity index 100% rename from 03 - rmtDev/video 19/index.html rename to 03 - rmtDev/lecture 20/index.html diff --git a/03 - rmtDev/video 19/index.js b/03 - rmtDev/lecture 20/index.js similarity index 100% rename from 03 - rmtDev/video 19/index.js rename to 03 - rmtDev/lecture 20/index.js diff --git a/03 - rmtDev/video 19/src/common.js b/03 - rmtDev/lecture 20/src/common.js similarity index 100% rename from 03 - rmtDev/video 19/src/common.js rename to 03 - rmtDev/lecture 20/src/common.js diff --git a/03 - rmtDev/video 19/src/components/Error.js b/03 - rmtDev/lecture 20/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 19/src/components/Error.js rename to 03 - rmtDev/lecture 20/src/components/Error.js diff --git a/03 - rmtDev/video 19/src/components/JobList.js b/03 - rmtDev/lecture 20/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 19/src/components/JobList.js rename to 03 - rmtDev/lecture 20/src/components/JobList.js diff --git a/03 - rmtDev/video 19/src/components/Search.js b/03 - rmtDev/lecture 20/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 19/src/components/Search.js rename to 03 - rmtDev/lecture 20/src/components/Search.js diff --git a/03 - rmtDev/video 19/src/components/Spinner.js b/03 - rmtDev/lecture 20/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 19/src/components/Spinner.js rename to 03 - rmtDev/lecture 20/src/components/Spinner.js diff --git a/03 - rmtDev/video 20/index.css b/03 - rmtDev/lecture 21/index.css similarity index 100% rename from 03 - rmtDev/video 20/index.css rename to 03 - rmtDev/lecture 21/index.css diff --git a/03 - rmtDev/video 20/index.html b/03 - rmtDev/lecture 21/index.html similarity index 100% rename from 03 - rmtDev/video 20/index.html rename to 03 - rmtDev/lecture 21/index.html diff --git a/03 - rmtDev/video 20/index.js b/03 - rmtDev/lecture 21/index.js similarity index 100% rename from 03 - rmtDev/video 20/index.js rename to 03 - rmtDev/lecture 21/index.js diff --git a/03 - rmtDev/video 20/src/common.js b/03 - rmtDev/lecture 21/src/common.js similarity index 100% rename from 03 - rmtDev/video 20/src/common.js rename to 03 - rmtDev/lecture 21/src/common.js diff --git a/03 - rmtDev/video 20/src/components/Error.js b/03 - rmtDev/lecture 21/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 20/src/components/Error.js rename to 03 - rmtDev/lecture 21/src/components/Error.js diff --git a/03 - rmtDev/video 20/src/components/JobList.js b/03 - rmtDev/lecture 21/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 20/src/components/JobList.js rename to 03 - rmtDev/lecture 21/src/components/JobList.js diff --git a/03 - rmtDev/video 20/src/components/Search.js b/03 - rmtDev/lecture 21/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 20/src/components/Search.js rename to 03 - rmtDev/lecture 21/src/components/Search.js diff --git a/03 - rmtDev/video 20/src/components/Spinner.js b/03 - rmtDev/lecture 21/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 20/src/components/Spinner.js rename to 03 - rmtDev/lecture 21/src/components/Spinner.js diff --git a/03 - rmtDev/video 21/index.css b/03 - rmtDev/lecture 22/index.css similarity index 100% rename from 03 - rmtDev/video 21/index.css rename to 03 - rmtDev/lecture 22/index.css diff --git a/03 - rmtDev/video 21/index.html b/03 - rmtDev/lecture 22/index.html similarity index 100% rename from 03 - rmtDev/video 21/index.html rename to 03 - rmtDev/lecture 22/index.html diff --git a/03 - rmtDev/video 21/index.js b/03 - rmtDev/lecture 22/index.js similarity index 100% rename from 03 - rmtDev/video 21/index.js rename to 03 - rmtDev/lecture 22/index.js diff --git a/03 - rmtDev/video 21/src/common.js b/03 - rmtDev/lecture 22/src/common.js similarity index 100% rename from 03 - rmtDev/video 21/src/common.js rename to 03 - rmtDev/lecture 22/src/common.js diff --git a/03 - rmtDev/video 21/src/components/Error.js b/03 - rmtDev/lecture 22/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 21/src/components/Error.js rename to 03 - rmtDev/lecture 22/src/components/Error.js diff --git a/03 - rmtDev/video 21/src/components/JobList.js b/03 - rmtDev/lecture 22/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 21/src/components/JobList.js rename to 03 - rmtDev/lecture 22/src/components/JobList.js diff --git a/03 - rmtDev/video 21/src/components/Search.js b/03 - rmtDev/lecture 22/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 21/src/components/Search.js rename to 03 - rmtDev/lecture 22/src/components/Search.js diff --git a/03 - rmtDev/video 21/src/components/Spinner.js b/03 - rmtDev/lecture 22/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 21/src/components/Spinner.js rename to 03 - rmtDev/lecture 22/src/components/Spinner.js diff --git a/03 - rmtDev/video 22/index.css b/03 - rmtDev/lecture 23/index.css similarity index 100% rename from 03 - rmtDev/video 22/index.css rename to 03 - rmtDev/lecture 23/index.css diff --git a/03 - rmtDev/video 22/index.html b/03 - rmtDev/lecture 23/index.html similarity index 100% rename from 03 - rmtDev/video 22/index.html rename to 03 - rmtDev/lecture 23/index.html diff --git a/03 - rmtDev/video 22/index.js b/03 - rmtDev/lecture 23/index.js similarity index 100% rename from 03 - rmtDev/video 22/index.js rename to 03 - rmtDev/lecture 23/index.js diff --git a/03 - rmtDev/video 22/src/common.js b/03 - rmtDev/lecture 23/src/common.js similarity index 100% rename from 03 - rmtDev/video 22/src/common.js rename to 03 - rmtDev/lecture 23/src/common.js diff --git a/03 - rmtDev/video 22/src/components/Error.js b/03 - rmtDev/lecture 23/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 22/src/components/Error.js rename to 03 - rmtDev/lecture 23/src/components/Error.js diff --git a/03 - rmtDev/video 22/src/components/JobDetails.js b/03 - rmtDev/lecture 23/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 22/src/components/JobDetails.js rename to 03 - rmtDev/lecture 23/src/components/JobDetails.js diff --git a/03 - rmtDev/video 22/src/components/JobList.js b/03 - rmtDev/lecture 23/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 22/src/components/JobList.js rename to 03 - rmtDev/lecture 23/src/components/JobList.js diff --git a/03 - rmtDev/video 22/src/components/Search.js b/03 - rmtDev/lecture 23/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 22/src/components/Search.js rename to 03 - rmtDev/lecture 23/src/components/Search.js diff --git a/03 - rmtDev/video 22/src/components/Spinner.js b/03 - rmtDev/lecture 23/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 22/src/components/Spinner.js rename to 03 - rmtDev/lecture 23/src/components/Spinner.js diff --git a/03 - rmtDev/video 23/index.css b/03 - rmtDev/lecture 24/index.css similarity index 100% rename from 03 - rmtDev/video 23/index.css rename to 03 - rmtDev/lecture 24/index.css diff --git a/03 - rmtDev/video 23/index.html b/03 - rmtDev/lecture 24/index.html similarity index 100% rename from 03 - rmtDev/video 23/index.html rename to 03 - rmtDev/lecture 24/index.html diff --git a/03 - rmtDev/video 23/index.js b/03 - rmtDev/lecture 24/index.js similarity index 100% rename from 03 - rmtDev/video 23/index.js rename to 03 - rmtDev/lecture 24/index.js diff --git a/03 - rmtDev/video 23/src/common.js b/03 - rmtDev/lecture 24/src/common.js similarity index 100% rename from 03 - rmtDev/video 23/src/common.js rename to 03 - rmtDev/lecture 24/src/common.js diff --git a/03 - rmtDev/video 23/src/components/Error.js b/03 - rmtDev/lecture 24/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 23/src/components/Error.js rename to 03 - rmtDev/lecture 24/src/components/Error.js diff --git a/03 - rmtDev/video 23/src/components/JobDetails.js b/03 - rmtDev/lecture 24/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 23/src/components/JobDetails.js rename to 03 - rmtDev/lecture 24/src/components/JobDetails.js diff --git a/03 - rmtDev/video 23/src/components/JobList.js b/03 - rmtDev/lecture 24/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 23/src/components/JobList.js rename to 03 - rmtDev/lecture 24/src/components/JobList.js diff --git a/03 - rmtDev/video 23/src/components/Search.js b/03 - rmtDev/lecture 24/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 23/src/components/Search.js rename to 03 - rmtDev/lecture 24/src/components/Search.js diff --git a/03 - rmtDev/video 23/src/components/Spinner.js b/03 - rmtDev/lecture 24/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 23/src/components/Spinner.js rename to 03 - rmtDev/lecture 24/src/components/Spinner.js diff --git a/03 - rmtDev/video 24/index.css b/03 - rmtDev/lecture 25/index.css similarity index 100% rename from 03 - rmtDev/video 24/index.css rename to 03 - rmtDev/lecture 25/index.css diff --git a/03 - rmtDev/video 24/index.html b/03 - rmtDev/lecture 25/index.html similarity index 100% rename from 03 - rmtDev/video 24/index.html rename to 03 - rmtDev/lecture 25/index.html diff --git a/03 - rmtDev/video 24/index.js b/03 - rmtDev/lecture 25/index.js similarity index 100% rename from 03 - rmtDev/video 24/index.js rename to 03 - rmtDev/lecture 25/index.js diff --git a/03 - rmtDev/video 24/src/common.js b/03 - rmtDev/lecture 25/src/common.js similarity index 100% rename from 03 - rmtDev/video 24/src/common.js rename to 03 - rmtDev/lecture 25/src/common.js diff --git a/03 - rmtDev/video 24/src/components/Error.js b/03 - rmtDev/lecture 25/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 24/src/components/Error.js rename to 03 - rmtDev/lecture 25/src/components/Error.js diff --git a/03 - rmtDev/video 24/src/components/JobDetails.js b/03 - rmtDev/lecture 25/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 24/src/components/JobDetails.js rename to 03 - rmtDev/lecture 25/src/components/JobDetails.js diff --git a/03 - rmtDev/video 24/src/components/JobList.js b/03 - rmtDev/lecture 25/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 24/src/components/JobList.js rename to 03 - rmtDev/lecture 25/src/components/JobList.js diff --git a/03 - rmtDev/video 24/src/components/Search.js b/03 - rmtDev/lecture 25/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 24/src/components/Search.js rename to 03 - rmtDev/lecture 25/src/components/Search.js diff --git a/03 - rmtDev/video 24/src/components/Spinner.js b/03 - rmtDev/lecture 25/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 24/src/components/Spinner.js rename to 03 - rmtDev/lecture 25/src/components/Spinner.js diff --git a/03 - rmtDev/video 25/index.css b/03 - rmtDev/lecture 26/index.css similarity index 100% rename from 03 - rmtDev/video 25/index.css rename to 03 - rmtDev/lecture 26/index.css diff --git a/03 - rmtDev/video 25/index.html b/03 - rmtDev/lecture 26/index.html similarity index 100% rename from 03 - rmtDev/video 25/index.html rename to 03 - rmtDev/lecture 26/index.html diff --git a/03 - rmtDev/video 25/index.js b/03 - rmtDev/lecture 26/index.js similarity index 100% rename from 03 - rmtDev/video 25/index.js rename to 03 - rmtDev/lecture 26/index.js diff --git a/03 - rmtDev/video 25/src/common.js b/03 - rmtDev/lecture 26/src/common.js similarity index 100% rename from 03 - rmtDev/video 25/src/common.js rename to 03 - rmtDev/lecture 26/src/common.js diff --git a/03 - rmtDev/video 25/src/components/Error.js b/03 - rmtDev/lecture 26/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 25/src/components/Error.js rename to 03 - rmtDev/lecture 26/src/components/Error.js diff --git a/03 - rmtDev/video 25/src/components/JobDetails.js b/03 - rmtDev/lecture 26/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 25/src/components/JobDetails.js rename to 03 - rmtDev/lecture 26/src/components/JobDetails.js diff --git a/03 - rmtDev/video 25/src/components/JobList.js b/03 - rmtDev/lecture 26/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 25/src/components/JobList.js rename to 03 - rmtDev/lecture 26/src/components/JobList.js diff --git a/03 - rmtDev/video 25/src/components/Search.js b/03 - rmtDev/lecture 26/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 25/src/components/Search.js rename to 03 - rmtDev/lecture 26/src/components/Search.js diff --git a/03 - rmtDev/video 25/src/components/Spinner.js b/03 - rmtDev/lecture 26/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 25/src/components/Spinner.js rename to 03 - rmtDev/lecture 26/src/components/Spinner.js diff --git a/03 - rmtDev/video 26/index.css b/03 - rmtDev/lecture 27/index.css similarity index 100% rename from 03 - rmtDev/video 26/index.css rename to 03 - rmtDev/lecture 27/index.css diff --git a/03 - rmtDev/video 26/index.html b/03 - rmtDev/lecture 27/index.html similarity index 100% rename from 03 - rmtDev/video 26/index.html rename to 03 - rmtDev/lecture 27/index.html diff --git a/03 - rmtDev/video 26/index.js b/03 - rmtDev/lecture 27/index.js similarity index 100% rename from 03 - rmtDev/video 26/index.js rename to 03 - rmtDev/lecture 27/index.js diff --git a/03 - rmtDev/video 26/src/common.js b/03 - rmtDev/lecture 27/src/common.js similarity index 100% rename from 03 - rmtDev/video 26/src/common.js rename to 03 - rmtDev/lecture 27/src/common.js diff --git a/03 - rmtDev/video 26/src/components/Error.js b/03 - rmtDev/lecture 27/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 26/src/components/Error.js rename to 03 - rmtDev/lecture 27/src/components/Error.js diff --git a/03 - rmtDev/video 26/src/components/JobDetails.js b/03 - rmtDev/lecture 27/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 26/src/components/JobDetails.js rename to 03 - rmtDev/lecture 27/src/components/JobDetails.js diff --git a/03 - rmtDev/video 26/src/components/JobList.js b/03 - rmtDev/lecture 27/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 26/src/components/JobList.js rename to 03 - rmtDev/lecture 27/src/components/JobList.js diff --git a/03 - rmtDev/video 26/src/components/Search.js b/03 - rmtDev/lecture 27/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 26/src/components/Search.js rename to 03 - rmtDev/lecture 27/src/components/Search.js diff --git a/03 - rmtDev/video 26/src/components/Sorting.js b/03 - rmtDev/lecture 27/src/components/Sorting.js similarity index 100% rename from 03 - rmtDev/video 26/src/components/Sorting.js rename to 03 - rmtDev/lecture 27/src/components/Sorting.js diff --git a/03 - rmtDev/video 26/src/components/Spinner.js b/03 - rmtDev/lecture 27/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 26/src/components/Spinner.js rename to 03 - rmtDev/lecture 27/src/components/Spinner.js diff --git a/03 - rmtDev/video 27/index.css b/03 - rmtDev/lecture 28/index.css similarity index 100% rename from 03 - rmtDev/video 27/index.css rename to 03 - rmtDev/lecture 28/index.css diff --git a/03 - rmtDev/video 27/index.html b/03 - rmtDev/lecture 28/index.html similarity index 100% rename from 03 - rmtDev/video 27/index.html rename to 03 - rmtDev/lecture 28/index.html diff --git a/03 - rmtDev/video 27/index.js b/03 - rmtDev/lecture 28/index.js similarity index 100% rename from 03 - rmtDev/video 27/index.js rename to 03 - rmtDev/lecture 28/index.js diff --git a/03 - rmtDev/video 27/src/common.js b/03 - rmtDev/lecture 28/src/common.js similarity index 100% rename from 03 - rmtDev/video 27/src/common.js rename to 03 - rmtDev/lecture 28/src/common.js diff --git a/03 - rmtDev/video 27/src/components/Error.js b/03 - rmtDev/lecture 28/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 27/src/components/Error.js rename to 03 - rmtDev/lecture 28/src/components/Error.js diff --git a/03 - rmtDev/video 27/src/components/JobDetails.js b/03 - rmtDev/lecture 28/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 27/src/components/JobDetails.js rename to 03 - rmtDev/lecture 28/src/components/JobDetails.js diff --git a/03 - rmtDev/video 27/src/components/JobList.js b/03 - rmtDev/lecture 28/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 27/src/components/JobList.js rename to 03 - rmtDev/lecture 28/src/components/JobList.js diff --git a/03 - rmtDev/video 27/src/components/Search.js b/03 - rmtDev/lecture 28/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 27/src/components/Search.js rename to 03 - rmtDev/lecture 28/src/components/Search.js diff --git a/03 - rmtDev/video 27/src/components/Sorting.js b/03 - rmtDev/lecture 28/src/components/Sorting.js similarity index 100% rename from 03 - rmtDev/video 27/src/components/Sorting.js rename to 03 - rmtDev/lecture 28/src/components/Sorting.js diff --git a/03 - rmtDev/video 27/src/components/Spinner.js b/03 - rmtDev/lecture 28/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 27/src/components/Spinner.js rename to 03 - rmtDev/lecture 28/src/components/Spinner.js diff --git a/03 - rmtDev/video 28/index.css b/03 - rmtDev/lecture 29/index.css similarity index 100% rename from 03 - rmtDev/video 28/index.css rename to 03 - rmtDev/lecture 29/index.css diff --git a/03 - rmtDev/video 28/index.html b/03 - rmtDev/lecture 29/index.html similarity index 100% rename from 03 - rmtDev/video 28/index.html rename to 03 - rmtDev/lecture 29/index.html diff --git a/03 - rmtDev/video 28/index.js b/03 - rmtDev/lecture 29/index.js similarity index 100% rename from 03 - rmtDev/video 28/index.js rename to 03 - rmtDev/lecture 29/index.js diff --git a/03 - rmtDev/video 28/src/common.js b/03 - rmtDev/lecture 29/src/common.js similarity index 100% rename from 03 - rmtDev/video 28/src/common.js rename to 03 - rmtDev/lecture 29/src/common.js diff --git a/03 - rmtDev/video 28/src/components/Error.js b/03 - rmtDev/lecture 29/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 28/src/components/Error.js rename to 03 - rmtDev/lecture 29/src/components/Error.js diff --git a/03 - rmtDev/video 28/src/components/JobDetails.js b/03 - rmtDev/lecture 29/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 28/src/components/JobDetails.js rename to 03 - rmtDev/lecture 29/src/components/JobDetails.js diff --git a/03 - rmtDev/video 28/src/components/JobList.js b/03 - rmtDev/lecture 29/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 28/src/components/JobList.js rename to 03 - rmtDev/lecture 29/src/components/JobList.js diff --git a/03 - rmtDev/video 28/src/components/Search.js b/03 - rmtDev/lecture 29/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 28/src/components/Search.js rename to 03 - rmtDev/lecture 29/src/components/Search.js diff --git a/03 - rmtDev/video 28/src/components/Sorting.js b/03 - rmtDev/lecture 29/src/components/Sorting.js similarity index 100% rename from 03 - rmtDev/video 28/src/components/Sorting.js rename to 03 - rmtDev/lecture 29/src/components/Sorting.js diff --git a/03 - rmtDev/video 28/src/components/Spinner.js b/03 - rmtDev/lecture 29/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 28/src/components/Spinner.js rename to 03 - rmtDev/lecture 29/src/components/Spinner.js diff --git a/03 - rmtDev/video 29/index.css b/03 - rmtDev/lecture 30/index.css similarity index 100% rename from 03 - rmtDev/video 29/index.css rename to 03 - rmtDev/lecture 30/index.css diff --git a/03 - rmtDev/video 29/index.html b/03 - rmtDev/lecture 30/index.html similarity index 100% rename from 03 - rmtDev/video 29/index.html rename to 03 - rmtDev/lecture 30/index.html diff --git a/03 - rmtDev/video 29/index.js b/03 - rmtDev/lecture 30/index.js similarity index 100% rename from 03 - rmtDev/video 29/index.js rename to 03 - rmtDev/lecture 30/index.js diff --git a/03 - rmtDev/video 29/src/common.js b/03 - rmtDev/lecture 30/src/common.js similarity index 100% rename from 03 - rmtDev/video 29/src/common.js rename to 03 - rmtDev/lecture 30/src/common.js diff --git a/03 - rmtDev/video 29/src/components/Error.js b/03 - rmtDev/lecture 30/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 29/src/components/Error.js rename to 03 - rmtDev/lecture 30/src/components/Error.js diff --git a/03 - rmtDev/video 29/src/components/JobDetails.js b/03 - rmtDev/lecture 30/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 29/src/components/JobDetails.js rename to 03 - rmtDev/lecture 30/src/components/JobDetails.js diff --git a/03 - rmtDev/video 29/src/components/JobList.js b/03 - rmtDev/lecture 30/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 29/src/components/JobList.js rename to 03 - rmtDev/lecture 30/src/components/JobList.js diff --git a/03 - rmtDev/video 29/src/components/Pagination.js b/03 - rmtDev/lecture 30/src/components/Pagination.js similarity index 100% rename from 03 - rmtDev/video 29/src/components/Pagination.js rename to 03 - rmtDev/lecture 30/src/components/Pagination.js diff --git a/03 - rmtDev/video 29/src/components/Search.js b/03 - rmtDev/lecture 30/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 29/src/components/Search.js rename to 03 - rmtDev/lecture 30/src/components/Search.js diff --git a/03 - rmtDev/video 29/src/components/Sorting.js b/03 - rmtDev/lecture 30/src/components/Sorting.js similarity index 100% rename from 03 - rmtDev/video 29/src/components/Sorting.js rename to 03 - rmtDev/lecture 30/src/components/Sorting.js diff --git a/03 - rmtDev/video 29/src/components/Spinner.js b/03 - rmtDev/lecture 30/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 29/src/components/Spinner.js rename to 03 - rmtDev/lecture 30/src/components/Spinner.js diff --git a/03 - rmtDev/video 30/index.css b/03 - rmtDev/lecture 31/index.css similarity index 100% rename from 03 - rmtDev/video 30/index.css rename to 03 - rmtDev/lecture 31/index.css diff --git a/03 - rmtDev/video 30/index.html b/03 - rmtDev/lecture 31/index.html similarity index 100% rename from 03 - rmtDev/video 30/index.html rename to 03 - rmtDev/lecture 31/index.html diff --git a/03 - rmtDev/video 30/index.js b/03 - rmtDev/lecture 31/index.js similarity index 100% rename from 03 - rmtDev/video 30/index.js rename to 03 - rmtDev/lecture 31/index.js diff --git a/03 - rmtDev/video 30/src/common.js b/03 - rmtDev/lecture 31/src/common.js similarity index 100% rename from 03 - rmtDev/video 30/src/common.js rename to 03 - rmtDev/lecture 31/src/common.js diff --git a/03 - rmtDev/video 30/src/components/Error.js b/03 - rmtDev/lecture 31/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 30/src/components/Error.js rename to 03 - rmtDev/lecture 31/src/components/Error.js diff --git a/03 - rmtDev/video 30/src/components/JobDetails.js b/03 - rmtDev/lecture 31/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 30/src/components/JobDetails.js rename to 03 - rmtDev/lecture 31/src/components/JobDetails.js diff --git a/03 - rmtDev/video 30/src/components/JobList.js b/03 - rmtDev/lecture 31/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 30/src/components/JobList.js rename to 03 - rmtDev/lecture 31/src/components/JobList.js diff --git a/03 - rmtDev/video 30/src/components/Pagination.js b/03 - rmtDev/lecture 31/src/components/Pagination.js similarity index 100% rename from 03 - rmtDev/video 30/src/components/Pagination.js rename to 03 - rmtDev/lecture 31/src/components/Pagination.js diff --git a/03 - rmtDev/video 30/src/components/Router.js b/03 - rmtDev/lecture 31/src/components/Router.js similarity index 100% rename from 03 - rmtDev/video 30/src/components/Router.js rename to 03 - rmtDev/lecture 31/src/components/Router.js diff --git a/03 - rmtDev/video 30/src/components/Search.js b/03 - rmtDev/lecture 31/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 30/src/components/Search.js rename to 03 - rmtDev/lecture 31/src/components/Search.js diff --git a/03 - rmtDev/video 30/src/components/Sorting.js b/03 - rmtDev/lecture 31/src/components/Sorting.js similarity index 100% rename from 03 - rmtDev/video 30/src/components/Sorting.js rename to 03 - rmtDev/lecture 31/src/components/Sorting.js diff --git a/03 - rmtDev/video 30/src/components/Spinner.js b/03 - rmtDev/lecture 31/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 30/src/components/Spinner.js rename to 03 - rmtDev/lecture 31/src/components/Spinner.js diff --git a/03 - rmtDev/video 31/index.css b/03 - rmtDev/lecture 32/index.css similarity index 100% rename from 03 - rmtDev/video 31/index.css rename to 03 - rmtDev/lecture 32/index.css diff --git a/03 - rmtDev/video 31/index.html b/03 - rmtDev/lecture 32/index.html similarity index 100% rename from 03 - rmtDev/video 31/index.html rename to 03 - rmtDev/lecture 32/index.html diff --git a/03 - rmtDev/video 31/index.js b/03 - rmtDev/lecture 32/index.js similarity index 100% rename from 03 - rmtDev/video 31/index.js rename to 03 - rmtDev/lecture 32/index.js diff --git a/03 - rmtDev/video 31/src/common.js b/03 - rmtDev/lecture 32/src/common.js similarity index 100% rename from 03 - rmtDev/video 31/src/common.js rename to 03 - rmtDev/lecture 32/src/common.js diff --git a/03 - rmtDev/video 31/src/components/Error.js b/03 - rmtDev/lecture 32/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 31/src/components/Error.js rename to 03 - rmtDev/lecture 32/src/components/Error.js diff --git a/03 - rmtDev/video 31/src/components/JobDetails.js b/03 - rmtDev/lecture 32/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 31/src/components/JobDetails.js rename to 03 - rmtDev/lecture 32/src/components/JobDetails.js diff --git a/03 - rmtDev/video 31/src/components/JobList.js b/03 - rmtDev/lecture 32/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 31/src/components/JobList.js rename to 03 - rmtDev/lecture 32/src/components/JobList.js diff --git a/03 - rmtDev/video 31/src/components/Pagination.js b/03 - rmtDev/lecture 32/src/components/Pagination.js similarity index 100% rename from 03 - rmtDev/video 31/src/components/Pagination.js rename to 03 - rmtDev/lecture 32/src/components/Pagination.js diff --git a/03 - rmtDev/video 31/src/components/Router.js b/03 - rmtDev/lecture 32/src/components/Router.js similarity index 100% rename from 03 - rmtDev/video 31/src/components/Router.js rename to 03 - rmtDev/lecture 32/src/components/Router.js diff --git a/03 - rmtDev/video 31/src/components/Search.js b/03 - rmtDev/lecture 32/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 31/src/components/Search.js rename to 03 - rmtDev/lecture 32/src/components/Search.js diff --git a/03 - rmtDev/video 31/src/components/Sorting.js b/03 - rmtDev/lecture 32/src/components/Sorting.js similarity index 100% rename from 03 - rmtDev/video 31/src/components/Sorting.js rename to 03 - rmtDev/lecture 32/src/components/Sorting.js diff --git a/03 - rmtDev/video 31/src/components/Spinner.js b/03 - rmtDev/lecture 32/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 31/src/components/Spinner.js rename to 03 - rmtDev/lecture 32/src/components/Spinner.js diff --git a/03 - rmtDev/video 32/index.css b/03 - rmtDev/lecture 33/index.css similarity index 100% rename from 03 - rmtDev/video 32/index.css rename to 03 - rmtDev/lecture 33/index.css diff --git a/03 - rmtDev/video 32/index.html b/03 - rmtDev/lecture 33/index.html similarity index 100% rename from 03 - rmtDev/video 32/index.html rename to 03 - rmtDev/lecture 33/index.html diff --git a/03 - rmtDev/video 32/index.js b/03 - rmtDev/lecture 33/index.js similarity index 100% rename from 03 - rmtDev/video 32/index.js rename to 03 - rmtDev/lecture 33/index.js diff --git a/03 - rmtDev/video 32/src/common.js b/03 - rmtDev/lecture 33/src/common.js similarity index 100% rename from 03 - rmtDev/video 32/src/common.js rename to 03 - rmtDev/lecture 33/src/common.js diff --git a/03 - rmtDev/video 32/src/components/Bookmarks.js b/03 - rmtDev/lecture 33/src/components/Bookmarks.js similarity index 100% rename from 03 - rmtDev/video 32/src/components/Bookmarks.js rename to 03 - rmtDev/lecture 33/src/components/Bookmarks.js diff --git a/03 - rmtDev/video 32/src/components/Error.js b/03 - rmtDev/lecture 33/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 32/src/components/Error.js rename to 03 - rmtDev/lecture 33/src/components/Error.js diff --git a/03 - rmtDev/video 32/src/components/JobDetails.js b/03 - rmtDev/lecture 33/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 32/src/components/JobDetails.js rename to 03 - rmtDev/lecture 33/src/components/JobDetails.js diff --git a/03 - rmtDev/video 32/src/components/JobList.js b/03 - rmtDev/lecture 33/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 32/src/components/JobList.js rename to 03 - rmtDev/lecture 33/src/components/JobList.js diff --git a/03 - rmtDev/video 32/src/components/Pagination.js b/03 - rmtDev/lecture 33/src/components/Pagination.js similarity index 100% rename from 03 - rmtDev/video 32/src/components/Pagination.js rename to 03 - rmtDev/lecture 33/src/components/Pagination.js diff --git a/03 - rmtDev/video 32/src/components/Router.js b/03 - rmtDev/lecture 33/src/components/Router.js similarity index 100% rename from 03 - rmtDev/video 32/src/components/Router.js rename to 03 - rmtDev/lecture 33/src/components/Router.js diff --git a/03 - rmtDev/video 32/src/components/Search.js b/03 - rmtDev/lecture 33/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 32/src/components/Search.js rename to 03 - rmtDev/lecture 33/src/components/Search.js diff --git a/03 - rmtDev/video 32/src/components/Sorting.js b/03 - rmtDev/lecture 33/src/components/Sorting.js similarity index 100% rename from 03 - rmtDev/video 32/src/components/Sorting.js rename to 03 - rmtDev/lecture 33/src/components/Sorting.js diff --git a/03 - rmtDev/video 32/src/components/Spinner.js b/03 - rmtDev/lecture 33/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 32/src/components/Spinner.js rename to 03 - rmtDev/lecture 33/src/components/Spinner.js diff --git a/03 - rmtDev/video 33/index.css b/03 - rmtDev/lecture 34/index.css similarity index 100% rename from 03 - rmtDev/video 33/index.css rename to 03 - rmtDev/lecture 34/index.css diff --git a/03 - rmtDev/video 33/index.html b/03 - rmtDev/lecture 34/index.html similarity index 100% rename from 03 - rmtDev/video 33/index.html rename to 03 - rmtDev/lecture 34/index.html diff --git a/03 - rmtDev/video 33/index.js b/03 - rmtDev/lecture 34/index.js similarity index 100% rename from 03 - rmtDev/video 33/index.js rename to 03 - rmtDev/lecture 34/index.js diff --git a/03 - rmtDev/video 33/src/common.js b/03 - rmtDev/lecture 34/src/common.js similarity index 100% rename from 03 - rmtDev/video 33/src/common.js rename to 03 - rmtDev/lecture 34/src/common.js diff --git a/03 - rmtDev/video 33/src/components/Bookmarks.js b/03 - rmtDev/lecture 34/src/components/Bookmarks.js similarity index 100% rename from 03 - rmtDev/video 33/src/components/Bookmarks.js rename to 03 - rmtDev/lecture 34/src/components/Bookmarks.js diff --git a/03 - rmtDev/video 33/src/components/Error.js b/03 - rmtDev/lecture 34/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 33/src/components/Error.js rename to 03 - rmtDev/lecture 34/src/components/Error.js diff --git a/03 - rmtDev/video 33/src/components/JobDetails.js b/03 - rmtDev/lecture 34/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 33/src/components/JobDetails.js rename to 03 - rmtDev/lecture 34/src/components/JobDetails.js diff --git a/03 - rmtDev/video 33/src/components/JobList.js b/03 - rmtDev/lecture 34/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 33/src/components/JobList.js rename to 03 - rmtDev/lecture 34/src/components/JobList.js diff --git a/03 - rmtDev/video 33/src/components/Pagination.js b/03 - rmtDev/lecture 34/src/components/Pagination.js similarity index 100% rename from 03 - rmtDev/video 33/src/components/Pagination.js rename to 03 - rmtDev/lecture 34/src/components/Pagination.js diff --git a/03 - rmtDev/video 33/src/components/Router.js b/03 - rmtDev/lecture 34/src/components/Router.js similarity index 100% rename from 03 - rmtDev/video 33/src/components/Router.js rename to 03 - rmtDev/lecture 34/src/components/Router.js diff --git a/03 - rmtDev/video 33/src/components/Search.js b/03 - rmtDev/lecture 34/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 33/src/components/Search.js rename to 03 - rmtDev/lecture 34/src/components/Search.js diff --git a/03 - rmtDev/video 33/src/components/Sorting.js b/03 - rmtDev/lecture 34/src/components/Sorting.js similarity index 100% rename from 03 - rmtDev/video 33/src/components/Sorting.js rename to 03 - rmtDev/lecture 34/src/components/Sorting.js diff --git a/03 - rmtDev/video 33/src/components/Spinner.js b/03 - rmtDev/lecture 34/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 33/src/components/Spinner.js rename to 03 - rmtDev/lecture 34/src/components/Spinner.js diff --git a/03 - rmtDev/video 33/src/components/Storage.js b/03 - rmtDev/lecture 34/src/components/Storage.js similarity index 100% rename from 03 - rmtDev/video 33/src/components/Storage.js rename to 03 - rmtDev/lecture 34/src/components/Storage.js diff --git a/03 - rmtDev/video 34/index.css b/03 - rmtDev/lecture 35/index.css similarity index 100% rename from 03 - rmtDev/video 34/index.css rename to 03 - rmtDev/lecture 35/index.css diff --git a/03 - rmtDev/video 34/index.html b/03 - rmtDev/lecture 35/index.html similarity index 100% rename from 03 - rmtDev/video 34/index.html rename to 03 - rmtDev/lecture 35/index.html diff --git a/03 - rmtDev/video 34/index.js b/03 - rmtDev/lecture 35/index.js similarity index 100% rename from 03 - rmtDev/video 34/index.js rename to 03 - rmtDev/lecture 35/index.js diff --git a/03 - rmtDev/video 34/src/common.js b/03 - rmtDev/lecture 35/src/common.js similarity index 100% rename from 03 - rmtDev/video 34/src/common.js rename to 03 - rmtDev/lecture 35/src/common.js diff --git a/03 - rmtDev/video 34/src/components/Bookmarks.js b/03 - rmtDev/lecture 35/src/components/Bookmarks.js similarity index 100% rename from 03 - rmtDev/video 34/src/components/Bookmarks.js rename to 03 - rmtDev/lecture 35/src/components/Bookmarks.js diff --git a/03 - rmtDev/video 34/src/components/Error.js b/03 - rmtDev/lecture 35/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 34/src/components/Error.js rename to 03 - rmtDev/lecture 35/src/components/Error.js diff --git a/03 - rmtDev/video 34/src/components/JobDetails.js b/03 - rmtDev/lecture 35/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 34/src/components/JobDetails.js rename to 03 - rmtDev/lecture 35/src/components/JobDetails.js diff --git a/03 - rmtDev/video 34/src/components/JobList.js b/03 - rmtDev/lecture 35/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 34/src/components/JobList.js rename to 03 - rmtDev/lecture 35/src/components/JobList.js diff --git a/03 - rmtDev/video 34/src/components/Pagination.js b/03 - rmtDev/lecture 35/src/components/Pagination.js similarity index 100% rename from 03 - rmtDev/video 34/src/components/Pagination.js rename to 03 - rmtDev/lecture 35/src/components/Pagination.js diff --git a/03 - rmtDev/video 34/src/components/Router.js b/03 - rmtDev/lecture 35/src/components/Router.js similarity index 100% rename from 03 - rmtDev/video 34/src/components/Router.js rename to 03 - rmtDev/lecture 35/src/components/Router.js diff --git a/03 - rmtDev/video 34/src/components/Search.js b/03 - rmtDev/lecture 35/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 34/src/components/Search.js rename to 03 - rmtDev/lecture 35/src/components/Search.js diff --git a/03 - rmtDev/video 34/src/components/Sorting.js b/03 - rmtDev/lecture 35/src/components/Sorting.js similarity index 100% rename from 03 - rmtDev/video 34/src/components/Sorting.js rename to 03 - rmtDev/lecture 35/src/components/Sorting.js diff --git a/03 - rmtDev/video 34/src/components/Spinner.js b/03 - rmtDev/lecture 35/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 34/src/components/Spinner.js rename to 03 - rmtDev/lecture 35/src/components/Spinner.js diff --git a/03 - rmtDev/video 34/src/components/Storage.js b/03 - rmtDev/lecture 35/src/components/Storage.js similarity index 100% rename from 03 - rmtDev/video 34/src/components/Storage.js rename to 03 - rmtDev/lecture 35/src/components/Storage.js diff --git a/03 - rmtDev/video 35/babel.config.js b/03 - rmtDev/lecture 36/babel.config.js similarity index 100% rename from 03 - rmtDev/video 35/babel.config.js rename to 03 - rmtDev/lecture 36/babel.config.js diff --git a/03 - rmtDev/video 35/dist/index.html b/03 - rmtDev/lecture 36/dist/index.html similarity index 100% rename from 03 - rmtDev/video 35/dist/index.html rename to 03 - rmtDev/lecture 36/dist/index.html diff --git a/03 - rmtDev/video 35/dist/main.css b/03 - rmtDev/lecture 36/dist/main.css similarity index 100% rename from 03 - rmtDev/video 35/dist/main.css rename to 03 - rmtDev/lecture 36/dist/main.css diff --git a/03 - rmtDev/video 35/dist/main.js b/03 - rmtDev/lecture 36/dist/main.js similarity index 100% rename from 03 - rmtDev/video 35/dist/main.js rename to 03 - rmtDev/lecture 36/dist/main.js diff --git a/03 - rmtDev/video 35/dist/main.js.LICENSE.txt b/03 - rmtDev/lecture 36/dist/main.js.LICENSE.txt similarity index 100% rename from 03 - rmtDev/video 35/dist/main.js.LICENSE.txt rename to 03 - rmtDev/lecture 36/dist/main.js.LICENSE.txt diff --git a/03 - rmtDev/video 35/package-lock.json b/03 - rmtDev/lecture 36/package-lock.json similarity index 100% rename from 03 - rmtDev/video 35/package-lock.json rename to 03 - rmtDev/lecture 36/package-lock.json diff --git a/03 - rmtDev/video 35/package.json b/03 - rmtDev/lecture 36/package.json similarity index 100% rename from 03 - rmtDev/video 35/package.json rename to 03 - rmtDev/lecture 36/package.json diff --git a/03 - rmtDev/video 35/postcss.config.js b/03 - rmtDev/lecture 36/postcss.config.js similarity index 100% rename from 03 - rmtDev/video 35/postcss.config.js rename to 03 - rmtDev/lecture 36/postcss.config.js diff --git a/03 - rmtDev/video 35/src/common.js b/03 - rmtDev/lecture 36/src/common.js similarity index 100% rename from 03 - rmtDev/video 35/src/common.js rename to 03 - rmtDev/lecture 36/src/common.js diff --git a/03 - rmtDev/video 35/src/components/Bookmarks.js b/03 - rmtDev/lecture 36/src/components/Bookmarks.js similarity index 100% rename from 03 - rmtDev/video 35/src/components/Bookmarks.js rename to 03 - rmtDev/lecture 36/src/components/Bookmarks.js diff --git a/03 - rmtDev/video 35/src/components/Error.js b/03 - rmtDev/lecture 36/src/components/Error.js similarity index 100% rename from 03 - rmtDev/video 35/src/components/Error.js rename to 03 - rmtDev/lecture 36/src/components/Error.js diff --git a/03 - rmtDev/video 35/src/components/JobDetails.js b/03 - rmtDev/lecture 36/src/components/JobDetails.js similarity index 100% rename from 03 - rmtDev/video 35/src/components/JobDetails.js rename to 03 - rmtDev/lecture 36/src/components/JobDetails.js diff --git a/03 - rmtDev/video 35/src/components/JobList.js b/03 - rmtDev/lecture 36/src/components/JobList.js similarity index 100% rename from 03 - rmtDev/video 35/src/components/JobList.js rename to 03 - rmtDev/lecture 36/src/components/JobList.js diff --git a/03 - rmtDev/video 35/src/components/Pagination.js b/03 - rmtDev/lecture 36/src/components/Pagination.js similarity index 100% rename from 03 - rmtDev/video 35/src/components/Pagination.js rename to 03 - rmtDev/lecture 36/src/components/Pagination.js diff --git a/03 - rmtDev/video 35/src/components/Router.js b/03 - rmtDev/lecture 36/src/components/Router.js similarity index 100% rename from 03 - rmtDev/video 35/src/components/Router.js rename to 03 - rmtDev/lecture 36/src/components/Router.js diff --git a/03 - rmtDev/video 35/src/components/Search.js b/03 - rmtDev/lecture 36/src/components/Search.js similarity index 100% rename from 03 - rmtDev/video 35/src/components/Search.js rename to 03 - rmtDev/lecture 36/src/components/Search.js diff --git a/03 - rmtDev/video 35/src/components/Sorting.js b/03 - rmtDev/lecture 36/src/components/Sorting.js similarity index 100% rename from 03 - rmtDev/video 35/src/components/Sorting.js rename to 03 - rmtDev/lecture 36/src/components/Sorting.js diff --git a/03 - rmtDev/video 35/src/components/Spinner.js b/03 - rmtDev/lecture 36/src/components/Spinner.js similarity index 100% rename from 03 - rmtDev/video 35/src/components/Spinner.js rename to 03 - rmtDev/lecture 36/src/components/Spinner.js diff --git a/03 - rmtDev/video 35/src/components/Storage.js b/03 - rmtDev/lecture 36/src/components/Storage.js similarity index 100% rename from 03 - rmtDev/video 35/src/components/Storage.js rename to 03 - rmtDev/lecture 36/src/components/Storage.js diff --git a/03 - rmtDev/video 35/src/index.css b/03 - rmtDev/lecture 36/src/index.css similarity index 100% rename from 03 - rmtDev/video 35/src/index.css rename to 03 - rmtDev/lecture 36/src/index.css diff --git a/03 - rmtDev/video 35/src/index.html b/03 - rmtDev/lecture 36/src/index.html similarity index 100% rename from 03 - rmtDev/video 35/src/index.html rename to 03 - rmtDev/lecture 36/src/index.html diff --git a/03 - rmtDev/video 35/src/index.js b/03 - rmtDev/lecture 36/src/index.js similarity index 100% rename from 03 - rmtDev/video 35/src/index.js rename to 03 - rmtDev/lecture 36/src/index.js diff --git a/03 - rmtDev/video 35/webpack.config.js b/03 - rmtDev/lecture 36/webpack.config.js similarity index 100% rename from 03 - rmtDev/video 35/webpack.config.js rename to 03 - rmtDev/lecture 36/webpack.config.js From 130132e2cf454d0c4f35b26a9a8069d0bc0cb9df Mon Sep 17 00:00:00 2001 From: ByteGrad Date: Fri, 20 Jan 2023 22:51:10 +0200 Subject: [PATCH 17/40] new folder names --- 04 - Supplemental/{video 36 => lecture 37}/babel.config.js | 0 04 - Supplemental/{video 36 => lecture 37}/dist/index.html | 0 04 - Supplemental/{video 36 => lecture 37}/dist/main.css | 0 04 - Supplemental/{video 36 => lecture 37}/dist/main.js | 0 .../{video 36 => lecture 37}/dist/main.js.LICENSE.txt | 0 04 - Supplemental/{video 36 => lecture 37}/package-lock.json | 0 04 - Supplemental/{video 36 => lecture 37}/package.json | 0 04 - Supplemental/{video 36 => lecture 37}/postcss.config.js | 0 04 - Supplemental/{video 36 => lecture 37}/src/common.js | 0 .../{video 36 => lecture 37}/src/components/Bookmarks.js | 0 .../{video 36 => lecture 37}/src/components/Error.js | 0 .../{video 36 => lecture 37}/src/components/JobDetails.js | 0 .../{video 36 => lecture 37}/src/components/JobList.js | 0 .../{video 36 => lecture 37}/src/components/Pagination.js | 0 .../{video 36 => lecture 37}/src/components/Router.js | 0 .../{video 36 => lecture 37}/src/components/Search.js | 0 .../{video 36 => lecture 37}/src/components/Sorting.js | 0 .../{video 36 => lecture 37}/src/components/Spinner.js | 0 .../{video 36 => lecture 37}/src/components/Storage.js | 0 04 - Supplemental/{video 36 => lecture 37}/src/index.css | 0 04 - Supplemental/{video 36 => lecture 37}/src/index.html | 0 04 - Supplemental/{video 36 => lecture 37}/src/index.js | 0 04 - Supplemental/{video 36 => lecture 37}/webpack.config.js | 0 04 - Supplemental/{video 37 => lecture 38}/index.html | 0 04 - Supplemental/{video 37 => lecture 38}/script.js | 0 04 - Supplemental/{video 37 => lecture 38}/style.css | 0 04 - Supplemental/{video 38 => lecture 39}/babel.config.js | 0 04 - Supplemental/{video 38 => lecture 39}/dist/index.html | 0 04 - Supplemental/{video 38 => lecture 39}/dist/main.css | 0 04 - Supplemental/{video 38 => lecture 39}/dist/main.js | 0 .../{video 38 => lecture 39}/dist/main.js.LICENSE.txt | 0 04 - Supplemental/{video 38 => lecture 39}/package-lock.json | 0 04 - Supplemental/{video 38 => lecture 39}/package.json | 0 04 - Supplemental/{video 38 => lecture 39}/postcss.config.js | 0 04 - Supplemental/{video 38 => lecture 39}/src/common.js | 0 .../{video 38 => lecture 39}/src/components/Bookmarks.js | 0 .../{video 38 => lecture 39}/src/components/Error.js | 0 .../{video 38 => lecture 39}/src/components/JobDetails.js | 0 .../{video 38 => lecture 39}/src/components/JobList.js | 0 .../{video 38 => lecture 39}/src/components/Pagination.js | 0 .../{video 38 => lecture 39}/src/components/Router.js | 0 .../{video 38 => lecture 39}/src/components/Search.js | 0 .../{video 38 => lecture 39}/src/components/Sorting.js | 0 .../{video 38 => lecture 39}/src/components/Spinner.js | 0 .../{video 38 => lecture 39}/src/components/Storage.js | 0 04 - Supplemental/{video 38 => lecture 39}/src/index.css | 0 04 - Supplemental/{video 38 => lecture 39}/src/index.html | 0 04 - Supplemental/{video 38 => lecture 39}/src/index.js | 0 04 - Supplemental/{video 38 => lecture 39}/webpack.config.js | 0 49 files changed, 0 insertions(+), 0 deletions(-) rename 04 - Supplemental/{video 36 => lecture 37}/babel.config.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/dist/index.html (100%) rename 04 - Supplemental/{video 36 => lecture 37}/dist/main.css (100%) rename 04 - Supplemental/{video 36 => lecture 37}/dist/main.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/dist/main.js.LICENSE.txt (100%) rename 04 - Supplemental/{video 36 => lecture 37}/package-lock.json (100%) rename 04 - Supplemental/{video 36 => lecture 37}/package.json (100%) rename 04 - Supplemental/{video 36 => lecture 37}/postcss.config.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/common.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/components/Bookmarks.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/components/Error.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/components/JobDetails.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/components/JobList.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/components/Pagination.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/components/Router.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/components/Search.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/components/Sorting.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/components/Spinner.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/components/Storage.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/index.css (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/index.html (100%) rename 04 - Supplemental/{video 36 => lecture 37}/src/index.js (100%) rename 04 - Supplemental/{video 36 => lecture 37}/webpack.config.js (100%) rename 04 - Supplemental/{video 37 => lecture 38}/index.html (100%) rename 04 - Supplemental/{video 37 => lecture 38}/script.js (100%) rename 04 - Supplemental/{video 37 => lecture 38}/style.css (100%) rename 04 - Supplemental/{video 38 => lecture 39}/babel.config.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/dist/index.html (100%) rename 04 - Supplemental/{video 38 => lecture 39}/dist/main.css (100%) rename 04 - Supplemental/{video 38 => lecture 39}/dist/main.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/dist/main.js.LICENSE.txt (100%) rename 04 - Supplemental/{video 38 => lecture 39}/package-lock.json (100%) rename 04 - Supplemental/{video 38 => lecture 39}/package.json (100%) rename 04 - Supplemental/{video 38 => lecture 39}/postcss.config.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/common.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/components/Bookmarks.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/components/Error.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/components/JobDetails.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/components/JobList.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/components/Pagination.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/components/Router.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/components/Search.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/components/Sorting.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/components/Spinner.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/components/Storage.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/index.css (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/index.html (100%) rename 04 - Supplemental/{video 38 => lecture 39}/src/index.js (100%) rename 04 - Supplemental/{video 38 => lecture 39}/webpack.config.js (100%) diff --git a/04 - Supplemental/video 36/babel.config.js b/04 - Supplemental/lecture 37/babel.config.js similarity index 100% rename from 04 - Supplemental/video 36/babel.config.js rename to 04 - Supplemental/lecture 37/babel.config.js diff --git a/04 - Supplemental/video 36/dist/index.html b/04 - Supplemental/lecture 37/dist/index.html similarity index 100% rename from 04 - Supplemental/video 36/dist/index.html rename to 04 - Supplemental/lecture 37/dist/index.html diff --git a/04 - Supplemental/video 36/dist/main.css b/04 - Supplemental/lecture 37/dist/main.css similarity index 100% rename from 04 - Supplemental/video 36/dist/main.css rename to 04 - Supplemental/lecture 37/dist/main.css diff --git a/04 - Supplemental/video 36/dist/main.js b/04 - Supplemental/lecture 37/dist/main.js similarity index 100% rename from 04 - Supplemental/video 36/dist/main.js rename to 04 - Supplemental/lecture 37/dist/main.js diff --git a/04 - Supplemental/video 36/dist/main.js.LICENSE.txt b/04 - Supplemental/lecture 37/dist/main.js.LICENSE.txt similarity index 100% rename from 04 - Supplemental/video 36/dist/main.js.LICENSE.txt rename to 04 - Supplemental/lecture 37/dist/main.js.LICENSE.txt diff --git a/04 - Supplemental/video 36/package-lock.json b/04 - Supplemental/lecture 37/package-lock.json similarity index 100% rename from 04 - Supplemental/video 36/package-lock.json rename to 04 - Supplemental/lecture 37/package-lock.json diff --git a/04 - Supplemental/video 36/package.json b/04 - Supplemental/lecture 37/package.json similarity index 100% rename from 04 - Supplemental/video 36/package.json rename to 04 - Supplemental/lecture 37/package.json diff --git a/04 - Supplemental/video 36/postcss.config.js b/04 - Supplemental/lecture 37/postcss.config.js similarity index 100% rename from 04 - Supplemental/video 36/postcss.config.js rename to 04 - Supplemental/lecture 37/postcss.config.js diff --git a/04 - Supplemental/video 36/src/common.js b/04 - Supplemental/lecture 37/src/common.js similarity index 100% rename from 04 - Supplemental/video 36/src/common.js rename to 04 - Supplemental/lecture 37/src/common.js diff --git a/04 - Supplemental/video 36/src/components/Bookmarks.js b/04 - Supplemental/lecture 37/src/components/Bookmarks.js similarity index 100% rename from 04 - Supplemental/video 36/src/components/Bookmarks.js rename to 04 - Supplemental/lecture 37/src/components/Bookmarks.js diff --git a/04 - Supplemental/video 36/src/components/Error.js b/04 - Supplemental/lecture 37/src/components/Error.js similarity index 100% rename from 04 - Supplemental/video 36/src/components/Error.js rename to 04 - Supplemental/lecture 37/src/components/Error.js diff --git a/04 - Supplemental/video 36/src/components/JobDetails.js b/04 - Supplemental/lecture 37/src/components/JobDetails.js similarity index 100% rename from 04 - Supplemental/video 36/src/components/JobDetails.js rename to 04 - Supplemental/lecture 37/src/components/JobDetails.js diff --git a/04 - Supplemental/video 36/src/components/JobList.js b/04 - Supplemental/lecture 37/src/components/JobList.js similarity index 100% rename from 04 - Supplemental/video 36/src/components/JobList.js rename to 04 - Supplemental/lecture 37/src/components/JobList.js diff --git a/04 - Supplemental/video 36/src/components/Pagination.js b/04 - Supplemental/lecture 37/src/components/Pagination.js similarity index 100% rename from 04 - Supplemental/video 36/src/components/Pagination.js rename to 04 - Supplemental/lecture 37/src/components/Pagination.js diff --git a/04 - Supplemental/video 36/src/components/Router.js b/04 - Supplemental/lecture 37/src/components/Router.js similarity index 100% rename from 04 - Supplemental/video 36/src/components/Router.js rename to 04 - Supplemental/lecture 37/src/components/Router.js diff --git a/04 - Supplemental/video 36/src/components/Search.js b/04 - Supplemental/lecture 37/src/components/Search.js similarity index 100% rename from 04 - Supplemental/video 36/src/components/Search.js rename to 04 - Supplemental/lecture 37/src/components/Search.js diff --git a/04 - Supplemental/video 36/src/components/Sorting.js b/04 - Supplemental/lecture 37/src/components/Sorting.js similarity index 100% rename from 04 - Supplemental/video 36/src/components/Sorting.js rename to 04 - Supplemental/lecture 37/src/components/Sorting.js diff --git a/04 - Supplemental/video 36/src/components/Spinner.js b/04 - Supplemental/lecture 37/src/components/Spinner.js similarity index 100% rename from 04 - Supplemental/video 36/src/components/Spinner.js rename to 04 - Supplemental/lecture 37/src/components/Spinner.js diff --git a/04 - Supplemental/video 36/src/components/Storage.js b/04 - Supplemental/lecture 37/src/components/Storage.js similarity index 100% rename from 04 - Supplemental/video 36/src/components/Storage.js rename to 04 - Supplemental/lecture 37/src/components/Storage.js diff --git a/04 - Supplemental/video 36/src/index.css b/04 - Supplemental/lecture 37/src/index.css similarity index 100% rename from 04 - Supplemental/video 36/src/index.css rename to 04 - Supplemental/lecture 37/src/index.css diff --git a/04 - Supplemental/video 36/src/index.html b/04 - Supplemental/lecture 37/src/index.html similarity index 100% rename from 04 - Supplemental/video 36/src/index.html rename to 04 - Supplemental/lecture 37/src/index.html diff --git a/04 - Supplemental/video 36/src/index.js b/04 - Supplemental/lecture 37/src/index.js similarity index 100% rename from 04 - Supplemental/video 36/src/index.js rename to 04 - Supplemental/lecture 37/src/index.js diff --git a/04 - Supplemental/video 36/webpack.config.js b/04 - Supplemental/lecture 37/webpack.config.js similarity index 100% rename from 04 - Supplemental/video 36/webpack.config.js rename to 04 - Supplemental/lecture 37/webpack.config.js diff --git a/04 - Supplemental/video 37/index.html b/04 - Supplemental/lecture 38/index.html similarity index 100% rename from 04 - Supplemental/video 37/index.html rename to 04 - Supplemental/lecture 38/index.html diff --git a/04 - Supplemental/video 37/script.js b/04 - Supplemental/lecture 38/script.js similarity index 100% rename from 04 - Supplemental/video 37/script.js rename to 04 - Supplemental/lecture 38/script.js diff --git a/04 - Supplemental/video 37/style.css b/04 - Supplemental/lecture 38/style.css similarity index 100% rename from 04 - Supplemental/video 37/style.css rename to 04 - Supplemental/lecture 38/style.css diff --git a/04 - Supplemental/video 38/babel.config.js b/04 - Supplemental/lecture 39/babel.config.js similarity index 100% rename from 04 - Supplemental/video 38/babel.config.js rename to 04 - Supplemental/lecture 39/babel.config.js diff --git a/04 - Supplemental/video 38/dist/index.html b/04 - Supplemental/lecture 39/dist/index.html similarity index 100% rename from 04 - Supplemental/video 38/dist/index.html rename to 04 - Supplemental/lecture 39/dist/index.html diff --git a/04 - Supplemental/video 38/dist/main.css b/04 - Supplemental/lecture 39/dist/main.css similarity index 100% rename from 04 - Supplemental/video 38/dist/main.css rename to 04 - Supplemental/lecture 39/dist/main.css diff --git a/04 - Supplemental/video 38/dist/main.js b/04 - Supplemental/lecture 39/dist/main.js similarity index 100% rename from 04 - Supplemental/video 38/dist/main.js rename to 04 - Supplemental/lecture 39/dist/main.js diff --git a/04 - Supplemental/video 38/dist/main.js.LICENSE.txt b/04 - Supplemental/lecture 39/dist/main.js.LICENSE.txt similarity index 100% rename from 04 - Supplemental/video 38/dist/main.js.LICENSE.txt rename to 04 - Supplemental/lecture 39/dist/main.js.LICENSE.txt diff --git a/04 - Supplemental/video 38/package-lock.json b/04 - Supplemental/lecture 39/package-lock.json similarity index 100% rename from 04 - Supplemental/video 38/package-lock.json rename to 04 - Supplemental/lecture 39/package-lock.json diff --git a/04 - Supplemental/video 38/package.json b/04 - Supplemental/lecture 39/package.json similarity index 100% rename from 04 - Supplemental/video 38/package.json rename to 04 - Supplemental/lecture 39/package.json diff --git a/04 - Supplemental/video 38/postcss.config.js b/04 - Supplemental/lecture 39/postcss.config.js similarity index 100% rename from 04 - Supplemental/video 38/postcss.config.js rename to 04 - Supplemental/lecture 39/postcss.config.js diff --git a/04 - Supplemental/video 38/src/common.js b/04 - Supplemental/lecture 39/src/common.js similarity index 100% rename from 04 - Supplemental/video 38/src/common.js rename to 04 - Supplemental/lecture 39/src/common.js diff --git a/04 - Supplemental/video 38/src/components/Bookmarks.js b/04 - Supplemental/lecture 39/src/components/Bookmarks.js similarity index 100% rename from 04 - Supplemental/video 38/src/components/Bookmarks.js rename to 04 - Supplemental/lecture 39/src/components/Bookmarks.js diff --git a/04 - Supplemental/video 38/src/components/Error.js b/04 - Supplemental/lecture 39/src/components/Error.js similarity index 100% rename from 04 - Supplemental/video 38/src/components/Error.js rename to 04 - Supplemental/lecture 39/src/components/Error.js diff --git a/04 - Supplemental/video 38/src/components/JobDetails.js b/04 - Supplemental/lecture 39/src/components/JobDetails.js similarity index 100% rename from 04 - Supplemental/video 38/src/components/JobDetails.js rename to 04 - Supplemental/lecture 39/src/components/JobDetails.js diff --git a/04 - Supplemental/video 38/src/components/JobList.js b/04 - Supplemental/lecture 39/src/components/JobList.js similarity index 100% rename from 04 - Supplemental/video 38/src/components/JobList.js rename to 04 - Supplemental/lecture 39/src/components/JobList.js diff --git a/04 - Supplemental/video 38/src/components/Pagination.js b/04 - Supplemental/lecture 39/src/components/Pagination.js similarity index 100% rename from 04 - Supplemental/video 38/src/components/Pagination.js rename to 04 - Supplemental/lecture 39/src/components/Pagination.js diff --git a/04 - Supplemental/video 38/src/components/Router.js b/04 - Supplemental/lecture 39/src/components/Router.js similarity index 100% rename from 04 - Supplemental/video 38/src/components/Router.js rename to 04 - Supplemental/lecture 39/src/components/Router.js diff --git a/04 - Supplemental/video 38/src/components/Search.js b/04 - Supplemental/lecture 39/src/components/Search.js similarity index 100% rename from 04 - Supplemental/video 38/src/components/Search.js rename to 04 - Supplemental/lecture 39/src/components/Search.js diff --git a/04 - Supplemental/video 38/src/components/Sorting.js b/04 - Supplemental/lecture 39/src/components/Sorting.js similarity index 100% rename from 04 - Supplemental/video 38/src/components/Sorting.js rename to 04 - Supplemental/lecture 39/src/components/Sorting.js diff --git a/04 - Supplemental/video 38/src/components/Spinner.js b/04 - Supplemental/lecture 39/src/components/Spinner.js similarity index 100% rename from 04 - Supplemental/video 38/src/components/Spinner.js rename to 04 - Supplemental/lecture 39/src/components/Spinner.js diff --git a/04 - Supplemental/video 38/src/components/Storage.js b/04 - Supplemental/lecture 39/src/components/Storage.js similarity index 100% rename from 04 - Supplemental/video 38/src/components/Storage.js rename to 04 - Supplemental/lecture 39/src/components/Storage.js diff --git a/04 - Supplemental/video 38/src/index.css b/04 - Supplemental/lecture 39/src/index.css similarity index 100% rename from 04 - Supplemental/video 38/src/index.css rename to 04 - Supplemental/lecture 39/src/index.css diff --git a/04 - Supplemental/video 38/src/index.html b/04 - Supplemental/lecture 39/src/index.html similarity index 100% rename from 04 - Supplemental/video 38/src/index.html rename to 04 - Supplemental/lecture 39/src/index.html diff --git a/04 - Supplemental/video 38/src/index.js b/04 - Supplemental/lecture 39/src/index.js similarity index 100% rename from 04 - Supplemental/video 38/src/index.js rename to 04 - Supplemental/lecture 39/src/index.js diff --git a/04 - Supplemental/video 38/webpack.config.js b/04 - Supplemental/lecture 39/webpack.config.js similarity index 100% rename from 04 - Supplemental/video 38/webpack.config.js rename to 04 - Supplemental/lecture 39/webpack.config.js From 7feb78314f8d4f05de6a98b9f3cfdd4be5e20e59 Mon Sep 17 00:00:00 2001 From: ByteGrad Date: Mon, 23 Jan 2023 18:23:20 +0200 Subject: [PATCH 18/40] changed course link --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index db91ecc..d78decd 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# Professional JavaScript Course -- [See course here](https://bytegrad.com/professional-javascript) +# Professional JavaScript Course -- [See course here](https://www.udemy.com/course/professional-javascript-course/) **You can find the final code for each video in the project folders here**. Use that if you get stuck. There is also a bugs file, a slides file and a readme file (you're reading it right now). From 71af0345716a9a3402ea2f56aad1cb53a17da908 Mon Sep 17 00:00:00 2001 From: ByteGrad Date: Mon, 23 Jan 2023 18:24:45 +0200 Subject: [PATCH 19/40] changed course link --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index d78decd..69eebd4 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# Professional JavaScript Course -- [See course here](https://www.udemy.com/course/professional-javascript-course/) +# Professional JavaScript Course -- [See course here](https://www.udemy.com/course/professional-javascript-course/?referralCode=0C1D5752923168BC87C2) **You can find the final code for each video in the project folders here**. Use that if you get stuck. There is also a bugs file, a slides file and a readme file (you're reading it right now). From 7eb1f66b8ba0468e41555dae2b37272565515c61 Mon Sep 17 00:00:00 2001 From: ByteGrad Date: Wed, 15 Feb 2023 15:50:56 +0200 Subject: [PATCH 20/40] Change structure --- 01 - Introduction/intro.txt | 7 + .../index.html | 0 .../script.js | 0 .../style.css | 0 03 - Fancy Counter/index.html | 40 + 03 - Fancy Counter/resources.txt | 10 + 03 - Fancy Counter/script.js | 83 + 03 - Fancy Counter/style.css | 129 + 04 - Supplemental/lecture 39/babel.config.js | 11 - 04 - Supplemental/lecture 39/dist/index.html | 121 - 04 - Supplemental/lecture 39/dist/main.css | 1 - 04 - Supplemental/lecture 39/dist/main.js | 2 - .../lecture 39/dist/main.js.LICENSE.txt | 1 - .../lecture 39/package-lock.json | 8346 ----------------- 04 - Supplemental/lecture 39/package.json | 28 - .../lecture 39/postcss.config.js | 7 - 04 - Supplemental/lecture 39/src/common.js | 53 - .../lecture 39/src/components/Bookmarks.js | 51 - .../lecture 39/src/components/Error.js | 15 - .../lecture 39/src/components/JobDetails.js | 63 - .../lecture 39/src/components/JobList.js | 105 - .../lecture 39/src/components/Pagination.js | 58 - .../lecture 39/src/components/Router.js | 52 - .../lecture 39/src/components/Search.js | 79 - .../lecture 39/src/components/Sorting.js | 51 - .../lecture 39/src/components/Spinner.js | 11 - .../lecture 39/src/components/Storage.js | 8 - 04 - Supplemental/lecture 39/src/index.css | 1130 --- 04 - Supplemental/lecture 39/src/index.html | 121 - 04 - Supplemental/lecture 39/src/index.js | 12 - .../lecture 39/webpack.config.js | 27 - 04 - Word Analytics/index.html | 54 + 04 - Word Analytics/resources.txt | 11 + 04 - Word Analytics/script.js | 43 + 04 - Word Analytics/style.css | 150 + .../Counter Component}/index.html | 0 .../Counter Component}/script.js | 0 .../Counter Component}/style.css | 0 .../Feedback List Component #1}/index.html | 0 .../Feedback List Component #1}/script.js | 0 .../Feedback List Component #1}/style.css | 0 .../Feedback List Component #2}/index.html | 0 .../Feedback List Component #2}/script.js | 0 .../Feedback List Component #2}/style.css | 0 .../Final Loose Ends}/index.html | 0 .../Final Loose Ends}/script.js | 0 .../Final Loose Ends}/style.css | 0 .../Form (Submit) Component}/index.html | 0 .../Form (Submit) Component}/script.js | 0 .../Form (Submit) Component}/style.css | 0 .../Hashtag List Component}/index.html | 0 .../Hashtag List Component}/script.js | 0 .../Hashtag List Component}/style.css | 0 .../More Refactoring}/index.html | 0 .../More Refactoring}/script.js | 0 .../More Refactoring}/style.css | 0 .../Project Setup}/index.html | 0 .../Project Setup}/script.js | 0 .../Project Setup}/style.css | 0 .../Refactoring}/index.html | 0 .../Refactoring}/script.js | 0 .../Refactoring}/style.css | 0 .../Submit Component}/index.html | 0 .../Submit Component}/script.js | 0 .../Submit Component}/style.css | 0 .../index.html | 0 .../script.js | 0 .../style.css | 0 .../resources.txt | 0 .../Active Job Item}/index.css | 0 .../Active Job Item}/index.html | 0 .../Active Job Item}/index.js | 0 .../Active Job Item}/src/common.js | 0 .../Active Job Item}/src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Pagination.js | 0 .../Active Job Item}/src/components/Router.js | 0 .../Active Job Item}/src/components/Search.js | 0 .../src/components/Sorting.js | 0 .../src/components/Spinner.js | 0 .../Async Await}/index.css | 0 .../Async Await}/index.html | 0 .../Async Await}/index.js | 0 .../Async Await}/src/common.js | 0 .../Async Await}/src/components/Error.js | 0 .../Async Await}/src/components/JobDetails.js | 0 .../Async Await}/src/components/JobList.js | 0 .../Async Await}/src/components/Search.js | 0 .../Async Await}/src/components/Spinner.js | 0 .../Bookmarks Component}/index.css | 0 .../Bookmarks Component}/index.html | 0 .../Bookmarks Component}/index.js | 0 .../Bookmarks Component}/src/common.js | 0 .../src/components/Bookmarks.js | 0 .../src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Pagination.js | 0 .../src/components/Router.js | 0 .../src/components/Search.js | 0 .../src/components/Sorting.js | 0 .../src/components/Spinner.js | 0 .../Build Process (webpack)}/babel.config.js | 0 .../Build Process (webpack)}/dist/index.html | 0 .../Build Process (webpack)}/dist/main.css | 0 .../Build Process (webpack)}/dist/main.js | 0 .../dist/main.js.LICENSE.txt | 0 .../package-lock.json | 0 .../Build Process (webpack)}/package.json | 0 .../postcss.config.js | 0 .../Build Process (webpack)}/src/common.js | 0 .../src/components/Bookmarks.js | 0 .../src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Pagination.js | 0 .../src/components/Router.js | 0 .../src/components/Search.js | 0 .../src/components/Sorting.js | 0 .../src/components/Spinner.js | 0 .../src/components/Storage.js | 0 .../Build Process (webpack)/src}/index.css | 0 .../Build Process (webpack)/src}/index.html | 0 .../Build Process (webpack)}/src/index.js | 0 .../webpack.config.js | 0 .../Error Component}/index.css | 0 .../Error Component}/index.html | 0 .../Error Component}/index.js | 0 .../Error Component}/src/common.js | 0 .../Error Component}/src/components/Error.js | 0 .../src/components/JobList.js | 0 .../Error Component}/src/components/Search.js | 0 .../src/components/Spinner.js | 0 .../Improving Fetch Calls}/index.css | 0 .../Improving Fetch Calls}/index.html | 0 .../Improving Fetch Calls}/index.js | 0 .../Improving Fetch Calls}/src/common.js | 0 .../src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Search.js | 0 .../src/components/Spinner.js | 0 .../Job List Component}/index.css | 0 .../Job List Component}/index.html | 0 .../Job List Component}/index.js | 0 .../Modularity with ES Modules}/index.css | 0 .../Modularity with ES Modules}/index.html | 0 .../Modularity with ES Modules}/index.js | 0 .../Modularity with ES Modules}/src/common.js | 0 .../src/components/Error.js | 0 .../src/components/JobList.js | 0 .../src/components/Search.js | 0 .../src/components/Spinner.js | 0 .../More Refactoring}/index.css | 0 .../More Refactoring}/index.html | 0 .../More Refactoring}/index.js | 0 .../More Refactoring}/src/common.js | 0 .../More Refactoring}/src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Search.js | 0 .../src/components/Spinner.js | 0 .../Pagination Component}/index.css | 0 .../Pagination Component}/index.html | 0 .../Pagination Component}/index.js | 0 .../Pagination Component}/src/common.js | 0 .../src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Pagination.js | 0 .../src/components/Search.js | 0 .../src/components/Sorting.js | 0 .../src/components/Spinner.js | 0 .../Project Setup}/index.css | 0 .../Project Setup}/index.html | 0 .../Project Setup}/index.js | 0 .../Refactor Fetch Calls}/index.css | 0 .../Refactor Fetch Calls}/index.html | 0 .../Refactor Fetch Calls}/index.js | 0 .../Refactor Fetch Calls}/src/common.js | 0 .../src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Search.js | 0 .../src/components/Spinner.js | 0 .../Refactoring}/index.css | 0 .../Refactoring}/index.html | 0 .../Refactoring}/index.js | 0 .../Refactoring}/src/common.js | 0 .../Refactoring}/src/components/Error.js | 0 .../Refactoring}/src/components/JobList.js | 0 .../Refactoring}/src/components/Search.js | 0 .../Refactoring}/src/components/Spinner.js | 0 .../Router Component}/index.css | 0 .../Router Component}/index.html | 0 .../Router Component}/index.js | 0 .../Router Component}/src/common.js | 0 .../Router Component}/src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Pagination.js | 0 .../src/components/Router.js | 0 .../src/components/Search.js | 0 .../src/components/Sorting.js | 0 .../src/components/Spinner.js | 0 .../Search Component}/index.css | 0 .../Search Component}/index.html | 0 .../Search Component}/index.js | 0 .../Sorting Component #2}/index.css | 0 .../Sorting Component #2}/index.html | 0 .../Sorting Component #2}/index.js | 0 .../Sorting Component #2}/src/common.js | 0 .../src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Search.js | 0 .../src/components/Sorting.js | 0 .../src/components/Spinner.js | 0 .../Sorting Component}/index.css | 0 .../Sorting Component}/index.html | 0 .../Sorting Component}/index.js | 0 .../Sorting Component}/src/common.js | 0 .../src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Search.js | 0 .../src/components/Sorting.js | 0 .../src/components/Spinner.js | 0 .../State}/index.css | 0 .../State}/index.html | 0 .../lecture 29 => 08 - rmtDev/State}/index.js | 0 .../State}/src/common.js | 0 .../State}/src/components/Error.js | 0 .../State}/src/components/JobDetails.js | 0 .../State}/src/components/JobList.js | 0 .../State}/src/components/Search.js | 0 .../State}/src/components/Sorting.js | 0 .../State}/src/components/Spinner.js | 0 .../Storage Component}/index.css | 0 .../Storage Component}/index.html | 0 .../Storage Component}/index.js | 0 .../Storage Component}/src/common.js | 0 .../src/components/Bookmarks.js | 0 .../src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Pagination.js | 0 .../src/components/Router.js | 0 .../src/components/Search.js | 0 .../src/components/Sorting.js | 0 .../src/components/Spinner.js | 0 .../src/components/Storage.js | 0 .../Taking Care of Details}/index.css | 0 .../Taking Care of Details}/index.html | 0 .../Taking Care of Details}/index.js | 0 .../Taking Care of Details}/src/common.js | 0 .../src/components/Bookmarks.js | 0 .../src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Pagination.js | 0 .../src/components/Router.js | 0 .../src/components/Search.js | 0 .../src/components/Sorting.js | 0 .../src/components/Spinner.js | 0 .../src/components/Storage.js | 0 {03 - rmtDev => 08 - rmtDev}/resources.txt | 0 .../Formatting in JS}/babel.config.js | 0 .../Formatting in JS}/dist/index.html | 0 .../Formatting in JS}/dist/main.css | 0 .../Formatting in JS}/dist/main.js | 0 .../dist/main.js.LICENSE.txt | 0 .../Formatting in JS}/package-lock.json | 0 .../Formatting in JS}/package.json | 0 .../Formatting in JS}/postcss.config.js | 0 .../Formatting in JS}/src/common.js | 0 .../src/components/Bookmarks.js | 0 .../Formatting in JS}/src/components/Error.js | 0 .../src/components/JobDetails.js | 0 .../src/components/JobList.js | 0 .../src/components/Pagination.js | 0 .../src/components/Router.js | 0 .../src/components/Search.js | 0 .../src/components/Sorting.js | 0 .../src/components/Spinner.js | 0 .../src/components/Storage.js | 0 .../Formatting in JS}/src/index.css | 0 .../Formatting in JS}/src/index.html | 0 .../Formatting in JS}/src/index.js | 0 .../Formatting in JS}/webpack.config.js | 0 .../index.html | 0 .../script.js | 0 .../style.css | 0 readme.md | 4 +- 295 files changed, 529 insertions(+), 10355 deletions(-) create mode 100644 01 - Introduction/intro.txt rename {01 - Modern JS Fundamentals => 02 - Modern JS Fundamentals}/index.html (100%) rename {01 - Modern JS Fundamentals => 02 - Modern JS Fundamentals}/script.js (100%) rename {01 - Modern JS Fundamentals => 02 - Modern JS Fundamentals}/style.css (100%) create mode 100644 03 - Fancy Counter/index.html create mode 100644 03 - Fancy Counter/resources.txt create mode 100644 03 - Fancy Counter/script.js create mode 100644 03 - Fancy Counter/style.css delete mode 100644 04 - Supplemental/lecture 39/babel.config.js delete mode 100644 04 - Supplemental/lecture 39/dist/index.html delete mode 100644 04 - Supplemental/lecture 39/dist/main.css delete mode 100644 04 - Supplemental/lecture 39/dist/main.js delete mode 100644 04 - Supplemental/lecture 39/dist/main.js.LICENSE.txt delete mode 100644 04 - Supplemental/lecture 39/package-lock.json delete mode 100644 04 - Supplemental/lecture 39/package.json delete mode 100644 04 - Supplemental/lecture 39/postcss.config.js delete mode 100644 04 - Supplemental/lecture 39/src/common.js delete mode 100644 04 - Supplemental/lecture 39/src/components/Bookmarks.js delete mode 100644 04 - Supplemental/lecture 39/src/components/Error.js delete mode 100644 04 - Supplemental/lecture 39/src/components/JobDetails.js delete mode 100644 04 - Supplemental/lecture 39/src/components/JobList.js delete mode 100644 04 - Supplemental/lecture 39/src/components/Pagination.js delete mode 100644 04 - Supplemental/lecture 39/src/components/Router.js delete mode 100644 04 - Supplemental/lecture 39/src/components/Search.js delete mode 100644 04 - Supplemental/lecture 39/src/components/Sorting.js delete mode 100644 04 - Supplemental/lecture 39/src/components/Spinner.js delete mode 100644 04 - Supplemental/lecture 39/src/components/Storage.js delete mode 100644 04 - Supplemental/lecture 39/src/index.css delete mode 100644 04 - Supplemental/lecture 39/src/index.html delete mode 100644 04 - Supplemental/lecture 39/src/index.js delete mode 100644 04 - Supplemental/lecture 39/webpack.config.js create mode 100644 04 - Word Analytics/index.html create mode 100644 04 - Word Analytics/resources.txt create mode 100644 04 - Word Analytics/script.js create mode 100644 04 - Word Analytics/style.css rename {02 - CorpComment/lecture 06 => 06 - CorpComment/Counter Component}/index.html (100%) rename {02 - CorpComment/lecture 07 => 06 - CorpComment/Counter Component}/script.js (100%) rename {02 - CorpComment/lecture 05 => 06 - CorpComment/Counter Component}/style.css (100%) rename {02 - CorpComment/lecture 10 => 06 - CorpComment/Feedback List Component #1}/index.html (100%) rename {02 - CorpComment/lecture 10 => 06 - CorpComment/Feedback List Component #1}/script.js (100%) rename {02 - CorpComment/lecture 06 => 06 - CorpComment/Feedback List Component #1}/style.css (100%) rename {02 - CorpComment/lecture 11 => 06 - CorpComment/Feedback List Component #2}/index.html (100%) rename {02 - CorpComment/lecture 13 => 06 - CorpComment/Feedback List Component #2}/script.js (100%) rename {02 - CorpComment/lecture 07 => 06 - CorpComment/Feedback List Component #2}/style.css (100%) rename {02 - CorpComment/lecture 12 => 06 - CorpComment/Final Loose Ends}/index.html (100%) rename {02 - CorpComment/lecture 15 => 06 - CorpComment/Final Loose Ends}/script.js (100%) rename {02 - CorpComment/lecture 08 => 06 - CorpComment/Final Loose Ends}/style.css (100%) rename {02 - CorpComment/lecture 13 => 06 - CorpComment/Form (Submit) Component}/index.html (100%) rename {02 - CorpComment/lecture 12 => 06 - CorpComment/Form (Submit) Component}/script.js (100%) rename {02 - CorpComment/lecture 12 => 06 - CorpComment/Form (Submit) Component}/style.css (100%) rename {02 - CorpComment/lecture 14 => 06 - CorpComment/Hashtag List Component}/index.html (100%) rename {02 - CorpComment/lecture 14 => 06 - CorpComment/Hashtag List Component}/script.js (100%) rename {02 - CorpComment/lecture 09 => 06 - CorpComment/Hashtag List Component}/style.css (100%) rename {02 - CorpComment/lecture 15 => 06 - CorpComment/More Refactoring}/index.html (100%) rename {02 - CorpComment/lecture 11 => 06 - CorpComment/More Refactoring}/script.js (100%) rename {02 - CorpComment/lecture 10 => 06 - CorpComment/More Refactoring}/style.css (100%) rename {02 - CorpComment/lecture 05 => 06 - CorpComment/Project Setup}/index.html (100%) rename {02 - CorpComment/lecture 05 => 06 - CorpComment/Project Setup}/script.js (100%) rename {02 - CorpComment/lecture 11 => 06 - CorpComment/Project Setup}/style.css (100%) rename {02 - CorpComment/lecture 08 => 06 - CorpComment/Refactoring}/index.html (100%) rename {02 - CorpComment/lecture 09 => 06 - CorpComment/Refactoring}/script.js (100%) rename {02 - CorpComment/lecture 13 => 06 - CorpComment/Refactoring}/style.css (100%) rename {02 - CorpComment/lecture 09 => 06 - CorpComment/Submit Component}/index.html (100%) rename {02 - CorpComment/lecture 08 => 06 - CorpComment/Submit Component}/script.js (100%) rename {02 - CorpComment/lecture 14 => 06 - CorpComment/Submit Component}/style.css (100%) rename {02 - CorpComment/lecture 07 => 06 - CorpComment/VS Code Extension (Live-Server)}/index.html (100%) rename {02 - CorpComment/lecture 06 => 06 - CorpComment/VS Code Extension (Live-Server)}/script.js (100%) rename {02 - CorpComment/lecture 15 => 06 - CorpComment/VS Code Extension (Live-Server)}/style.css (100%) rename {02 - CorpComment => 06 - CorpComment}/resources.txt (100%) rename {03 - rmtDev/lecture 17 => 08 - rmtDev/Active Job Item}/index.css (100%) rename {03 - rmtDev/lecture 20 => 08 - rmtDev/Active Job Item}/index.html (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Active Job Item}/index.js (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Active Job Item}/src/common.js (100%) rename {03 - rmtDev/lecture 22 => 08 - rmtDev/Active Job Item}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 23 => 08 - rmtDev/Active Job Item}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Active Job Item}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Active Job Item}/src/components/Pagination.js (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Active Job Item}/src/components/Router.js (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Active Job Item}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Active Job Item}/src/components/Sorting.js (100%) rename {03 - rmtDev/lecture 21 => 08 - rmtDev/Active Job Item}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 18 => 08 - rmtDev/Async Await}/index.css (100%) rename {03 - rmtDev/lecture 21 => 08 - rmtDev/Async Await}/index.html (100%) rename {03 - rmtDev/lecture 23 => 08 - rmtDev/Async Await}/index.js (100%) rename {03 - rmtDev/lecture 22 => 08 - rmtDev/Async Await}/src/common.js (100%) rename {03 - rmtDev/lecture 23 => 08 - rmtDev/Async Await}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 24 => 08 - rmtDev/Async Await}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 25 => 08 - rmtDev/Async Await}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 25 => 08 - rmtDev/Async Await}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 22 => 08 - rmtDev/Async Await}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 19 => 08 - rmtDev/Bookmarks Component}/index.css (100%) rename {03 - rmtDev/lecture 22 => 08 - rmtDev/Bookmarks Component}/index.html (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Bookmarks Component}/index.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Bookmarks Component}/src/common.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Bookmarks Component}/src/components/Bookmarks.js (100%) rename {03 - rmtDev/lecture 24 => 08 - rmtDev/Bookmarks Component}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 25 => 08 - rmtDev/Bookmarks Component}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Bookmarks Component}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Bookmarks Component}/src/components/Pagination.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Bookmarks Component}/src/components/Router.js (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Bookmarks Component}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Bookmarks Component}/src/components/Sorting.js (100%) rename {03 - rmtDev/lecture 23 => 08 - rmtDev/Bookmarks Component}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Build Process (webpack)}/babel.config.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Build Process (webpack)}/dist/index.html (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Build Process (webpack)}/dist/main.css (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Build Process (webpack)}/dist/main.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Build Process (webpack)}/dist/main.js.LICENSE.txt (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Build Process (webpack)}/package-lock.json (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Build Process (webpack)}/package.json (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Build Process (webpack)}/postcss.config.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/Build Process (webpack)}/src/common.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Build Process (webpack)}/src/components/Bookmarks.js (100%) rename {03 - rmtDev/lecture 25 => 08 - rmtDev/Build Process (webpack)}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Build Process (webpack)}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Build Process (webpack)}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Build Process (webpack)}/src/components/Pagination.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Build Process (webpack)}/src/components/Router.js (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Build Process (webpack)}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Build Process (webpack)}/src/components/Sorting.js (100%) rename {03 - rmtDev/lecture 24 => 08 - rmtDev/Build Process (webpack)}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/Build Process (webpack)}/src/components/Storage.js (100%) rename {03 - rmtDev/lecture 20 => 08 - rmtDev/Build Process (webpack)/src}/index.css (100%) rename {03 - rmtDev/lecture 23 => 08 - rmtDev/Build Process (webpack)/src}/index.html (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Build Process (webpack)}/src/index.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Build Process (webpack)}/webpack.config.js (100%) rename {03 - rmtDev/lecture 21 => 08 - rmtDev/Error Component}/index.css (100%) rename {03 - rmtDev/lecture 24 => 08 - rmtDev/Error Component}/index.html (100%) rename {03 - rmtDev/lecture 20 => 08 - rmtDev/Error Component}/index.js (100%) rename {03 - rmtDev/lecture 20 => 08 - rmtDev/Error Component}/src/common.js (100%) rename {03 - rmtDev/lecture 21 => 08 - rmtDev/Error Component}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 21 => 08 - rmtDev/Error Component}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 21 => 08 - rmtDev/Error Component}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 25 => 08 - rmtDev/Error Component}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 22 => 08 - rmtDev/Improving Fetch Calls}/index.css (100%) rename {03 - rmtDev/lecture 25 => 08 - rmtDev/Improving Fetch Calls}/index.html (100%) rename {03 - rmtDev/lecture 24 => 08 - rmtDev/Improving Fetch Calls}/index.js (100%) rename {03 - rmtDev/lecture 23 => 08 - rmtDev/Improving Fetch Calls}/src/common.js (100%) rename {03 - rmtDev/lecture 26 => 08 - rmtDev/Improving Fetch Calls}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 26 => 08 - rmtDev/Improving Fetch Calls}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 24 => 08 - rmtDev/Improving Fetch Calls}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 24 => 08 - rmtDev/Improving Fetch Calls}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 26 => 08 - rmtDev/Improving Fetch Calls}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 23 => 08 - rmtDev/Job List Component}/index.css (100%) rename {03 - rmtDev/lecture 17 => 08 - rmtDev/Job List Component}/index.html (100%) rename {03 - rmtDev/lecture 19 => 08 - rmtDev/Job List Component}/index.js (100%) rename {03 - rmtDev/lecture 24 => 08 - rmtDev/Modularity with ES Modules}/index.css (100%) rename {03 - rmtDev/lecture 26 => 08 - rmtDev/Modularity with ES Modules}/index.html (100%) rename {03 - rmtDev/lecture 21 => 08 - rmtDev/Modularity with ES Modules}/index.js (100%) rename {03 - rmtDev/lecture 21 => 08 - rmtDev/Modularity with ES Modules}/src/common.js (100%) rename {03 - rmtDev/lecture 20 => 08 - rmtDev/Modularity with ES Modules}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 20 => 08 - rmtDev/Modularity with ES Modules}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 20 => 08 - rmtDev/Modularity with ES Modules}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 20 => 08 - rmtDev/Modularity with ES Modules}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 25 => 08 - rmtDev/More Refactoring}/index.css (100%) rename {03 - rmtDev/lecture 27 => 08 - rmtDev/More Refactoring}/index.html (100%) rename {03 - rmtDev/lecture 25 => 08 - rmtDev/More Refactoring}/index.js (100%) rename {03 - rmtDev/lecture 24 => 08 - rmtDev/More Refactoring}/src/common.js (100%) rename {03 - rmtDev/lecture 27 => 08 - rmtDev/More Refactoring}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 27 => 08 - rmtDev/More Refactoring}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 23 => 08 - rmtDev/More Refactoring}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 23 => 08 - rmtDev/More Refactoring}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 27 => 08 - rmtDev/More Refactoring}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 26 => 08 - rmtDev/Pagination Component}/index.css (100%) rename {03 - rmtDev/lecture 28 => 08 - rmtDev/Pagination Component}/index.html (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Pagination Component}/index.js (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Pagination Component}/src/common.js (100%) rename {03 - rmtDev/lecture 28 => 08 - rmtDev/Pagination Component}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 28 => 08 - rmtDev/Pagination Component}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Pagination Component}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Pagination Component}/src/components/Pagination.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Pagination Component}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Pagination Component}/src/components/Sorting.js (100%) rename {03 - rmtDev/lecture 28 => 08 - rmtDev/Pagination Component}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 27 => 08 - rmtDev/Project Setup}/index.css (100%) rename {03 - rmtDev/lecture 18 => 08 - rmtDev/Project Setup}/index.html (100%) rename {03 - rmtDev/lecture 17 => 08 - rmtDev/Project Setup}/index.js (100%) rename {03 - rmtDev/lecture 28 => 08 - rmtDev/Refactor Fetch Calls}/index.css (100%) rename {03 - rmtDev/lecture 29 => 08 - rmtDev/Refactor Fetch Calls}/index.html (100%) rename {03 - rmtDev/lecture 26 => 08 - rmtDev/Refactor Fetch Calls}/index.js (100%) rename {03 - rmtDev/lecture 26 => 08 - rmtDev/Refactor Fetch Calls}/src/common.js (100%) rename {03 - rmtDev/lecture 29 => 08 - rmtDev/Refactor Fetch Calls}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 29 => 08 - rmtDev/Refactor Fetch Calls}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 26 => 08 - rmtDev/Refactor Fetch Calls}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 26 => 08 - rmtDev/Refactor Fetch Calls}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 29 => 08 - rmtDev/Refactor Fetch Calls}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 29 => 08 - rmtDev/Refactoring}/index.css (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Refactoring}/index.html (100%) rename {03 - rmtDev/lecture 22 => 08 - rmtDev/Refactoring}/index.js (100%) rename {03 - rmtDev/lecture 25 => 08 - rmtDev/Refactoring}/src/common.js (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Refactoring}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 22 => 08 - rmtDev/Refactoring}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 22 => 08 - rmtDev/Refactoring}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Refactoring}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Router Component}/index.css (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Router Component}/index.html (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Router Component}/index.js (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Router Component}/src/common.js (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Router Component}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 30 => 08 - rmtDev/Router Component}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Router Component}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/Router Component}/src/components/Pagination.js (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Router Component}/src/components/Router.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/Router Component}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/Router Component}/src/components/Sorting.js (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Router Component}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Search Component}/index.css (100%) rename {03 - rmtDev/lecture 19 => 08 - rmtDev/Search Component}/index.html (100%) rename {03 - rmtDev/lecture 18 => 08 - rmtDev/Search Component}/index.js (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Sorting Component #2}/index.css (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Sorting Component #2}/index.html (100%) rename {03 - rmtDev/lecture 27 => 08 - rmtDev/Sorting Component #2}/index.js (100%) rename {03 - rmtDev/lecture 28 => 08 - rmtDev/Sorting Component #2}/src/common.js (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Sorting Component #2}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 31 => 08 - rmtDev/Sorting Component #2}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 29 => 08 - rmtDev/Sorting Component #2}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 28 => 08 - rmtDev/Sorting Component #2}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 29 => 08 - rmtDev/Sorting Component #2}/src/components/Sorting.js (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Sorting Component #2}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Sorting Component}/index.css (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Sorting Component}/index.html (100%) rename {03 - rmtDev/lecture 28 => 08 - rmtDev/Sorting Component}/index.js (100%) rename {03 - rmtDev/lecture 27 => 08 - rmtDev/Sorting Component}/src/common.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Sorting Component}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 32 => 08 - rmtDev/Sorting Component}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 27 => 08 - rmtDev/Sorting Component}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 27 => 08 - rmtDev/Sorting Component}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 27 => 08 - rmtDev/Sorting Component}/src/components/Sorting.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/Sorting Component}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/State}/index.css (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/State}/index.html (100%) rename {03 - rmtDev/lecture 29 => 08 - rmtDev/State}/index.js (100%) rename {03 - rmtDev/lecture 29 => 08 - rmtDev/State}/src/common.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/State}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 33 => 08 - rmtDev/State}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 28 => 08 - rmtDev/State}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 29 => 08 - rmtDev/State}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 28 => 08 - rmtDev/State}/src/components/Sorting.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/State}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Storage Component}/index.css (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Storage Component}/index.html (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/Storage Component}/index.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Storage Component}/src/common.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/Storage Component}/src/components/Bookmarks.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Storage Component}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/Storage Component}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/Storage Component}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Storage Component}/src/components/Pagination.js (100%) rename {03 - rmtDev/lecture 34 => 08 - rmtDev/Storage Component}/src/components/Router.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Storage Component}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Storage Component}/src/components/Sorting.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Storage Component}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Storage Component}/src/components/Storage.js (100%) rename {03 - rmtDev/lecture 36/src => 08 - rmtDev/Taking Care of Details}/index.css (100%) rename {03 - rmtDev/lecture 36/src => 08 - rmtDev/Taking Care of Details}/index.html (100%) rename {03 - rmtDev/lecture 35 => 08 - rmtDev/Taking Care of Details}/index.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/common.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/components/Bookmarks.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/components/Error.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/components/JobDetails.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/components/JobList.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/components/Pagination.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/components/Router.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/components/Search.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/components/Sorting.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/components/Spinner.js (100%) rename {03 - rmtDev/lecture 36 => 08 - rmtDev/Taking Care of Details}/src/components/Storage.js (100%) rename {03 - rmtDev => 08 - rmtDev}/resources.txt (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/babel.config.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/dist/index.html (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/dist/main.css (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/dist/main.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/dist/main.js.LICENSE.txt (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/package-lock.json (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/package.json (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/postcss.config.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/common.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/components/Bookmarks.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/components/Error.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/components/JobDetails.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/components/JobList.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/components/Pagination.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/components/Router.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/components/Search.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/components/Sorting.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/components/Spinner.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/components/Storage.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/index.css (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/index.html (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/src/index.js (100%) rename {04 - Supplemental/lecture 37 => 09 - Supplemental/Formatting in JS}/webpack.config.js (100%) rename {04 - Supplemental/lecture 38 => 09 - Supplemental/Has the For Loop Been Replaced by forEach}/index.html (100%) rename {04 - Supplemental/lecture 38 => 09 - Supplemental/Has the For Loop Been Replaced by forEach}/script.js (100%) rename {04 - Supplemental/lecture 38 => 09 - Supplemental/Has the For Loop Been Replaced by forEach}/style.css (100%) diff --git a/01 - Introduction/intro.txt b/01 - Introduction/intro.txt new file mode 100644 index 0000000..9600d83 --- /dev/null +++ b/01 - Introduction/intro.txt @@ -0,0 +1,7 @@ +Welcome to the course! + +We will build some beautiful projects in this course. Sometimes, you will have to copy-paste an image URL or something else. You can find all those resources in a 'resources.txt' file in the folder for the project. + +Glad to have you on board, let's get started! + +Wesley \ No newline at end of file diff --git a/01 - Modern JS Fundamentals/index.html b/02 - Modern JS Fundamentals/index.html similarity index 100% rename from 01 - Modern JS Fundamentals/index.html rename to 02 - Modern JS Fundamentals/index.html diff --git a/01 - Modern JS Fundamentals/script.js b/02 - Modern JS Fundamentals/script.js similarity index 100% rename from 01 - Modern JS Fundamentals/script.js rename to 02 - Modern JS Fundamentals/script.js diff --git a/01 - Modern JS Fundamentals/style.css b/02 - Modern JS Fundamentals/style.css similarity index 100% rename from 01 - Modern JS Fundamentals/style.css rename to 02 - Modern JS Fundamentals/style.css diff --git a/03 - Fancy Counter/index.html b/03 - Fancy Counter/index.html new file mode 100644 index 0000000..cbda3b5 --- /dev/null +++ b/03 - Fancy Counter/index.html @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + Fancy Counter + + + +
    +

    Fancy Counter

    + + 0 + + + +
    + + +
    +
    + + + diff --git a/03 - Fancy Counter/resources.txt b/03 - Fancy Counter/resources.txt new file mode 100644 index 0000000..41d87c9 --- /dev/null +++ b/03 - Fancy Counter/resources.txt @@ -0,0 +1,10 @@ +####### Font Link + + + + + + +####### Font Awesome + + diff --git a/03 - Fancy Counter/script.js b/03 - Fancy Counter/script.js new file mode 100644 index 0000000..712fdda --- /dev/null +++ b/03 - Fancy Counter/script.js @@ -0,0 +1,83 @@ +const counterEl = document.querySelector('.counter'); +const increaseButtonEl = document.querySelector('.counter__button--increase'); +const decreaseButtonEl = document.querySelector('.counter__button--decrease'); +const resetButtonEl = document.querySelector('.counter__reset-button'); +const counterValueEl = document.querySelector('.counter__value'); +const counterTitleEl = document.querySelector('.counter__title'); + +resetButtonEl.addEventListener('click', () => { + // set counter value to 0 + counterValueEl.textContent = 0; + + // reset background color + counterEl.classList.remove('counter--limit'); + + // reset counter title + counterTitleEl.textContent = 'Fancy Counter'; + + // enable increase and decrease buttons + increaseButtonEl.disabled = false; + decreaseButtonEl.disabled = false; + + // unfocus (blur) reset button + resetButtonEl.blur(); +}); + +decreaseButtonEl.addEventListener('click', () => { + // get current value of counter + const currentValue = counterValueEl.textContent; + + // convert value value to number type + const currentValueAsNumber = +currentValue; + + // decrement by 1 + let newValue = currentValueAsNumber - 1; + + // check if new value is less than 0 + if (newValue < 0) { + // if it is, force it to be 0 instead + newValue = 0; + } + + // update counter value with new value + counterValueEl.textContent = newValue; + + // unfocus (blur) button + decreaseButtonEl.blur(); +}); + +const incrementCounter = () => { + // get current value of counter + const currentValue = counterValueEl.textContent; + + // convert value to number type + const currentValueAsNumber = +currentValue; + + // increment by 1 + let newValue = currentValueAsNumber + 1; + + // check if new value is greater than 5 + if (newValue > 5) { + // if it is, force it to be 5 instead + newValue = 5; + + // give visual indicator that limit has been reached + counterEl.classList.add('counter--limit'); + + // update counter title to say limit has been reached + counterTitleEl.innerHTML = 'Limit! Buy Pro for >5'; + + // disable increase and decrease buttons + increaseButtonEl.disabled = true; + decreaseButtonEl.disabled = true; + } + + // set counter element with new value + counterValueEl.textContent = newValue; + + // unfocus (blur) button + increaseButtonEl.blur(); +} + +increaseButtonEl.addEventListener('click', incrementCounter); +document.addEventListener('keydown', incrementCounter); \ No newline at end of file diff --git a/03 - Fancy Counter/style.css b/03 - Fancy Counter/style.css new file mode 100644 index 0000000..5645ed4 --- /dev/null +++ b/03 - Fancy Counter/style.css @@ -0,0 +1,129 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +button { + all: unset; +} + +body { + font-family: 'Inter', sans-serif; + height: 100vh; + background-image: radial-gradient(ellipse, #385300, #212900); + display: flex; + justify-content: center; + align-items: center; +} + +.counter { + background-color: #bef227; + width: 476px; + height: 615px; + border-radius: 12px; + box-shadow: 0 25px 121px rgba(0, 0, 0, 0.5); + display: flex; + flex-direction: column; + align-items: center; + transition: all 0.4s; +} + +.counter--limit { + background-color: #a3d11b; +} + +.counter__title { + opacity: 0.6; + text-transform: uppercase; + letter-spacing: 1px; + width: 200px; + text-align: center; + line-height: 0.9; + font-weight: 500; + font-size: 32px; + margin-top: 60px; +} + +.counter__value { + color: #1d2507; + font-size: 200px; + font-weight: 600; + margin-top: 10px; +} + +.counter__reset-button { + cursor: pointer; +} + +.counter__reset-icon { + opacity: 0.3; + font-size: 35px; + transition: all 0.4s; +} + +.counter__reset-icon:hover { + opacity: 0.5; +} + +.counter__buttons { + background-color: #1d2507; + height: 116px; + width: 100%; + margin-top: auto; + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; + display: flex; +} + +.counter__button { + flex: 1; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + transition: all 0.4s; +} + +.counter__button:hover { + background-color: #212b06; +} + +.counter__button--decrease { + +} + +.counter__button--increase { + border-left: 1px solid rgba(189, 254, 0, 0.15); +} + +.counter__icon { + color: #bdfe00; + opacity: 0.9; + font-size: 40px; +} + +.counter__icon--minus { + +} + +.counter__icon--plus { + +} + +@media (max-width: 680px) or (max-height: 730px) { + .counter { + width: 100%; + height: 100%; + border-radius: 0; + } + + .counter__title { + margin-top: 100px; + } + + .counter__buttons { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } +} \ No newline at end of file diff --git a/04 - Supplemental/lecture 39/babel.config.js b/04 - Supplemental/lecture 39/babel.config.js deleted file mode 100644 index 9c22988..0000000 --- a/04 - Supplemental/lecture 39/babel.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - presets: [ - [ - '@babel/preset-env', - { - useBuiltIns: 'usage', - corejs: '3.0' - } - ] - ] -}; \ No newline at end of file diff --git a/04 - Supplemental/lecture 39/dist/index.html b/04 - Supplemental/lecture 39/dist/index.html deleted file mode 100644 index 217a364..0000000 --- a/04 - Supplemental/lecture 39/dist/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/04 - Supplemental/lecture 39/dist/main.css b/04 - Supplemental/lecture 39/dist/main.css deleted file mode 100644 index 56d663d..0000000 --- a/04 - Supplemental/lecture 39/dist/main.css +++ /dev/null @@ -1 +0,0 @@ -*,:after,:before{box-sizing:border-box;margin:0;padding:0}ol,ul{list-style:none}a{color:inherit;text-decoration:initial}button{background-color:initial}button,input{border:initial;font:inherit;outline:initial}.u-bold{font-weight:700}@-webkit-keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@-webkit-keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}@keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}.body{background-color:#dee3e9;color:#16181c;font-family:Inter,sans-serif;min-height:100vh;position:relative;scrollbar-width:none}.body::-webkit-scrollbar{display:none}.first-heading{color:#fff;display:none;font-size:1.9375rem;font-size:1.6875rem;font-weight:400}.second-heading{color:#fff;font-size:1.4375rem;font-weight:500}.third-heading{font-size:.8125rem;font-weight:600}.fourth-heading{font-size:1rem;font-weight:600;text-transform:capitalize}.background{background-image:linear-gradient(125deg,#1f74f1 -10%,#0850b9);box-shadow:0 .125rem .1875rem rgba(0,0,0,.1);height:13.125rem;left:0;overflow:hidden;position:absolute;top:0;width:100%;z-index:-2}.background:before{background-image:linear-gradient(-180deg,rgba(0,0,0,.025),rgba(0,0,0,.075) 99%);bottom:0;content:"";left:0;position:absolute;right:0;top:0}.background__pattern{left:0;position:absolute;top:-1.5625rem;-webkit-transform:scale(1.1);transform:scale(1.1);-webkit-user-select:none;-ms-user-select:none;user-select:none;z-index:-1}.header{margin-bottom:0;position:relative}.header__top{align-items:center;-webkit-animation:intro .3s;animation:intro .3s;display:flex;justify-content:center;margin:0 auto;max-width:62.5rem;padding:2.5rem .75rem 0;position:relative}.header__submit-job{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.75rem;margin-right:0;padding-left:.625rem;text-transform:lowercase;-webkit-transform:translateY(-.125rem);transform:translateY(-.125rem)}.header__submit-job:before{background-color:hsla(0,0%,100%,.25);content:"";display:inline-block;height:.8125rem;margin-right:.5rem;-webkit-transform:translateY(.1875rem);transform:translateY(.1875rem);width:.125rem}.logo{margin-left:-.5rem;-webkit-user-select:none;-ms-user-select:none;user-select:none}.logo__img{margin-bottom:-.3125rem}.bookmarks-btn{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.8125rem;height:2rem;margin-left:.8125rem;padding-left:.8125rem;position:relative;text-transform:lowercase;transition:all .2s}.bookmarks-btn--active,.bookmarks-btn:focus,.bookmarks-btn:hover{color:#fff}.bookmarks-btn--active .bookmarks-btn__icon,.bookmarks-btn:focus .bookmarks-btn__icon,.bookmarks-btn:hover .bookmarks-btn__icon{color:hsla(0,0%,100%,.8)}.bookmarks-btn:before{background-color:hsla(0,0%,100%,.3);content:"";display:block;height:.9375rem;left:0;position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:.125rem}.bookmarks-btn__icon{color:hsla(0,0%,100%,.6);font-size:.625rem;margin-left:.125rem;-webkit-transform:translateY(-.0625rem);transform:translateY(-.0625rem);transition:all .2s}.job-list{background-color:#fff;scrollbar-color:#cacdd0 #fff;scrollbar-width:thin}.job-list::-webkit-scrollbar{width:.25rem}.job-list::-webkit-scrollbar-track{background-color:#fff}.job-list::-webkit-scrollbar-thumb{background-color:#cacdd0;-webkit-transition:all .2s;transition:all .2s}.job-list::-webkit-scrollbar-thumb:hover{background-color:#b1b4b8}.job-list--bookmarks{border-radius:.25rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);left:50%;min-height:4.75rem;min-width:21.25rem;opacity:0;overflow:hidden;pointer-events:none;position:fixed;top:5.125rem;-webkit-transform:scale(.9) translateX(-50%);transform:scale(.9) translateX(-50%);-webkit-transform-origin:left;transform-origin:left;transition:all .2s;visibility:hidden;width:21.25rem;z-index:10}.job-list--bookmarks:hover{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-list--bookmarks:before{color:#616268;content:"Nothing bookmarked yet...";font-size:.8125rem;left:50%;position:absolute;top:49%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);z-index:-1}.job-list--visible{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-item{background-color:#fff;cursor:pointer;padding:.875rem 1.25rem;transition:all .2s}.job-item:not(:nth-child(7)){border-bottom:.0625rem solid #ebeff1}.job-item--active,.job-item:hover{background-color:#f4f5f7}.job-item__link{display:flex;height:100%;width:100%}.job-item__badge{align-items:center;background-color:#8dd335;border-radius:.3125rem;display:flex;font-size:.8125rem;font-weight:600;height:2.875rem;justify-content:center;margin-right:.8125rem;width:2.375rem}.job-item:nth-child(4n+2) .job-item__badge{background-color:#3d87f1}.job-item:nth-child(4n+3) .job-item__badge{background-color:#d2d631}.job-item:nth-child(4n+4) .job-item__badge{background-color:#d96a46}.job-item__company{font-size:.75rem;font-style:italic;margin-bottom:.125rem}.job-item__extras{-webkit-column-gap:.625rem;column-gap:.625rem;display:grid;grid-template-columns:4.0625rem 4.5rem 4.0625rem}.job-item__extra{color:#4d5054;font-size:.6875rem}.job-item__extra-icon{color:#bec5ce;font-size:.625rem;margin-right:.0625rem}.job-item__right{align-items:flex-end;display:flex;flex-direction:column;margin-left:auto}.job-item__bookmark-icon{color:#d7dbe0;cursor:pointer;font-size:.875rem;transition:all .2s}.job-item__bookmark-icon--bookmarked{color:#2671dd}.job-item__time{color:#515459;font-size:.625rem;margin-top:.25rem}.main{justify-content:center}.intro,.main{align-items:center;display:flex;flex-direction:column}.intro{margin-top:1.25rem;row-gap:2.0625rem;row-gap:1.25rem}.search{-webkit-animation:intro .3s .1s backwards;animation:intro .3s .1s backwards;position:relative}.search__submit-btn{cursor:pointer;left:1.5625rem;position:absolute;top:1.0625rem}.search__icon{color:rgba(0,0,0,.73);transition:all .2s}.search__icon:focus,.search__icon:hover{color:rgba(0,0,0,.93)}.search__input{background-color:hsla(0,0%,100%,.9);border-radius:.25rem;caret-color:rgba(0,0,0,.5);color:rgba(0,0,0,.9);height:3.5rem;padding-bottom:.125rem;padding-left:3.4375rem;padding-right:.9375rem;transition:all .2s,box-shadow .1s;width:38.125rem}.search__input::selection{background-color:rgba(0,0,0,.25)}.search__input:focus,.search__input:hover{background-color:#fff}.search__input:focus{box-shadow:0 0 0 .25rem hsla(0,0%,100%,.4)}.search__input::-webkit-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input:-ms-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input::placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input--invalid{box-shadow:0 0 0 .25rem rgba(47,19,19,.729)}.container{-webkit-animation:intro .3s .2s backwards;animation:intro .3s .2s backwards;background-color:#fff;border-radius:.5rem;border-top-right-radius:.5625rem;box-shadow:0 .1875rem .3125rem rgba(0,0,0,.07);display:flex;height:38.5rem;margin:2.5rem .75rem 0;width:61rem}.search-results{cursor:default;display:flex;flex-direction:column;position:relative;width:21.25rem}.search-results__top{align-items:center;border-bottom:.0625rem solid #e8edf0;display:flex;justify-content:space-between;padding:.625rem 1.25rem}.count{font-size:.75rem}.sorting__icon{color:#4c4f50;font-size:.6875rem;margin-right:.3125rem}.sorting__button{background-color:#e8edf0;border-radius:.1875rem;cursor:pointer;font-size:.625rem;margin-left:.125rem;padding:.375rem .5rem;text-transform:uppercase;transition:all .2s}.sorting__button:focus,.sorting__button:hover{background-color:#d0d5d8}.sorting__button--active,.sorting__button--active:focus,.sorting__button--active:hover{background-color:#3c4041;color:#fff}.pagination{align-items:center;border-top:.0625rem solid #e8edf0;display:flex;height:2.5rem;justify-content:space-between;margin-top:auto;padding:0 1.25rem .0625rem}.pagination__button{background-color:#eceff2;border-radius:31.25rem;color:#747c82;cursor:pointer;font-size:.625rem;padding:.25rem .625rem;transition:all .2s,visibility 0s}.pagination__button:focus,.pagination__button:hover{background-color:#dde2e6}.pagination__button--hidden{visibility:hidden}.pagination__number{font-weight:500}.pagination__icon{color:#9fa6b0;font-size:.5rem}.job-details{background-color:#eff2f5;border-bottom-right-radius:.5rem;border-top-right-radius:.75rem;flex:1;position:relative}.job-details__start-view{left:50%;position:absolute;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.job-details__start-text{color:#24292d;font-size:.875rem;opacity:.55;text-align:center;width:17.1875rem}.job-details__start-text--big{color:#0d1114;font-size:1.125rem;font-weight:600;margin-bottom:.625rem}.job-details__content{height:100%}.job-details__cover-img{border-top-right-radius:.5rem;height:10.875rem;-o-object-fit:cover;object-fit:cover;position:absolute;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none;width:100%;z-index:0}.job-details__footer{border-top:.0625rem solid #dce2e8;margin-left:2.625rem;margin-right:2.625rem;margin-top:2.0625rem;padding-top:.8125rem}.job-details__footer-text{color:#858b8f;font-size:.625rem}.apply-btn{align-items:center;background-color:#2671dd;border-radius:.1875rem;color:hsla(0,0%,100%,.92);cursor:pointer;display:flex;font-size:.6875rem;padding:.3125rem .625rem .375rem;position:absolute;right:.75rem;text-transform:uppercase;top:.75rem;transition:all .2s;z-index:2}.apply-btn:focus,.apply-btn:hover{background-color:#1d60bd;color:#fff}.apply-btn__icon{color:hsla(0,0%,100%,.65);font-size:.5rem;margin-left:.25rem;margin-top:-.0625rem}.job-info{-webkit-column-gap:1rem;column-gap:1rem;display:flex;margin-bottom:2.5rem;padding-top:7.5rem;position:relative;z-index:1}.job-info:before{background-image:linear-gradient(0deg,rgba(0,0,0,.7),rgba(0,0,0,.15));border-top-right-radius:.5rem;content:"";height:10.875rem;left:0;position:absolute;top:0;width:100%;z-index:-1}.job-info__left{padding-left:2.625rem}.job-info__right{padding-right:2.625rem}.job-info__badge{align-items:center;background-color:#d0d335;border-radius:.3125rem;display:flex;font-size:1.375rem;font-weight:600;height:4.375rem;justify-content:center;margin-bottom:.8125rem;width:3.4375rem}.job-info__below-badge{display:flex;justify-content:space-between}.job-info__time{color:#494d4f;font-size:.75rem;-webkit-transform:translateY(.0625rem);transform:translateY(.0625rem)}.job-info__bookmark-btn{cursor:pointer}.job-info__bookmark-btn:hover .job-info__bookmark-icon{color:#2671dd}.job-info__bookmark-icon{color:#d7dbe0;font-size:1.125rem;transition:all .2s}.job-info__bookmark-icon--bookmarked{color:#2671dd}.job-info__company{color:hsla(0,0%,100%,.8);font-size:.875rem;font-style:italic}.job-info__description{font-size:.875rem;line-height:1.4;margin-bottom:.75rem;margin-top:1.125rem}.job-info__extras{-webkit-column-gap:2.1875rem;column-gap:2.1875rem;display:flex}.job-info__extra{align-items:center;display:flex;font-size:.75rem}.job-info__extra-icon{align-items:center;background-color:#e4e9ed;border-radius:50%;color:#a1a8b0;display:flex;font-size:.5625rem;height:1.4375rem;justify-content:center;margin-right:.5rem;width:1.4375rem}.qualifications{display:flex;margin-bottom:1.875rem;padding-left:2.625rem;padding-right:2.625rem}.qualifications__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.qualifications__left{margin-right:2.1875rem}.qualifications__list{display:flex;flex-wrap:wrap;gap:.375rem}.qualifications__item{background-color:#e6ebee;border-radius:.125rem;color:#494d4f;font-size:.8125rem;padding:.375rem .625rem}.reviews{display:flex;padding-left:2.625rem;padding-right:2.625rem}.reviews__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.reviews__left{margin-right:2.1875rem}.reviews__list{-webkit-column-gap:1.25rem;column-gap:1.25rem;display:grid;flex:1;grid-template-columns:1fr 1fr;grid-template-rows:auto auto;row-gap:1.25rem}.reviews__item{color:#494d4f;font-size:.8125rem;font-style:italic;position:relative;-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.reviews__item:before{color:#d2d7db;content:"“";font-size:3.125rem;left:-.625rem;position:absolute;top:-.9375rem;-webkit-transform:translateZ(-.0625rem);transform:translateZ(-.0625rem)}.footer{align-items:center;color:#a4a9ac;display:flex;justify-content:space-between;margin:.9375rem auto 0;max-width:62.5rem;padding:0 .75rem}.copyright{font-size:.6875rem}.copyright ::selection{background-color:hsla(0,0%,100%,.1)}.copyright__text{line-height:1.2}.copyright__link{text-decoration:underline}.copyright__icon{color:#aeb3b6;font-size:.625rem;margin-left:.125rem;margin-right:.25rem}.jobs-available{align-self:flex-start;font-size:.6875rem}.spinner{-webkit-animation:spinner 1s linear infinite;animation:spinner 1s linear infinite;border-radius:50%;position:absolute;visibility:hidden}.spinner--search{border:.3125rem solid #e2e7e9;border-left-color:#ccd1d3;height:5.3125rem;left:50%;top:18%;width:5.3125rem}.spinner--job-details{border:.375rem solid #d5d9db;border-left-color:#bbc0c2;height:6.5625rem;left:50%;top:40%;width:6.5625rem}.spinner--visible{visibility:visible}.error{background:#fff;border-radius:.1875rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);display:flex;min-height:2.875rem;opacity:0;padding:.875rem 1.25rem;position:absolute;right:.9375rem;top:.9375rem;-webkit-transform:translateY(-7.5rem);transform:translateY(-7.5rem);transition:all .3s;visibility:hidden;width:17.5rem}.error--visible{opacity:1;-webkit-transform:initial;transform:none;visibility:initial}.error__icon{color:#7b4040;font-size:1rem;margin-top:.125rem}.error__right{margin-left:.625rem}.error__title{font-size:.75rem;font-weight:500;margin-bottom:.0625rem;text-transform:uppercase}.error__text{color:#616268;font-size:.8125rem}@media (max-height:925px) and (min-width:1010px){.body{padding-bottom:3.125rem}}@media (max-width:1179px){.job-list--bookmarks{right:0}}@media (max-width:1009px){.body{padding:0 .75rem 3.125rem}.header__top{max-width:50rem;padding-left:0;padding-right:0}.container{border-radius:.5rem;flex-direction:column;height:auto;max-width:50rem;overflow:hidden;width:100%}.search-results{width:100%}.job-details{display:none}.footer{max-width:50rem;padding-left:0;padding-right:0}}@media (max-width:660px){.intro,.search,.search__input{width:100%}.footer{justify-content:center}.copyright{text-align:center}.jobs-available{display:none;margin-left:.9375rem;text-align:right}.intro{row-gap:1.5625rem}.first-heading{max-width:25rem;text-align:center}}@media (max-width:370px){.job-list--bookmarks{min-width:auto;width:93vw}.job-item{width:100%}.job-item__badge{display:none}.error{left:50%;right:auto;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:93vw}} diff --git a/04 - Supplemental/lecture 39/dist/main.js b/04 - Supplemental/lecture 39/dist/main.js deleted file mode 100644 index eba8c55..0000000 --- a/04 - Supplemental/lecture 39/dist/main.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! For license information please see main.js.LICENSE.txt */ -!function(){var t={9662:function(t,r,e){var n=e(614),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a function")}},9483:function(t,r,e){var n=e(4411),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a constructor")}},6077:function(t,r,e){var n=e(614),o=String,i=TypeError;t.exports=function(t){if("object"==typeof t||n(t))return t;throw i("Can't set "+o(t)+" as a prototype")}},1223:function(t,r,e){var n=e(5112),o=e(30),i=e(3070).f,a=n("unscopables"),c=Array.prototype;null==c[a]&&i(c,a,{configurable:!0,value:o(null)}),t.exports=function(t){c[a][t]=!0}},5787:function(t,r,e){var n=e(7976),o=TypeError;t.exports=function(t,r){if(n(r,t))return t;throw o("Incorrect invocation")}},9670:function(t,r,e){var n=e(111),o=String,i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not an object")}},8533:function(t,r,e){"use strict";var n=e(2092).forEach,o=e(2133)("forEach");t.exports=o?[].forEach:function(t){return n(this,t,arguments.length>1?arguments[1]:void 0)}},8457:function(t,r,e){"use strict";var n=e(9974),o=e(6916),i=e(7908),a=e(3411),c=e(7659),u=e(4411),s=e(6244),f=e(6135),l=e(8554),p=e(1246),h=Array;t.exports=function(t){var r=i(t),e=u(this),v=arguments.length,d=v>1?arguments[1]:void 0,y=void 0!==d;y&&(d=n(d,v>2?arguments[2]:void 0));var m,g,b,x,w,_,S=p(r),j=0;if(!S||this===h&&c(S))for(m=s(r),g=e?new this(m):h(m);m>j;j++)_=y?d(r[j],j):r[j],f(g,j,_);else for(w=(x=l(r,S)).next,g=e?new this:[];!(b=o(w,x)).done;j++)_=y?a(x,d,[b.value,j],!0):b.value,f(g,j,_);return g.length=j,g}},1318:function(t,r,e){var n=e(5656),o=e(1400),i=e(6244),a=function(t){return function(r,e,a){var c,u=n(r),s=i(u),f=o(a,s);if(t&&e!=e){for(;s>f;)if((c=u[f++])!=c)return!0}else for(;s>f;f++)if((t||f in u)&&u[f]===e)return t||f||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},2092:function(t,r,e){var n=e(9974),o=e(1702),i=e(8361),a=e(7908),c=e(6244),u=e(5417),s=o([].push),f=function(t){var r=1==t,e=2==t,o=3==t,f=4==t,l=6==t,p=7==t,h=5==t||l;return function(v,d,y,m){for(var g,b,x=a(v),w=i(x),_=n(d,y),S=c(w),j=0,L=m||u,E=r?L(v,S):e||p?L(v,0):void 0;S>j;j++)if((h||j in w)&&(b=_(g=w[j],j,x),t))if(r)E[j]=b;else if(b)switch(t){case 3:return!0;case 5:return g;case 6:return j;case 2:s(E,g)}else switch(t){case 4:return!1;case 7:s(E,g)}return l?-1:o||f?f:E}};t.exports={forEach:f(0),map:f(1),filter:f(2),some:f(3),every:f(4),find:f(5),findIndex:f(6),filterReject:f(7)}},1194:function(t,r,e){var n=e(7293),o=e(5112),i=e(7392),a=o("species");t.exports=function(t){return i>=51||!n((function(){var r=[];return(r.constructor={})[a]=function(){return{foo:1}},1!==r[t](Boolean).foo}))}},2133:function(t,r,e){"use strict";var n=e(7293);t.exports=function(t,r){var e=[][t];return!!e&&n((function(){e.call(null,r||function(){return 1},1)}))}},1589:function(t,r,e){var n=e(1400),o=e(6244),i=e(6135),a=Array,c=Math.max;t.exports=function(t,r,e){for(var u=o(t),s=n(r,u),f=n(void 0===e?u:e,u),l=a(c(f-s,0)),p=0;s0;)t[n]=t[--n];n!==i++&&(t[n]=e)}return t},c=function(t,r,e,n){for(var o=r.length,i=e.length,a=0,c=0;a9007199254740991)throw r("Maximum allowed index exceeded");return t}},8324:function(t){t.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},8509:function(t,r,e){var n=e(317)("span").classList,o=n&&n.constructor&&n.constructor.prototype;t.exports=o===Object.prototype?void 0:o},8886:function(t,r,e){var n=e(8113).match(/firefox\/(\d+)/i);t.exports=!!n&&+n[1]},7871:function(t){t.exports="object"==typeof window&&"object"!=typeof Deno},256:function(t,r,e){var n=e(8113);t.exports=/MSIE|Trident/.test(n)},1528:function(t,r,e){var n=e(8113),o=e(7854);t.exports=/ipad|iphone|ipod/i.test(n)&&void 0!==o.Pebble},6833:function(t,r,e){var n=e(8113);t.exports=/(?:ipad|iphone|ipod).*applewebkit/i.test(n)},5268:function(t,r,e){var n=e(4326),o=e(7854);t.exports="process"==n(o.process)},1036:function(t,r,e){var n=e(8113);t.exports=/web0s(?!.*chrome)/i.test(n)},8113:function(t,r,e){var n=e(5005);t.exports=n("navigator","userAgent")||""},7392:function(t,r,e){var n,o,i=e(7854),a=e(8113),c=i.process,u=i.Deno,s=c&&c.versions||u&&u.version,f=s&&s.v8;f&&(o=(n=f.split("."))[0]>0&&n[0]<4?1:+(n[0]+n[1])),!o&&a&&(!(n=a.match(/Edge\/(\d+)/))||n[1]>=74)&&(n=a.match(/Chrome\/(\d+)/))&&(o=+n[1]),t.exports=o},8008:function(t,r,e){var n=e(8113).match(/AppleWebKit\/(\d+)\./);t.exports=!!n&&+n[1]},748:function(t){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(t,r,e){var n=e(7854),o=e(1236).f,i=e(8880),a=e(8052),c=e(3072),u=e(9920),s=e(4705);t.exports=function(t,r){var e,f,l,p,h,v=t.target,d=t.global,y=t.stat;if(e=d?n:y?n[v]||c(v,{}):(n[v]||{}).prototype)for(f in r){if(p=r[f],l=t.dontCallGetSet?(h=o(e,f))&&h.value:e[f],!s(d?f:v+(y?".":"#")+f,t.forced)&&void 0!==l){if(typeof p==typeof l)continue;u(p,l)}(t.sham||l&&l.sham)&&i(p,"sham",!0),a(e,f,p,t)}}},7293:function(t){t.exports=function(t){try{return!!t()}catch(t){return!0}}},2104:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.apply,a=o.call;t.exports="object"==typeof Reflect&&Reflect.apply||(n?a.bind(i):function(){return a.apply(i,arguments)})},9974:function(t,r,e){var n=e(1702),o=e(9662),i=e(4374),a=n(n.bind);t.exports=function(t,r){return o(t),void 0===r?t:i?a(t,r):function(){return t.apply(r,arguments)}}},4374:function(t,r,e){var n=e(7293);t.exports=!n((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))},6916:function(t,r,e){var n=e(4374),o=Function.prototype.call;t.exports=n?o.bind(o):function(){return o.apply(o,arguments)}},6530:function(t,r,e){var n=e(9781),o=e(2597),i=Function.prototype,a=n&&Object.getOwnPropertyDescriptor,c=o(i,"name"),u=c&&"something"===function(){}.name,s=c&&(!n||n&&a(i,"name").configurable);t.exports={EXISTS:c,PROPER:u,CONFIGURABLE:s}},1702:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.bind,a=o.call,c=n&&i.bind(a,a);t.exports=n?function(t){return t&&c(t)}:function(t){return t&&function(){return a.apply(t,arguments)}}},5005:function(t,r,e){var n=e(7854),o=e(614),i=function(t){return o(t)?t:void 0};t.exports=function(t,r){return arguments.length<2?i(n[t]):n[t]&&n[t][r]}},1246:function(t,r,e){var n=e(648),o=e(8173),i=e(7497),a=e(5112)("iterator");t.exports=function(t){if(null!=t)return o(t,a)||o(t,"@@iterator")||i[n(t)]}},8554:function(t,r,e){var n=e(6916),o=e(9662),i=e(9670),a=e(6330),c=e(1246),u=TypeError;t.exports=function(t,r){var e=arguments.length<2?c(t):r;if(o(e))return i(n(e,t));throw u(a(t)+" is not iterable")}},8173:function(t,r,e){var n=e(9662);t.exports=function(t,r){var e=t[r];return null==e?void 0:n(e)}},7854:function(t,r,e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e.g&&e.g)||function(){return this}()||Function("return this")()},2597:function(t,r,e){var n=e(1702),o=e(7908),i=n({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,r){return i(o(t),r)}},3501:function(t){t.exports={}},842:function(t,r,e){var n=e(7854);t.exports=function(t,r){var e=n.console;e&&e.error&&(1==arguments.length?e.error(t):e.error(t,r))}},490:function(t,r,e){var n=e(5005);t.exports=n("document","documentElement")},4664:function(t,r,e){var n=e(9781),o=e(7293),i=e(317);t.exports=!n&&!o((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},8361:function(t,r,e){var n=e(1702),o=e(7293),i=e(4326),a=Object,c=n("".split);t.exports=o((function(){return!a("z").propertyIsEnumerable(0)}))?function(t){return"String"==i(t)?c(t,""):a(t)}:a},2788:function(t,r,e){var n=e(1702),o=e(614),i=e(5465),a=n(Function.toString);o(i.inspectSource)||(i.inspectSource=function(t){return a(t)}),t.exports=i.inspectSource},9909:function(t,r,e){var n,o,i,a=e(8536),c=e(7854),u=e(1702),s=e(111),f=e(8880),l=e(2597),p=e(5465),h=e(6200),v=e(3501),d="Object already initialized",y=c.TypeError,m=c.WeakMap;if(a||p.state){var g=p.state||(p.state=new m),b=u(g.get),x=u(g.has),w=u(g.set);n=function(t,r){if(x(g,t))throw new y(d);return r.facade=t,w(g,t,r),r},o=function(t){return b(g,t)||{}},i=function(t){return x(g,t)}}else{var _=h("state");v[_]=!0,n=function(t,r){if(l(t,_))throw new y(d);return r.facade=t,f(t,_,r),r},o=function(t){return l(t,_)?t[_]:{}},i=function(t){return l(t,_)}}t.exports={set:n,get:o,has:i,enforce:function(t){return i(t)?o(t):n(t,{})},getterFor:function(t){return function(r){var e;if(!s(r)||(e=o(r)).type!==t)throw y("Incompatible receiver, "+t+" required");return e}}}},7659:function(t,r,e){var n=e(5112),o=e(7497),i=n("iterator"),a=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||a[i]===t)}},3157:function(t,r,e){var n=e(4326);t.exports=Array.isArray||function(t){return"Array"==n(t)}},614:function(t){t.exports=function(t){return"function"==typeof t}},4411:function(t,r,e){var n=e(1702),o=e(7293),i=e(614),a=e(648),c=e(5005),u=e(2788),s=function(){},f=[],l=c("Reflect","construct"),p=/^\s*(?:class|function)\b/,h=n(p.exec),v=!p.exec(s),d=function(t){if(!i(t))return!1;try{return l(s,f,t),!0}catch(t){return!1}},y=function(t){if(!i(t))return!1;switch(a(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return v||!!h(p,u(t))}catch(t){return!0}};y.sham=!0,t.exports=!l||o((function(){var t;return d(d.call)||!d(Object)||!d((function(){t=!0}))||t}))?y:d},4705:function(t,r,e){var n=e(7293),o=e(614),i=/#|\.prototype\./,a=function(t,r){var e=u[c(t)];return e==f||e!=s&&(o(r)?n(r):!!r)},c=a.normalize=function(t){return String(t).replace(i,".").toLowerCase()},u=a.data={},s=a.NATIVE="N",f=a.POLYFILL="P";t.exports=a},111:function(t,r,e){var n=e(614);t.exports=function(t){return"object"==typeof t?null!==t:n(t)}},1913:function(t){t.exports=!1},7850:function(t,r,e){var n=e(111),o=e(4326),i=e(5112)("match");t.exports=function(t){var r;return n(t)&&(void 0!==(r=t[i])?!!r:"RegExp"==o(t))}},2190:function(t,r,e){var n=e(5005),o=e(614),i=e(7976),a=e(3307),c=Object;t.exports=a?function(t){return"symbol"==typeof t}:function(t){var r=n("Symbol");return o(r)&&i(r.prototype,c(t))}},408:function(t,r,e){var n=e(9974),o=e(6916),i=e(9670),a=e(6330),c=e(7659),u=e(6244),s=e(7976),f=e(8554),l=e(1246),p=e(9212),h=TypeError,v=function(t,r){this.stopped=t,this.result=r},d=v.prototype;t.exports=function(t,r,e){var y,m,g,b,x,w,_,S=e&&e.that,j=!(!e||!e.AS_ENTRIES),L=!(!e||!e.IS_ITERATOR),E=!(!e||!e.INTERRUPTED),O=n(r,S),k=function(t){return y&&p(y,"normal",t),new v(!0,t)},P=function(t){return j?(i(t),E?O(t[0],t[1],k):O(t[0],t[1])):E?O(t,k):O(t)};if(L)y=t;else{if(!(m=l(t)))throw h(a(t)+" is not iterable");if(c(m)){for(g=0,b=u(t);b>g;g++)if((x=P(t[g]))&&s(d,x))return x;return new v(!1)}y=f(t,m)}for(w=y.next;!(_=o(w,y)).done;){try{x=P(_.value)}catch(t){p(y,"throw",t)}if("object"==typeof x&&x&&s(d,x))return x}return new v(!1)}},9212:function(t,r,e){var n=e(6916),o=e(9670),i=e(8173);t.exports=function(t,r,e){var a,c;o(t);try{if(!(a=i(t,"return"))){if("throw"===r)throw e;return e}a=n(a,t)}catch(t){c=!0,a=t}if("throw"===r)throw e;if(c)throw a;return o(a),e}},3383:function(t,r,e){"use strict";var n,o,i,a=e(7293),c=e(614),u=e(30),s=e(9518),f=e(8052),l=e(5112),p=e(1913),h=l("iterator"),v=!1;[].keys&&("next"in(i=[].keys())?(o=s(s(i)))!==Object.prototype&&(n=o):v=!0),null==n||a((function(){var t={};return n[h].call(t)!==t}))?n={}:p&&(n=u(n)),c(n[h])||f(n,h,(function(){return this})),t.exports={IteratorPrototype:n,BUGGY_SAFARI_ITERATORS:v}},7497:function(t){t.exports={}},6244:function(t,r,e){var n=e(7466);t.exports=function(t){return n(t.length)}},6339:function(t,r,e){var n=e(7293),o=e(614),i=e(2597),a=e(9781),c=e(6530).CONFIGURABLE,u=e(2788),s=e(9909),f=s.enforce,l=s.get,p=Object.defineProperty,h=a&&!n((function(){return 8!==p((function(){}),"length",{value:8}).length})),v=String(String).split("String"),d=t.exports=function(t,r,e){"Symbol("===String(r).slice(0,7)&&(r="["+String(r).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),e&&e.getter&&(r="get "+r),e&&e.setter&&(r="set "+r),(!i(t,"name")||c&&t.name!==r)&&p(t,"name",{value:r,configurable:!0}),h&&e&&i(e,"arity")&&t.length!==e.arity&&p(t,"length",{value:e.arity});try{e&&i(e,"constructor")&&e.constructor?a&&p(t,"prototype",{writable:!1}):t.prototype&&(t.prototype=void 0)}catch(t){}var n=f(t);return i(n,"source")||(n.source=v.join("string"==typeof r?r:"")),t};Function.prototype.toString=d((function(){return o(this)&&l(this).source||u(this)}),"toString")},4758:function(t){var r=Math.ceil,e=Math.floor;t.exports=Math.trunc||function(t){var n=+t;return(n>0?e:r)(n)}},5948:function(t,r,e){var n,o,i,a,c,u,s,f,l=e(7854),p=e(9974),h=e(1236).f,v=e(261).set,d=e(6833),y=e(1528),m=e(1036),g=e(5268),b=l.MutationObserver||l.WebKitMutationObserver,x=l.document,w=l.process,_=l.Promise,S=h(l,"queueMicrotask"),j=S&&S.value;j||(n=function(){var t,r;for(g&&(t=w.domain)&&t.exit();o;){r=o.fn,o=o.next;try{r()}catch(t){throw o?a():i=void 0,t}}i=void 0,t&&t.enter()},d||g||m||!b||!x?!y&&_&&_.resolve?((s=_.resolve(void 0)).constructor=_,f=p(s.then,s),a=function(){f(n)}):g?a=function(){w.nextTick(n)}:(v=p(v,l),a=function(){v(n)}):(c=!0,u=x.createTextNode(""),new b(n).observe(u,{characterData:!0}),a=function(){u.data=c=!c})),t.exports=j||function(t){var r={fn:t,next:void 0};i&&(i.next=r),o||(o=r,a()),i=r}},735:function(t,r,e){var n=e(133);t.exports=n&&!!Symbol.for&&!!Symbol.keyFor},133:function(t,r,e){var n=e(7392),o=e(7293);t.exports=!!Object.getOwnPropertySymbols&&!o((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&n&&n<41}))},8536:function(t,r,e){var n=e(7854),o=e(614),i=e(2788),a=n.WeakMap;t.exports=o(a)&&/native code/.test(i(a))},8523:function(t,r,e){"use strict";var n=e(9662),o=function(t){var r,e;this.promise=new t((function(t,n){if(void 0!==r||void 0!==e)throw TypeError("Bad Promise constructor");r=t,e=n})),this.resolve=n(r),this.reject=n(e)};t.exports.f=function(t){return new o(t)}},3929:function(t,r,e){var n=e(7850),o=TypeError;t.exports=function(t){if(n(t))throw o("The method doesn't accept regular expressions");return t}},30:function(t,r,e){var n,o=e(9670),i=e(6048),a=e(748),c=e(3501),u=e(490),s=e(317),f=e(6200)("IE_PROTO"),l=function(){},p=function(t){return" - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/04 - Supplemental/lecture 39/src/index.js b/04 - Supplemental/lecture 39/src/index.js deleted file mode 100644 index 7dfe77f..0000000 --- a/04 - Supplemental/lecture 39/src/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import './components/Bookmarks.js'; -import './components/Error.js'; -import './components/JobDetails.js'; -import './components/JobList.js'; -import './components/Pagination.js'; -import './components/Router.js'; -import './components/Search.js'; -import './components/Sorting.js'; -import './components/Spinner.js'; -import './components/Storage.js'; - -// import './index.css'; \ No newline at end of file diff --git a/04 - Supplemental/lecture 39/webpack.config.js b/04 - Supplemental/lecture 39/webpack.config.js deleted file mode 100644 index b970c2c..0000000 --- a/04 - Supplemental/lecture 39/webpack.config.js +++ /dev/null @@ -1,27 +0,0 @@ -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); - -module.exports = { - mode: 'production', - entry: './src/index.js', - output: { - filename: 'main.js' - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: 'main.css' - }) - ], - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: ['babel-loader'] - }, - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] - } - ] - } -}; \ No newline at end of file diff --git a/04 - Word Analytics/index.html b/04 - Word Analytics/index.html new file mode 100644 index 0000000..8d5c4d4 --- /dev/null +++ b/04 - Word Analytics/index.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + Word Analytics + + + +
    + +

    + WordAnalytics +

    + +
    + + +
    +
    + 0 +

    Words

    +
    +
    + 0 +

    Characters

    +
    +
    + +

    Twitter

    +
    +
    + +

    Facebook

    +
    +
    +
    + +
    + © Copyright by ByteGrad. All rights reserved. + Last checked limits: 17 days ago +
    + + + \ No newline at end of file diff --git a/04 - Word Analytics/resources.txt b/04 - Word Analytics/resources.txt new file mode 100644 index 0000000..b6ab578 --- /dev/null +++ b/04 - Word Analytics/resources.txt @@ -0,0 +1,11 @@ +####### Image URL + +https://images.unsplash.com/photo-1546453667-8a8d2d07bc20?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1974&q=100 + + +####### Font Link + + + + + diff --git a/04 - Word Analytics/script.js b/04 - Word Analytics/script.js new file mode 100644 index 0000000..f73ac23 --- /dev/null +++ b/04 - Word Analytics/script.js @@ -0,0 +1,43 @@ +const textareaEl = document.querySelector('.textarea'); +const charactersNumberEl = document.querySelector('.stat__number--characters'); +const twitterNumberEl = document.querySelector('.stat__number--twitter'); +const facebookNumberEl = document.querySelector('.stat__number--facebook'); +const wordsNumberEl = document.querySelector('.stat__number--words'); + +const inputHandler = () => { + // example of input validation + if (textareaEl.value.includes(' - - Overview of Modern JS Fundamentals - - - -

    This is a quick overview of modern JS

    - - - \ No newline at end of file diff --git a/02 - Modern JS Fundamentals/script.js b/02 - Modern JS Fundamentals/script.js deleted file mode 100644 index e5115f7..0000000 --- a/02 - Modern JS Fundamentals/script.js +++ /dev/null @@ -1,68 +0,0 @@ -// -- variables (var/let/const) & data types/structures (strings/numbers/booleans/arrays/objects) -- -// const description = 'We need a new floor.'; -// const squareMeters = 100; -// const specialCoating = true; -// const floorOptions = ['carpet', 'hardwood', 'tiles']; -// const renovationJob = { -// ownerName: 'John', -// maximumPrice: 5000, -// category: 'bathroom', -// newShower: true -// }; - - -// -- traditional functions vs arrow functions -- - -// function calculatePrice(sqMeters) { -// return 1000 + sqMeters; -// } - -// var calculatePrice = function(sqMeters) { -// return 1000 + sqMeters; -// }; - -// const calculatePrice = (sqMeters) => { -// return 1000 + sqMeters; -// }; - -// const calculatePrice = sqMeters => 1000 + sqMeters; - - -// -- string concatenation vs template literals -- -// const price = 5000; -// const result = 'The total cost will be: ' + price; -// const result = `The total cost will be: ${price}`; - - -// -- if-else vs ternary operator -- -// const price = 5000; - -// if (price) { -// console.log('hello'); -// } else { -// console.log('blabla'); -// } - -// price > 3000 ? console.log('expensive') : console.log('cheap'); - - -// -- manipulating HTML and CSS -- -// const headingEl = document.querySelector('.heading'); - -// headingEl.textContent = 'Hello everyone!'; -// headingEl.innerHTML = 'Hello everyone!'; -// headingEl.insertAdjacentHTML('beforebegin', 'Hello everyone!'); - -// headingEl.style.fontSize = '55px'; -// headingEl.classList.add('heading--big'); - - -// -- events and functions for handling events (also called "event handlers") -- -// const headingEl = document.querySelector('.heading'); - -// const clickHandler = () => { -// headingEl.style.color = 'red'; -// console.log('changed color'); -// }; - -// headingEl.addEventListener('click', clickHandler); \ No newline at end of file diff --git a/02 - Modern JS Fundamentals/style.css b/02 - Modern JS Fundamentals/style.css deleted file mode 100644 index a7721c6..0000000 --- a/02 - Modern JS Fundamentals/style.css +++ /dev/null @@ -1,7 +0,0 @@ -.heading { - font-size: 15px; -} - -.heading--big { - font-size: 55px; -} \ No newline at end of file diff --git a/03 - Fancy Counter/index.html b/03 - Fancy Counter/index.html deleted file mode 100644 index cbda3b5..0000000 --- a/03 - Fancy Counter/index.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - Fancy Counter - - - -
    -

    Fancy Counter

    - - 0 - - - -
    - - -
    -
    - - - diff --git a/03 - Fancy Counter/resources.txt b/03 - Fancy Counter/resources.txt deleted file mode 100644 index 41d87c9..0000000 --- a/03 - Fancy Counter/resources.txt +++ /dev/null @@ -1,10 +0,0 @@ -####### Font Link - - - - - - -####### Font Awesome - - diff --git a/03 - Fancy Counter/script.js b/03 - Fancy Counter/script.js deleted file mode 100644 index 712fdda..0000000 --- a/03 - Fancy Counter/script.js +++ /dev/null @@ -1,83 +0,0 @@ -const counterEl = document.querySelector('.counter'); -const increaseButtonEl = document.querySelector('.counter__button--increase'); -const decreaseButtonEl = document.querySelector('.counter__button--decrease'); -const resetButtonEl = document.querySelector('.counter__reset-button'); -const counterValueEl = document.querySelector('.counter__value'); -const counterTitleEl = document.querySelector('.counter__title'); - -resetButtonEl.addEventListener('click', () => { - // set counter value to 0 - counterValueEl.textContent = 0; - - // reset background color - counterEl.classList.remove('counter--limit'); - - // reset counter title - counterTitleEl.textContent = 'Fancy Counter'; - - // enable increase and decrease buttons - increaseButtonEl.disabled = false; - decreaseButtonEl.disabled = false; - - // unfocus (blur) reset button - resetButtonEl.blur(); -}); - -decreaseButtonEl.addEventListener('click', () => { - // get current value of counter - const currentValue = counterValueEl.textContent; - - // convert value value to number type - const currentValueAsNumber = +currentValue; - - // decrement by 1 - let newValue = currentValueAsNumber - 1; - - // check if new value is less than 0 - if (newValue < 0) { - // if it is, force it to be 0 instead - newValue = 0; - } - - // update counter value with new value - counterValueEl.textContent = newValue; - - // unfocus (blur) button - decreaseButtonEl.blur(); -}); - -const incrementCounter = () => { - // get current value of counter - const currentValue = counterValueEl.textContent; - - // convert value to number type - const currentValueAsNumber = +currentValue; - - // increment by 1 - let newValue = currentValueAsNumber + 1; - - // check if new value is greater than 5 - if (newValue > 5) { - // if it is, force it to be 5 instead - newValue = 5; - - // give visual indicator that limit has been reached - counterEl.classList.add('counter--limit'); - - // update counter title to say limit has been reached - counterTitleEl.innerHTML = 'Limit! Buy Pro for >5'; - - // disable increase and decrease buttons - increaseButtonEl.disabled = true; - decreaseButtonEl.disabled = true; - } - - // set counter element with new value - counterValueEl.textContent = newValue; - - // unfocus (blur) button - increaseButtonEl.blur(); -} - -increaseButtonEl.addEventListener('click', incrementCounter); -document.addEventListener('keydown', incrementCounter); \ No newline at end of file diff --git a/03 - Fancy Counter/style.css b/03 - Fancy Counter/style.css deleted file mode 100644 index 5645ed4..0000000 --- a/03 - Fancy Counter/style.css +++ /dev/null @@ -1,129 +0,0 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -button { - all: unset; -} - -body { - font-family: 'Inter', sans-serif; - height: 100vh; - background-image: radial-gradient(ellipse, #385300, #212900); - display: flex; - justify-content: center; - align-items: center; -} - -.counter { - background-color: #bef227; - width: 476px; - height: 615px; - border-radius: 12px; - box-shadow: 0 25px 121px rgba(0, 0, 0, 0.5); - display: flex; - flex-direction: column; - align-items: center; - transition: all 0.4s; -} - -.counter--limit { - background-color: #a3d11b; -} - -.counter__title { - opacity: 0.6; - text-transform: uppercase; - letter-spacing: 1px; - width: 200px; - text-align: center; - line-height: 0.9; - font-weight: 500; - font-size: 32px; - margin-top: 60px; -} - -.counter__value { - color: #1d2507; - font-size: 200px; - font-weight: 600; - margin-top: 10px; -} - -.counter__reset-button { - cursor: pointer; -} - -.counter__reset-icon { - opacity: 0.3; - font-size: 35px; - transition: all 0.4s; -} - -.counter__reset-icon:hover { - opacity: 0.5; -} - -.counter__buttons { - background-color: #1d2507; - height: 116px; - width: 100%; - margin-top: auto; - border-bottom-right-radius: 10px; - border-bottom-left-radius: 10px; - display: flex; -} - -.counter__button { - flex: 1; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - transition: all 0.4s; -} - -.counter__button:hover { - background-color: #212b06; -} - -.counter__button--decrease { - -} - -.counter__button--increase { - border-left: 1px solid rgba(189, 254, 0, 0.15); -} - -.counter__icon { - color: #bdfe00; - opacity: 0.9; - font-size: 40px; -} - -.counter__icon--minus { - -} - -.counter__icon--plus { - -} - -@media (max-width: 680px) or (max-height: 730px) { - .counter { - width: 100%; - height: 100%; - border-radius: 0; - } - - .counter__title { - margin-top: 100px; - } - - .counter__buttons { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } -} \ No newline at end of file diff --git a/04 - Word Analytics/index.html b/04 - Word Analytics/index.html deleted file mode 100644 index 8d5c4d4..0000000 --- a/04 - Word Analytics/index.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - Word Analytics - - - -
    - -

    - WordAnalytics -

    - -
    - - -
    -
    - 0 -

    Words

    -
    -
    - 0 -

    Characters

    -
    -
    - -

    Twitter

    -
    -
    - -

    Facebook

    -
    -
    -
    - -
    - © Copyright by ByteGrad. All rights reserved. - Last checked limits: 17 days ago -
    - - - \ No newline at end of file diff --git a/04 - Word Analytics/resources.txt b/04 - Word Analytics/resources.txt deleted file mode 100644 index b6ab578..0000000 --- a/04 - Word Analytics/resources.txt +++ /dev/null @@ -1,11 +0,0 @@ -####### Image URL - -https://images.unsplash.com/photo-1546453667-8a8d2d07bc20?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1974&q=100 - - -####### Font Link - - - - - diff --git a/04 - Word Analytics/script.js b/04 - Word Analytics/script.js deleted file mode 100644 index f73ac23..0000000 --- a/04 - Word Analytics/script.js +++ /dev/null @@ -1,43 +0,0 @@ -const textareaEl = document.querySelector('.textarea'); -const charactersNumberEl = document.querySelector('.stat__number--characters'); -const twitterNumberEl = document.querySelector('.stat__number--twitter'); -const facebookNumberEl = document.querySelector('.stat__number--facebook'); -const wordsNumberEl = document.querySelector('.stat__number--words'); - -const inputHandler = () => { - // example of input validation - if (textareaEl.value.includes(' - - afdasdf CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      - -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/Counter Component/script.js b/06 - CorpComment/Counter Component/script.js deleted file mode 100644 index 6d694c9..0000000 --- a/06 - CorpComment/Counter Component/script.js +++ /dev/null @@ -1,19 +0,0 @@ -// -- COUNTER COMPONENT -- -const textareaEl = document.querySelector('.form__textarea'); -const counterEl = document.querySelector('.counter'); - -const inputHandler = () => { - // determine maximum number of characters - const maxNrChars = 150; - - // determine number of characters currently typed - const nrCharsTyped = textareaEl.value.length; - - // calculate number of characters left (maximum minus currently typed) - const charsLeft = maxNrChars - nrCharsTyped; - - // show number of characters left - counterEl.textContent = charsLeft; -}; - -textareaEl.addEventListener('input', inputHandler); diff --git a/06 - CorpComment/Counter Component/style.css b/06 - CorpComment/Counter Component/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/06 - CorpComment/Counter Component/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/Feedback List Component #1/index.html b/06 - CorpComment/Feedback List Component #1/index.html deleted file mode 100644 index b4a3c9b..0000000 --- a/06 - CorpComment/Feedback List Component #1/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      -
      -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/Feedback List Component #1/script.js b/06 - CorpComment/Feedback List Component #1/script.js deleted file mode 100644 index e919ed2..0000000 --- a/06 - CorpComment/Feedback List Component #1/script.js +++ /dev/null @@ -1,137 +0,0 @@ -// -- GLOBAL -- -const MAX_CHARS = 150; - -const textareaEl = document.querySelector('.form__textarea'); -const counterEl = document.querySelector('.counter'); -const formEl = document.querySelector('.form'); -const feedbackListEl = document.querySelector('.feedbacks'); -const submitBtnEl = document.querySelector('.submit-btn'); -const spinnerEl = document.querySelector('.spinner'); - - -// -- COUNTER COMPONENT -- -const inputHandler = () => { - // determine maximum number of characters - const maxNrChars = MAX_CHARS; - - // determine number of characters currently typed - const nrCharsTyped = textareaEl.value.length; - - // calculate number of characters left (maximum minus currently typed) - const charsLeft = maxNrChars - nrCharsTyped; - - // show number of characters left - counterEl.textContent = charsLeft; -}; - -textareaEl.addEventListener('input', inputHandler); - - -// -- FORM COMPONENT -- -const showVisualIndicator = textCheck => { - const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; - - // show valid indicator - formEl.classList.add(className); - - // remove visual indicator - setTimeout(() => { - formEl.classList.remove(className); - }, 2000); -}; - -const submitHandler = event => { - // prevent default browser action (submitting form data to 'action'-address and refreshing page) - event.preventDefault(); - - // get text from textarea - const text = textareaEl.value; - - // validate text (e.g. check if #hashtag is present and text is long enough) - if (text.includes('#') && text.length >= 5) { - showVisualIndicator('valid'); - } else { - showVisualIndicator('invalid'); - - // focus textarea - textareaEl.focus(); - - // stop this function execution - return; - } - - // we have text, now extract other info from text - const hashtag = text.split(' ').find(word => word.includes('#')); - const company = hashtag.substring(1); - const badgeLetter = company.substring(0, 1).toUpperCase(); - const upvoteCount = 0; - const daysAgo = 0; - - // new feedback item HTML - const feedbackItemHTML = ` - - `; - - // insert new feedback item in list - feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); - - // clear textarea - textareaEl.value = ''; - - // blur submit button - submitBtnEl.blur(); - - // reset counter - counterEl.textContent = MAX_CHARS; -}; - -formEl.addEventListener('submit', submitHandler); - - -// -- FEEDBACK LIST COMPONENT -- -fetch('https://bytegrad.com/course-assets/js/1/api/feedbacks') - .then(response => response.json()) - .then(data => { - // remove spinner - spinnerEl.remove(); - - // iterate over each element in feedbacks array and render it in list - data.feedbacks.forEach(feedbackItem => { - // new feedback item HTML - const feedbackItemHTML = ` - - `; - - // insert new feedback item in list - feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); - }); - }) - .catch(error => { - feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; - }); diff --git a/06 - CorpComment/Feedback List Component #1/style.css b/06 - CorpComment/Feedback List Component #1/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/06 - CorpComment/Feedback List Component #1/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/Feedback List Component #2/index.html b/06 - CorpComment/Feedback List Component #2/index.html deleted file mode 100644 index b4a3c9b..0000000 --- a/06 - CorpComment/Feedback List Component #2/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      -
      -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/Feedback List Component #2/script.js b/06 - CorpComment/Feedback List Component #2/script.js deleted file mode 100644 index 83d0ecf..0000000 --- a/06 - CorpComment/Feedback List Component #2/script.js +++ /dev/null @@ -1,177 +0,0 @@ -// -- GLOBAL -- -const MAX_CHARS = 150; -const BASE_API_URL = 'https://bytegrad.com/course-assets/js/1/api'; - -const textareaEl = document.querySelector('.form__textarea'); -const counterEl = document.querySelector('.counter'); -const formEl = document.querySelector('.form'); -const feedbackListEl = document.querySelector('.feedbacks'); -const submitBtnEl = document.querySelector('.submit-btn'); -const spinnerEl = document.querySelector('.spinner'); - -const renderFeedbackItem = feedbackItem => { - // new feedback item HTML - const feedbackItemHTML = ` - - `; - - // insert new feedback item in list - feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); -}; - - -// -- COUNTER COMPONENT -- -const inputHandler = () => { - // determine maximum number of characters - const maxNrChars = MAX_CHARS; - - // determine number of characters currently typed - const nrCharsTyped = textareaEl.value.length; - - // calculate number of characters left (maximum minus currently typed) - const charsLeft = maxNrChars - nrCharsTyped; - - // show number of characters left - counterEl.textContent = charsLeft; -}; - -textareaEl.addEventListener('input', inputHandler); - - -// -- FORM COMPONENT -- -const showVisualIndicator = textCheck => { - const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; - - // show valid indicator - formEl.classList.add(className); - - // remove visual indicator - setTimeout(() => { - formEl.classList.remove(className); - }, 2000); -}; - -const submitHandler = event => { - // prevent default browser action (submitting form data to 'action'-address and refreshing page) - event.preventDefault(); - - // get text from textarea - const text = textareaEl.value; - - // validate text (e.g. check if #hashtag is present and text is long enough) - if (text.includes('#') && text.length >= 5) { - showVisualIndicator('valid'); - } else { - showVisualIndicator('invalid'); - - // focus textarea - textareaEl.focus(); - - // stop this function execution - return; - } - - // we have text, now extract other info from text - const hashtag = text.split(' ').find(word => word.includes('#')); - const company = hashtag.substring(1); - const badgeLetter = company.substring(0, 1).toUpperCase(); - const upvoteCount = 0; - const daysAgo = 0; - - // render feedback item in list - const feedbackItem = { - upvoteCount: upvoteCount, - company: company, - badgeLetter: badgeLetter, - daysAgo: daysAgo, - text: text - }; - renderFeedbackItem(feedbackItem); - - // send feedback item to server - fetch(`${BASE_API_URL}/feedbacks`, { - method: 'POST', - body: JSON.stringify(feedbackItem), - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - } - }).then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - console.log('Successfully submitted'); - }).catch(error => console.log(error)); - - // clear textarea - textareaEl.value = ''; - - // blur submit button - submitBtnEl.blur(); - - // reset counter - counterEl.textContent = MAX_CHARS; -}; - -formEl.addEventListener('submit', submitHandler); - - -// -- FEEDBACK LIST COMPONENT -- -const clickHandler = event => { - // get clicked HTML-element - const clickedEl = event.target; - - // determine if user intended to upvote or expand - const upvoteIntention = clickedEl.className.includes('upvote'); - - // run the appropriate logic - if (upvoteIntention) { - // get the closest upvote button - const upvoteBtnEl = clickedEl.closest('.upvote'); - - // disable upvote button (prevent double-clicks, spam) - upvoteBtnEl.disabled = true; - - // select the upvote count element within the upvote button - const upvoteCountEl = upvoteBtnEl.querySelector('.upvote__count'); - - // get currently displayed upvote count as number (+) - let upvoteCount = +upvoteCountEl.textContent; - - // set upvote count incremented by 1 - upvoteCountEl.textContent = ++upvoteCount; - } else { - // expand the clicked feedback item - clickedEl.closest('.feedback').classList.toggle('feedback--expand'); - } -}; - -feedbackListEl.addEventListener('click', clickHandler); - -fetch(`${BASE_API_URL}/feedbacks`) - .then(response => response.json()) - .then(data => { - // remove spinner - spinnerEl.remove(); - - // iterate over each element in feedbacks array and render it in list - data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); - }) - .catch(error => { - feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; - }); diff --git a/06 - CorpComment/Feedback List Component #2/style.css b/06 - CorpComment/Feedback List Component #2/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/06 - CorpComment/Feedback List Component #2/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/Final Loose Ends/index.html b/06 - CorpComment/Final Loose Ends/index.html deleted file mode 100644 index b4a3c9b..0000000 --- a/06 - CorpComment/Final Loose Ends/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      -
      -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/Final Loose Ends/script.js b/06 - CorpComment/Final Loose Ends/script.js deleted file mode 100644 index 809fa3e..0000000 --- a/06 - CorpComment/Final Loose Ends/script.js +++ /dev/null @@ -1,215 +0,0 @@ -// -- GLOBAL -- -const MAX_CHARS = 150; -const BASE_API_URL = 'https://bytegrad.com/course-assets/js/1/api'; - -const textareaEl = document.querySelector('.form__textarea'); -const counterEl = document.querySelector('.counter'); -const formEl = document.querySelector('.form'); -const feedbackListEl = document.querySelector('.feedbacks'); -const submitBtnEl = document.querySelector('.submit-btn'); -const spinnerEl = document.querySelector('.spinner'); -const hashtagListEl = document.querySelector('.hashtags'); - -const renderFeedbackItem = feedbackItem => { - // new feedback item HTML - const feedbackItemHTML = ` - - `; - - // insert new feedback item in list - feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); -}; - - -// -- COUNTER COMPONENT -- -(() => { - const inputHandler = () => { - // determine maximum number of characters - const maxNrChars = MAX_CHARS; - - // determine number of characters currently typed - const nrCharsTyped = textareaEl.value.length; - - // calculate number of characters left (maximum minus currently typed) - const charsLeft = maxNrChars - nrCharsTyped; - - // show number of characters left - counterEl.textContent = charsLeft; - }; - - textareaEl.addEventListener('input', inputHandler); -})(); - - -// -- FORM COMPONENT -- -(() => { - const showVisualIndicator = textCheck => { - const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; - - // show valid indicator - formEl.classList.add(className); - - // remove visual indicator - setTimeout(() => { - formEl.classList.remove(className); - }, 2000); - }; - - const submitHandler = event => { - // prevent default browser action (submitting form data to 'action'-address and refreshing page) - event.preventDefault(); - - // get text from textarea - const text = textareaEl.value; - - // validate text (e.g. check if #hashtag is present and text is long enough) - if (text.includes('#') && text.length >= 5) { - showVisualIndicator('valid'); - } else { - showVisualIndicator('invalid'); - - // focus textarea - textareaEl.focus(); - - // stop this function execution - return; - } - - // we have text, now extract other info from text - const hashtag = text.split(' ').find(word => word.includes('#')); - const company = hashtag.substring(1); - const badgeLetter = company.substring(0, 1).toUpperCase(); - const upvoteCount = 0; - const daysAgo = 0; - - // render feedback item in list - const feedbackItem = { - upvoteCount: upvoteCount, - company: company, - badgeLetter: badgeLetter, - daysAgo: daysAgo, - text: text - }; - renderFeedbackItem(feedbackItem); - - // send feedback item to server - fetch(`${BASE_API_URL}/feedbacks`, { - method: 'POST', - body: JSON.stringify(feedbackItem), - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - } - }).then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - console.log('Successfully submitted'); - }).catch(error => console.log(error)); - - // clear textarea - textareaEl.value = ''; - - // blur submit button - submitBtnEl.blur(); - - // reset counter - counterEl.textContent = MAX_CHARS; - }; - - formEl.addEventListener('submit', submitHandler); -})(); - - -// -- FEEDBACK LIST COMPONENT -- -(() => { - const clickHandler = event => { - // get clicked HTML-element - const clickedEl = event.target; - - // determine if user intended to upvote or expand - const upvoteIntention = clickedEl.className.includes('upvote'); - - // run the appropriate logic - if (upvoteIntention) { - // get the closest upvote button - const upvoteBtnEl = clickedEl.closest('.upvote'); - - // disable upvote button (prevent double-clicks, spam) - upvoteBtnEl.disabled = true; - - // select the upvote count element within the upvote button - const upvoteCountEl = upvoteBtnEl.querySelector('.upvote__count'); - - // get currently displayed upvote count as number (+) - let upvoteCount = +upvoteCountEl.textContent; - - // set upvote count incremented by 1 - upvoteCountEl.textContent = ++upvoteCount; - } else { - // expand the clicked feedback item - clickedEl.closest('.feedback').classList.toggle('feedback--expand'); - } - }; - - feedbackListEl.addEventListener('click', clickHandler); - - fetch(`${BASE_API_URL}/feedbacks`) - .then(response => response.json()) - .then(data => { - // remove spinner - spinnerEl.remove(); - - // iterate over each element in feedbacks array and render it in list - data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); - }) - .catch(error => { - feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; - }); -})(); - - -// -- HASHTAG LIST COMPONENT -- -(() => { - const clickHandler = event => { - // get the clicked element - const clickedEl = event.target; - - // stop function if click happened in list, but outside buttons - if (clickedEl.className === 'hashtags') return; - - // extract company name - const companyNameFromHashtag = clickedEl.textContent.substring(1).toLowerCase().trim(); - - // iterate over each feedback item in the list - feedbackListEl.childNodes.forEach(childNode => { - // stop this iteration if it's a text node - if (childNode.nodeType === 3) return; - - // extract company name - const companyNameFromFeedbackItem = childNode.querySelector('.feedback__company').textContent.toLowerCase().trim(); - - // remove feedback item from list if company names are not equal - if (companyNameFromHashtag !== companyNameFromFeedbackItem) { - childNode.remove(); - } - }); - }; - - hashtagListEl.addEventListener('click', clickHandler); -})(); diff --git a/06 - CorpComment/Final Loose Ends/style.css b/06 - CorpComment/Final Loose Ends/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/06 - CorpComment/Final Loose Ends/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/Form (Submit) Component/index.html b/06 - CorpComment/Form (Submit) Component/index.html deleted file mode 100644 index b4a3c9b..0000000 --- a/06 - CorpComment/Form (Submit) Component/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      -
      -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/Form (Submit) Component/script.js b/06 - CorpComment/Form (Submit) Component/script.js deleted file mode 100644 index 3d1bf44..0000000 --- a/06 - CorpComment/Form (Submit) Component/script.js +++ /dev/null @@ -1,146 +0,0 @@ -// -- GLOBAL -- -const MAX_CHARS = 150; -const BASE_API_URL = 'https://bytegrad.com/course-assets/js/1/api'; - -const textareaEl = document.querySelector('.form__textarea'); -const counterEl = document.querySelector('.counter'); -const formEl = document.querySelector('.form'); -const feedbackListEl = document.querySelector('.feedbacks'); -const submitBtnEl = document.querySelector('.submit-btn'); -const spinnerEl = document.querySelector('.spinner'); - -const renderFeedbackItem = feedbackItem => { - // new feedback item HTML - const feedbackItemHTML = ` - - `; - - // insert new feedback item in list - feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); -}; - - -// -- COUNTER COMPONENT -- -const inputHandler = () => { - // determine maximum number of characters - const maxNrChars = MAX_CHARS; - - // determine number of characters currently typed - const nrCharsTyped = textareaEl.value.length; - - // calculate number of characters left (maximum minus currently typed) - const charsLeft = maxNrChars - nrCharsTyped; - - // show number of characters left - counterEl.textContent = charsLeft; -}; - -textareaEl.addEventListener('input', inputHandler); - - -// -- FORM COMPONENT -- -const showVisualIndicator = textCheck => { - const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; - - // show valid indicator - formEl.classList.add(className); - - // remove visual indicator - setTimeout(() => { - formEl.classList.remove(className); - }, 2000); -}; - -const submitHandler = event => { - // prevent default browser action (submitting form data to 'action'-address and refreshing page) - event.preventDefault(); - - // get text from textarea - const text = textareaEl.value; - - // validate text (e.g. check if #hashtag is present and text is long enough) - if (text.includes('#') && text.length >= 5) { - showVisualIndicator('valid'); - } else { - showVisualIndicator('invalid'); - - // focus textarea - textareaEl.focus(); - - // stop this function execution - return; - } - - // we have text, now extract other info from text - const hashtag = text.split(' ').find(word => word.includes('#')); - const company = hashtag.substring(1); - const badgeLetter = company.substring(0, 1).toUpperCase(); - const upvoteCount = 0; - const daysAgo = 0; - - // render feedback item in list - const feedbackItem = { - upvoteCount: upvoteCount, - company: company, - badgeLetter: badgeLetter, - daysAgo: daysAgo, - text: text - }; - renderFeedbackItem(feedbackItem); - - // send feedback item to server - fetch(`${BASE_API_URL}/feedbacks`, { - method: 'POST', - body: JSON.stringify(feedbackItem), - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - } - }).then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - console.log('Successfully submitted'); - }).catch(error => console.log(error)); - - // clear textarea - textareaEl.value = ''; - - // blur submit button - submitBtnEl.blur(); - - // reset counter - counterEl.textContent = MAX_CHARS; -}; - -formEl.addEventListener('submit', submitHandler); - - -// -- FEEDBACK LIST COMPONENT -- -fetch(`${BASE_API_URL}/feedbacks`) - .then(response => response.json()) - .then(data => { - // remove spinner - spinnerEl.remove(); - - // iterate over each element in feedbacks array and render it in list - data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); - }) - .catch(error => { - feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; - }); diff --git a/06 - CorpComment/Form (Submit) Component/style.css b/06 - CorpComment/Form (Submit) Component/style.css deleted file mode 100644 index 5746913..0000000 --- a/06 - CorpComment/Form (Submit) Component/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - margin-bottom: 11px; - max-width: max-content; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/Hashtag List Component/index.html b/06 - CorpComment/Hashtag List Component/index.html deleted file mode 100644 index b4a3c9b..0000000 --- a/06 - CorpComment/Hashtag List Component/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      -
      -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/Hashtag List Component/script.js b/06 - CorpComment/Hashtag List Component/script.js deleted file mode 100644 index 4436179..0000000 --- a/06 - CorpComment/Hashtag List Component/script.js +++ /dev/null @@ -1,207 +0,0 @@ -// -- GLOBAL -- -const MAX_CHARS = 150; -const BASE_API_URL = 'https://bytegrad.com/course-assets/js/1/api'; - -const textareaEl = document.querySelector('.form__textarea'); -const counterEl = document.querySelector('.counter'); -const formEl = document.querySelector('.form'); -const feedbackListEl = document.querySelector('.feedbacks'); -const submitBtnEl = document.querySelector('.submit-btn'); -const spinnerEl = document.querySelector('.spinner'); -const hashtagListEl = document.querySelector('.hashtags'); - -const renderFeedbackItem = feedbackItem => { - // new feedback item HTML - const feedbackItemHTML = ` - - `; - - // insert new feedback item in list - feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); -}; - - -// -- COUNTER COMPONENT -- -const inputHandler = () => { - // determine maximum number of characters - const maxNrChars = MAX_CHARS; - - // determine number of characters currently typed - const nrCharsTyped = textareaEl.value.length; - - // calculate number of characters left (maximum minus currently typed) - const charsLeft = maxNrChars - nrCharsTyped; - - // show number of characters left - counterEl.textContent = charsLeft; -}; - -textareaEl.addEventListener('input', inputHandler); - - -// -- FORM COMPONENT -- -const showVisualIndicator = textCheck => { - const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; - - // show valid indicator - formEl.classList.add(className); - - // remove visual indicator - setTimeout(() => { - formEl.classList.remove(className); - }, 2000); -}; - -const submitHandler = event => { - // prevent default browser action (submitting form data to 'action'-address and refreshing page) - event.preventDefault(); - - // get text from textarea - const text = textareaEl.value; - - // validate text (e.g. check if #hashtag is present and text is long enough) - if (text.includes('#') && text.length >= 5) { - showVisualIndicator('valid'); - } else { - showVisualIndicator('invalid'); - - // focus textarea - textareaEl.focus(); - - // stop this function execution - return; - } - - // we have text, now extract other info from text - const hashtag = text.split(' ').find(word => word.includes('#')); - const company = hashtag.substring(1); - const badgeLetter = company.substring(0, 1).toUpperCase(); - const upvoteCount = 0; - const daysAgo = 0; - - // render feedback item in list - const feedbackItem = { - upvoteCount: upvoteCount, - company: company, - badgeLetter: badgeLetter, - daysAgo: daysAgo, - text: text - }; - renderFeedbackItem(feedbackItem); - - // send feedback item to server - fetch(`${BASE_API_URL}/feedbacks`, { - method: 'POST', - body: JSON.stringify(feedbackItem), - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - } - }).then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - console.log('Successfully submitted'); - }).catch(error => console.log(error)); - - // clear textarea - textareaEl.value = ''; - - // blur submit button - submitBtnEl.blur(); - - // reset counter - counterEl.textContent = MAX_CHARS; -}; - -formEl.addEventListener('submit', submitHandler); - - -// -- FEEDBACK LIST COMPONENT -- -const clickHandler = event => { - // get clicked HTML-element - const clickedEl = event.target; - - // determine if user intended to upvote or expand - const upvoteIntention = clickedEl.className.includes('upvote'); - - // run the appropriate logic - if (upvoteIntention) { - // get the closest upvote button - const upvoteBtnEl = clickedEl.closest('.upvote'); - - // disable upvote button (prevent double-clicks, spam) - upvoteBtnEl.disabled = true; - - // select the upvote count element within the upvote button - const upvoteCountEl = upvoteBtnEl.querySelector('.upvote__count'); - - // get currently displayed upvote count as number (+) - let upvoteCount = +upvoteCountEl.textContent; - - // set upvote count incremented by 1 - upvoteCountEl.textContent = ++upvoteCount; - } else { - // expand the clicked feedback item - clickedEl.closest('.feedback').classList.toggle('feedback--expand'); - } -}; - -feedbackListEl.addEventListener('click', clickHandler); - -fetch(`${BASE_API_URL}/feedbacks`) - .then(response => response.json()) - .then(data => { - // remove spinner - spinnerEl.remove(); - - // iterate over each element in feedbacks array and render it in list - data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); - }) - .catch(error => { - feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; - }); - - -// -- HASHTAG LIST COMPONENT -- -const clickHandler2 = event => { - // get the clicked element - const clickedEl = event.target; - - // stop function if click happened in list, but outside buttons - if (clickedEl.className === 'hashtags') return; - - // extract company name - const companyNameFromHashtag = clickedEl.textContent.substring(1).toLowerCase().trim(); - - // iterate over each feedback item in the list - feedbackListEl.childNodes.forEach(childNode => { - // stop this iteration if it's a text node - if (childNode.nodeType === 3) return; - - // extract company name - const companyNameFromFeedbackItem = childNode.querySelector('.feedback__company').textContent.toLowerCase().trim(); - - // remove feedback item from list if company names are not equal - if (companyNameFromHashtag !== companyNameFromFeedbackItem) { - childNode.remove(); - } - }); -}; - -hashtagListEl.addEventListener('click', clickHandler2); \ No newline at end of file diff --git a/06 - CorpComment/Hashtag List Component/style.css b/06 - CorpComment/Hashtag List Component/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/06 - CorpComment/Hashtag List Component/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/More Refactoring/index.html b/06 - CorpComment/More Refactoring/index.html deleted file mode 100644 index b4a3c9b..0000000 --- a/06 - CorpComment/More Refactoring/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      -
      -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/More Refactoring/script.js b/06 - CorpComment/More Refactoring/script.js deleted file mode 100644 index 8a2320b..0000000 --- a/06 - CorpComment/More Refactoring/script.js +++ /dev/null @@ -1,130 +0,0 @@ -// -- GLOBAL -- -const MAX_CHARS = 150; - -const textareaEl = document.querySelector('.form__textarea'); -const counterEl = document.querySelector('.counter'); -const formEl = document.querySelector('.form'); -const feedbackListEl = document.querySelector('.feedbacks'); -const submitBtnEl = document.querySelector('.submit-btn'); -const spinnerEl = document.querySelector('.spinner'); - -const renderFeedbackItem = feedbackItem => { - // new feedback item HTML - const feedbackItemHTML = ` - - `; - - // insert new feedback item in list - feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); -}; - - -// -- COUNTER COMPONENT -- -const inputHandler = () => { - // determine maximum number of characters - const maxNrChars = MAX_CHARS; - - // determine number of characters currently typed - const nrCharsTyped = textareaEl.value.length; - - // calculate number of characters left (maximum minus currently typed) - const charsLeft = maxNrChars - nrCharsTyped; - - // show number of characters left - counterEl.textContent = charsLeft; -}; - -textareaEl.addEventListener('input', inputHandler); - - -// -- FORM COMPONENT -- -const showVisualIndicator = textCheck => { - const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; - - // show valid indicator - formEl.classList.add(className); - - // remove visual indicator - setTimeout(() => { - formEl.classList.remove(className); - }, 2000); -}; - -const submitHandler = event => { - // prevent default browser action (submitting form data to 'action'-address and refreshing page) - event.preventDefault(); - - // get text from textarea - const text = textareaEl.value; - - // validate text (e.g. check if #hashtag is present and text is long enough) - if (text.includes('#') && text.length >= 5) { - showVisualIndicator('valid'); - } else { - showVisualIndicator('invalid'); - - // focus textarea - textareaEl.focus(); - - // stop this function execution - return; - } - - // we have text, now extract other info from text - const hashtag = text.split(' ').find(word => word.includes('#')); - const company = hashtag.substring(1); - const badgeLetter = company.substring(0, 1).toUpperCase(); - const upvoteCount = 0; - const daysAgo = 0; - - // create feedback item object - const feedbackItem = { - upvoteCount: upvoteCount, - company: company, - badgeLetter: badgeLetter, - daysAgo: daysAgo, - text: text - }; - - // render feedback item - renderFeedbackItem(feedbackItem); - - // clear textarea - textareaEl.value = ''; - - // blur submit button - submitBtnEl.blur(); - - // reset counter - counterEl.textContent = MAX_CHARS; -}; - -formEl.addEventListener('submit', submitHandler); - - -// -- FEEDBACK LIST COMPONENT -- -fetch('https://bytegrad.com/course-assets/js/1/api/feedbacks') - .then(response => response.json()) - .then(data => { - // remove spinner - spinnerEl.remove(); - - // iterate over each element in feedbacks array and render it in list - data.feedbacks.forEach(feedbackItem => renderFeedbackItem(feedbackItem)); - }) - .catch(error => { - feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; - }); diff --git a/06 - CorpComment/More Refactoring/style.css b/06 - CorpComment/More Refactoring/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/06 - CorpComment/More Refactoring/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/Project Setup/index.html b/06 - CorpComment/Project Setup/index.html deleted file mode 100644 index a7a9d00..0000000 --- a/06 - CorpComment/Project Setup/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      - -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/Project Setup/script.js b/06 - CorpComment/Project Setup/script.js deleted file mode 100644 index e69de29..0000000 diff --git a/06 - CorpComment/Project Setup/style.css b/06 - CorpComment/Project Setup/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/06 - CorpComment/Project Setup/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/Refactoring/index.html b/06 - CorpComment/Refactoring/index.html deleted file mode 100644 index a7a9d00..0000000 --- a/06 - CorpComment/Refactoring/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      - -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/Refactoring/script.js b/06 - CorpComment/Refactoring/script.js deleted file mode 100644 index 1ca56ec..0000000 --- a/06 - CorpComment/Refactoring/script.js +++ /dev/null @@ -1,100 +0,0 @@ -// -- GLOBAL -- -const MAX_CHARS = 150; - -const textareaEl = document.querySelector('.form__textarea'); -const counterEl = document.querySelector('.counter'); -const formEl = document.querySelector('.form'); -const feedbackListEl = document.querySelector('.feedbacks'); -const submitBtnEl = document.querySelector('.submit-btn'); - - -// -- COUNTER COMPONENT -- -const inputHandler = () => { - // determine maximum number of characters - const maxNrChars = MAX_CHARS; - - // determine number of characters currently typed - const nrCharsTyped = textareaEl.value.length; - - // calculate number of characters left (maximum minus currently typed) - const charsLeft = maxNrChars - nrCharsTyped; - - // show number of characters left - counterEl.textContent = charsLeft; -}; - -textareaEl.addEventListener('input', inputHandler); - - -// -- FORM COMPONENT -- -const showVisualIndicator = textCheck => { - const className = textCheck === 'valid' ? 'form--valid' : 'form--invalid'; - - // show valid indicator - formEl.classList.add(className); - - // remove visual indicator - setTimeout(() => { - formEl.classList.remove(className); - }, 2000); -}; - -const submitHandler = event => { - // prevent default browser action (submitting form data to 'action'-address and refreshing page) - event.preventDefault(); - - // get text from textarea - const text = textareaEl.value; - - // validate text (e.g. check if #hashtag is present and text is long enough) - if (text.includes('#') && text.length >= 5) { - showVisualIndicator('valid'); - } else { - showVisualIndicator('invalid'); - - // focus textarea - textareaEl.focus(); - - // stop this function execution - return; - } - - // we have text, now extract other info from text - const hashtag = text.split(' ').find(word => word.includes('#')); - const company = hashtag.substring(1); - const badgeLetter = company.substring(0, 1).toUpperCase(); - const upvoteCount = 0; - const daysAgo = 0; - - // new feedback item HTML - const feedbackItemHTML = ` - - `; - - // insert new feedback item in list - feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); - - // clear textarea - textareaEl.value = ''; - - // blur submit button - submitBtnEl.blur(); - - // reset counter - counterEl.textContent = MAX_CHARS; -}; - -formEl.addEventListener('submit', submitHandler); diff --git a/06 - CorpComment/Refactoring/style.css b/06 - CorpComment/Refactoring/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/06 - CorpComment/Refactoring/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/Submit Component/index.html b/06 - CorpComment/Submit Component/index.html deleted file mode 100644 index a7a9d00..0000000 --- a/06 - CorpComment/Submit Component/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      - -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/Submit Component/script.js b/06 - CorpComment/Submit Component/script.js deleted file mode 100644 index 40454ab..0000000 --- a/06 - CorpComment/Submit Component/script.js +++ /dev/null @@ -1,98 +0,0 @@ -// -- GLOBAL -- -const textareaEl = document.querySelector('.form__textarea'); -const counterEl = document.querySelector('.counter'); -const formEl = document.querySelector('.form'); -const feedbackListEl = document.querySelector('.feedbacks'); -const submitBtnEl = document.querySelector('.submit-btn'); - - -// -- COUNTER COMPONENT -- -const inputHandler = () => { - // determine maximum number of characters - const maxNrChars = 150; - - // determine number of characters currently typed - const nrCharsTyped = textareaEl.value.length; - - // calculate number of characters left (maximum minus currently typed) - const charsLeft = maxNrChars - nrCharsTyped; - - // show number of characters left - counterEl.textContent = charsLeft; -}; - -textareaEl.addEventListener('input', inputHandler); - - -// -- SUBMIT COMPONENT -- -const submitHandler = event => { - // prevent default browser action (submitting form data to 'action'-address and refreshing page) - event.preventDefault(); - - // get text from textarea - const text = textareaEl.value; - - // validate text (e.g. check if #hashtag is present and text is long enough) - if (text.includes('#') && text.length >= 5) { - // show valid indicator - formEl.classList.add('form--valid'); - - // remove visual indicator - setTimeout(() => { - formEl.classList.remove('form--valid'); - }, 2000); - } else { - // show invalid indicator - formEl.classList.add('form--invalid'); - - // remove visual indicator - setTimeout(() => { - formEl.classList.remove('form--invalid'); - }, 2000); - - // focus textarea - textareaEl.focus(); - - // stop this function execution - return; - } - - // we have text, now extract other info from text - const hashtag = text.split(' ').find(word => word.includes('#')); - const company = hashtag.substring(1); - const badgeLetter = company.substring(0, 1).toUpperCase(); - const upvoteCount = 0; - const daysAgo = 0; - - // new feedback item HTML - const feedbackItemHTML = ` - - `; - - // insert new feedback item in list - feedbackListEl.insertAdjacentHTML('beforeend', feedbackItemHTML); - - // clear textarea - textareaEl.value = ''; - - // blur submit button - submitBtnEl.blur(); - - // reset counter - counterEl.textContent = '150'; -}; - -formEl.addEventListener('submit', submitHandler); diff --git a/06 - CorpComment/Submit Component/style.css b/06 - CorpComment/Submit Component/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/06 - CorpComment/Submit Component/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/VS Code Extension (Live-Server)/index.html b/06 - CorpComment/VS Code Extension (Live-Server)/index.html deleted file mode 100644 index 816df8b..0000000 --- a/06 - CorpComment/VS Code Extension (Live-Server)/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - afdasdf CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      - -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/06 - CorpComment/VS Code Extension (Live-Server)/script.js b/06 - CorpComment/VS Code Extension (Live-Server)/script.js deleted file mode 100644 index e69de29..0000000 diff --git a/06 - CorpComment/VS Code Extension (Live-Server)/style.css b/06 - CorpComment/VS Code Extension (Live-Server)/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/06 - CorpComment/VS Code Extension (Live-Server)/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/06 - CorpComment/resources.txt b/06 - CorpComment/resources.txt deleted file mode 100644 index f8c41e9..0000000 --- a/06 - CorpComment/resources.txt +++ /dev/null @@ -1,20 +0,0 @@ -####### Feedback Item HTML Template - - - -####### fetch URL - -https://bytegrad.com/course-assets/js/1/api/feedbacks \ No newline at end of file diff --git a/08 - rmtDev/Active Job Item/index.css b/08 - rmtDev/Active Job Item/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Active Job Item/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Active Job Item/index.html b/08 - rmtDev/Active Job Item/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Active Job Item/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Active Job Item/index.js b/08 - rmtDev/Active Job Item/index.js deleted file mode 100644 index e88260f..0000000 --- a/08 - rmtDev/Active Job Item/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Pagination.js'; -import './src/components/Router.js'; -import './src/components/Search.js'; -import './src/components/Sorting.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Active Job Item/src/common.js b/08 - rmtDev/Active Job Item/src/common.js deleted file mode 100644 index 6015300..0000000 --- a/08 - rmtDev/Active Job Item/src/common.js +++ /dev/null @@ -1,45 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; -export const RESULTS_PER_PAGE = 7; - -// STATE -export const state = { - searchJobItems: [], - activeJobItem: {}, - currentPage: 1 -}; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// HELPER / UTILITY FUNCTIONS -export const getData = async completeURL => { - const response = await fetch(completeURL); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - return data; -}; \ No newline at end of file diff --git a/08 - rmtDev/Active Job Item/src/components/Error.js b/08 - rmtDev/Active Job Item/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Active Job Item/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Active Job Item/src/components/JobDetails.js b/08 - rmtDev/Active Job Item/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/Active Job Item/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Active Job Item/src/components/JobList.js b/08 - rmtDev/Active Job Item/src/components/JobList.js deleted file mode 100644 index 3cf5983..0000000 --- a/08 - rmtDev/Active Job Item/src/components/JobList.js +++ /dev/null @@ -1,91 +0,0 @@ -import { - BASE_API_URL, - RESULTS_PER_PAGE, - state, - jobListSearchEl, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = () => { - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // display job items - state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // update state - state.activeJobItem = state.searchJobItems.find(jobItem => jobItem.id === +id); - - // add id to url - history.pushState(null, '', `/#${id}`); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Active Job Item/src/components/Pagination.js b/08 - rmtDev/Active Job Item/src/components/Pagination.js deleted file mode 100644 index 720f1c7..0000000 --- a/08 - rmtDev/Active Job Item/src/components/Pagination.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - RESULTS_PER_PAGE, - state, - paginationEl, - paginationNumberNextEl, - paginationNumberBackEl, - paginationBtnNextEl, - paginationBtnBackEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const renderPaginationButtons = () => { - // display back button if we are on page 2 or further - if (state.currentPage >= 2) { - paginationBtnBackEl.classList.remove('pagination__button--hidden'); - } else { - paginationBtnBackEl.classList.add('pagination__button--hidden'); - } - - // display next button if there are more job items on next page - if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { - paginationBtnNextEl.classList.add('pagination__button--hidden'); - } else { - paginationBtnNextEl.classList.remove('pagination__button--hidden'); - } - - // update page numbers - paginationNumberNextEl.textContent = state.currentPage + 1; - paginationNumberBackEl.textContent = state.currentPage - 1; - - // unfocus ('blur') buttons - paginationBtnNextEl.blur(); - paginationBtnBackEl.blur(); -}; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.pagination__button'); - - // stop function if null - if (!clickedButtonEl) return; - - // check if intention is next or back - const nextPage = clickedButtonEl.className.includes('--next') ? true : false; - - // update state - nextPage ? state.currentPage++ : state.currentPage--; - - // render pagination buttons - renderPaginationButtons(); - - // render job items for that page - renderJobList(); -}; - -paginationEl.addEventListener('click', clickHandler); - -export default renderPaginationButtons; \ No newline at end of file diff --git a/08 - rmtDev/Active Job Item/src/components/Router.js b/08 - rmtDev/Active Job Item/src/components/Router.js deleted file mode 100644 index b1d0be8..0000000 --- a/08 - rmtDev/Active Job Item/src/components/Router.js +++ /dev/null @@ -1,45 +0,0 @@ -import { - BASE_API_URL, - state, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const loadHashChangeHandler = async () => { - // get id from url - const id = window.location.hash.substring(1); - - if (id) { - // remove previous job details content - jobDetailsContentEl.innerHTML = ''; - - // add spinner - renderSpinner('job-details'); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // update state - state.activeJobItem = jobItem; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } - } -}; - -window.addEventListener('DOMContentLoaded', loadHashChangeHandler); -window.addEventListener('hashchange', loadHashChangeHandler); \ No newline at end of file diff --git a/08 - rmtDev/Active Job Item/src/components/Search.js b/08 - rmtDev/Active Job Item/src/components/Search.js deleted file mode 100644 index e672606..0000000 --- a/08 - rmtDev/Active Job Item/src/components/Search.js +++ /dev/null @@ -1,73 +0,0 @@ -import { - BASE_API_URL, - state, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl, - sortingBtnRecentEl, - sortingBtnRelevantEl, - getData -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // reset sorting buttons - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - - // render spinner - renderSpinner('search'); - - try { - // fetch search results - const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); - - // extract job items - const { jobItems } = data; - - // update state - state.searchJobItems = jobItems; - state.currentPage = 1; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in search job list - renderJobList(); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Active Job Item/src/components/Sorting.js b/08 - rmtDev/Active Job Item/src/components/Sorting.js deleted file mode 100644 index cd08f17..0000000 --- a/08 - rmtDev/Active Job Item/src/components/Sorting.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - state, - sortingEl, - sortingBtnRecentEl, - sortingBtnRelevantEl -} from '../common.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.sorting__button'); - - // stop function if no clicked button element - if (!clickedButtonEl) return; - - // update state (reset to page 1) - state.currentPage = 1; - - // check if intention is recent or relevant sorting - const recent = clickedButtonEl.className.includes('--recent') ? true : false; - - // make sorting button look (in)active - if (recent) { - sortingBtnRecentEl.classList.add('sorting__button--active'); - sortingBtnRelevantEl.classList.remove('sorting__button--active'); - } else { - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - } - - // sort job items - // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same - if (recent) { - state.searchJobItems.sort((a, b) => { - return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. - }); - } else { - state.searchJobItems.sort((a, b) => { - return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. - }); - } - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in list - renderJobList(); -}; - -sortingEl.addEventListener('click', clickHandler); diff --git a/08 - rmtDev/Active Job Item/src/components/Spinner.js b/08 - rmtDev/Active Job Item/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Active Job Item/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Async Await/index.css b/08 - rmtDev/Async Await/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Async Await/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Async Await/index.html b/08 - rmtDev/Async Await/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Async Await/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Async Await/index.js b/08 - rmtDev/Async Await/index.js deleted file mode 100644 index 65ddab7..0000000 --- a/08 - rmtDev/Async Await/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Search.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Async Await/src/common.js b/08 - rmtDev/Async Await/src/common.js deleted file mode 100644 index 39a7caf..0000000 --- a/08 - rmtDev/Async Await/src/common.js +++ /dev/null @@ -1,25 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/08 - rmtDev/Async Await/src/components/Error.js b/08 - rmtDev/Async Await/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Async Await/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Async Await/src/components/JobDetails.js b/08 - rmtDev/Async Await/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/Async Await/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Async Await/src/components/JobList.js b/08 - rmtDev/Async Await/src/components/JobList.js deleted file mode 100644 index 221b7b2..0000000 --- a/08 - rmtDev/Async Await/src/components/JobList.js +++ /dev/null @@ -1,83 +0,0 @@ -import { - BASE_API_URL, - jobListSearchEl, - jobDetailsContentEl -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = jobItems => { - jobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // fetch job item data - try { - const response = await fetch(`${BASE_API_URL}/jobs/${id}`); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Async Await/src/components/Search.js b/08 - rmtDev/Async Await/src/components/Search.js deleted file mode 100644 index 7bb49b3..0000000 --- a/08 - rmtDev/Async Await/src/components/Search.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - BASE_API_URL, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - renderSpinner('search'); - - // fetch search results - try { - const response = await fetch(`${BASE_API_URL}/jobs?search=${searchText}`); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - // extract job items - const { jobItems } = data; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - renderJobList(jobItems); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Async Await/src/components/Spinner.js b/08 - rmtDev/Async Await/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Async Await/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/index.css b/08 - rmtDev/Bookmarks Component/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Bookmarks Component/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Bookmarks Component/index.html b/08 - rmtDev/Bookmarks Component/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Bookmarks Component/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/index.js b/08 - rmtDev/Bookmarks Component/index.js deleted file mode 100644 index fe6eede..0000000 --- a/08 - rmtDev/Bookmarks Component/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import './src/components/Bookmarks.js'; -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Pagination.js'; -import './src/components/Router.js'; -import './src/components/Search.js'; -import './src/components/Sorting.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/src/common.js b/08 - rmtDev/Bookmarks Component/src/common.js deleted file mode 100644 index 34e739a..0000000 --- a/08 - rmtDev/Bookmarks Component/src/common.js +++ /dev/null @@ -1,46 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; -export const RESULTS_PER_PAGE = 7; - -// STATE -export const state = { - searchJobItems: [], - bookmarkJobItems: [], - activeJobItem: {}, - currentPage: 1 -}; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// HELPER / UTILITY FUNCTIONS -export const getData = async completeURL => { - const response = await fetch(completeURL); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - return data; -}; \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/src/components/Bookmarks.js b/08 - rmtDev/Bookmarks Component/src/components/Bookmarks.js deleted file mode 100644 index 674ce47..0000000 --- a/08 - rmtDev/Bookmarks Component/src/components/Bookmarks.js +++ /dev/null @@ -1,45 +0,0 @@ -import { - state, - bookmarksBtnEl, - jobDetailsEl, - jobListBookmarksEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const clickHandler = event => { - // don't continue if click was outside bookmark button - if (!event.target.className.includes('bookmark')) return; - - // update state - if (state.bookmarkJobItems.some(bookmarkJobItem => bookmarkJobItem.id === state.activeJobItem.id)) { - state.bookmarkJobItems = state.bookmarkJobItems.filter(bookmarkJobItem => bookmarkJobItem.id !== state.activeJobItem.id); - } else { - state.bookmarkJobItems.push(state.activeJobItem); - } - - // update bookmark icon - document.querySelector('.job-info__bookmark-icon').classList.toggle('job-info__bookmark-icon--bookmarked'); -}; - -const mouseEnterHandler = () => { - // make bookmarks button look active - bookmarksBtnEl.classList.add('bookmarks-btn--active'); - - // make job list visible - jobListBookmarksEl.classList.add('job-list--visible'); - - // render bookmarks job list - renderJobList('bookmarks'); -}; - -const mouseLeaveHandler = () => { - // make bookmarks button look inactive - bookmarksBtnEl.classList.remove('bookmarks-btn--active'); - - // make job list invisible - jobListBookmarksEl.classList.remove('job-list--visible'); -}; - -jobDetailsEl.addEventListener('click', clickHandler); -bookmarksBtnEl.addEventListener('mouseenter', mouseEnterHandler); -jobListBookmarksEl.addEventListener('mouseleave', mouseLeaveHandler); \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/src/components/Error.js b/08 - rmtDev/Bookmarks Component/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Bookmarks Component/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/src/components/JobDetails.js b/08 - rmtDev/Bookmarks Component/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/Bookmarks Component/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/src/components/JobList.js b/08 - rmtDev/Bookmarks Component/src/components/JobList.js deleted file mode 100644 index 980e04c..0000000 --- a/08 - rmtDev/Bookmarks Component/src/components/JobList.js +++ /dev/null @@ -1,104 +0,0 @@ -import { - BASE_API_URL, - RESULTS_PER_PAGE, - state, - jobListSearchEl, - jobListBookmarksEl, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = (whichJobList = 'search') => { - // determine correct selector for job list (search results list or bookmarks list) - const jobListEl = whichJobList === 'search' ? jobListSearchEl : jobListBookmarksEl; - - // remove previous job items - jobListEl.innerHTML = ''; - - // determine the job items that should be rendered - let jobItems; - if (whichJobList === 'search') { - jobItems = state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE); - } else if (whichJobList === 'bookmarks') { - jobItems = state.bookmarkJobItems; - } - - // display job items - jobItems.forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job items - document.querySelectorAll('.job-item--active').forEach(jobItemWithActiveClass => jobItemWithActiveClass.classList.remove('job-item--active')); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // update state - state.activeJobItem = state.searchJobItems.find(jobItem => jobItem.id === +id); - - // add id to url - history.pushState(null, '', `/#${id}`); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); -jobListBookmarksEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/src/components/Pagination.js b/08 - rmtDev/Bookmarks Component/src/components/Pagination.js deleted file mode 100644 index 720f1c7..0000000 --- a/08 - rmtDev/Bookmarks Component/src/components/Pagination.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - RESULTS_PER_PAGE, - state, - paginationEl, - paginationNumberNextEl, - paginationNumberBackEl, - paginationBtnNextEl, - paginationBtnBackEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const renderPaginationButtons = () => { - // display back button if we are on page 2 or further - if (state.currentPage >= 2) { - paginationBtnBackEl.classList.remove('pagination__button--hidden'); - } else { - paginationBtnBackEl.classList.add('pagination__button--hidden'); - } - - // display next button if there are more job items on next page - if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { - paginationBtnNextEl.classList.add('pagination__button--hidden'); - } else { - paginationBtnNextEl.classList.remove('pagination__button--hidden'); - } - - // update page numbers - paginationNumberNextEl.textContent = state.currentPage + 1; - paginationNumberBackEl.textContent = state.currentPage - 1; - - // unfocus ('blur') buttons - paginationBtnNextEl.blur(); - paginationBtnBackEl.blur(); -}; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.pagination__button'); - - // stop function if null - if (!clickedButtonEl) return; - - // check if intention is next or back - const nextPage = clickedButtonEl.className.includes('--next') ? true : false; - - // update state - nextPage ? state.currentPage++ : state.currentPage--; - - // render pagination buttons - renderPaginationButtons(); - - // render job items for that page - renderJobList(); -}; - -paginationEl.addEventListener('click', clickHandler); - -export default renderPaginationButtons; \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/src/components/Router.js b/08 - rmtDev/Bookmarks Component/src/components/Router.js deleted file mode 100644 index b1d0be8..0000000 --- a/08 - rmtDev/Bookmarks Component/src/components/Router.js +++ /dev/null @@ -1,45 +0,0 @@ -import { - BASE_API_URL, - state, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const loadHashChangeHandler = async () => { - // get id from url - const id = window.location.hash.substring(1); - - if (id) { - // remove previous job details content - jobDetailsContentEl.innerHTML = ''; - - // add spinner - renderSpinner('job-details'); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // update state - state.activeJobItem = jobItem; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } - } -}; - -window.addEventListener('DOMContentLoaded', loadHashChangeHandler); -window.addEventListener('hashchange', loadHashChangeHandler); \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/src/components/Search.js b/08 - rmtDev/Bookmarks Component/src/components/Search.js deleted file mode 100644 index e672606..0000000 --- a/08 - rmtDev/Bookmarks Component/src/components/Search.js +++ /dev/null @@ -1,73 +0,0 @@ -import { - BASE_API_URL, - state, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl, - sortingBtnRecentEl, - sortingBtnRelevantEl, - getData -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // reset sorting buttons - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - - // render spinner - renderSpinner('search'); - - try { - // fetch search results - const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); - - // extract job items - const { jobItems } = data; - - // update state - state.searchJobItems = jobItems; - state.currentPage = 1; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in search job list - renderJobList(); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Bookmarks Component/src/components/Sorting.js b/08 - rmtDev/Bookmarks Component/src/components/Sorting.js deleted file mode 100644 index cd08f17..0000000 --- a/08 - rmtDev/Bookmarks Component/src/components/Sorting.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - state, - sortingEl, - sortingBtnRecentEl, - sortingBtnRelevantEl -} from '../common.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.sorting__button'); - - // stop function if no clicked button element - if (!clickedButtonEl) return; - - // update state (reset to page 1) - state.currentPage = 1; - - // check if intention is recent or relevant sorting - const recent = clickedButtonEl.className.includes('--recent') ? true : false; - - // make sorting button look (in)active - if (recent) { - sortingBtnRecentEl.classList.add('sorting__button--active'); - sortingBtnRelevantEl.classList.remove('sorting__button--active'); - } else { - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - } - - // sort job items - // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same - if (recent) { - state.searchJobItems.sort((a, b) => { - return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. - }); - } else { - state.searchJobItems.sort((a, b) => { - return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. - }); - } - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in list - renderJobList(); -}; - -sortingEl.addEventListener('click', clickHandler); diff --git a/08 - rmtDev/Bookmarks Component/src/components/Spinner.js b/08 - rmtDev/Bookmarks Component/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Bookmarks Component/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Build Process (webpack)/babel.config.js b/08 - rmtDev/Build Process (webpack)/babel.config.js deleted file mode 100644 index 9c22988..0000000 --- a/08 - rmtDev/Build Process (webpack)/babel.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - presets: [ - [ - '@babel/preset-env', - { - useBuiltIns: 'usage', - corejs: '3.0' - } - ] - ] -}; \ No newline at end of file diff --git a/08 - rmtDev/Build Process (webpack)/dist/index.html b/08 - rmtDev/Build Process (webpack)/dist/index.html deleted file mode 100644 index 217a364..0000000 --- a/08 - rmtDev/Build Process (webpack)/dist/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Build Process (webpack)/dist/main.css b/08 - rmtDev/Build Process (webpack)/dist/main.css deleted file mode 100644 index 56d663d..0000000 --- a/08 - rmtDev/Build Process (webpack)/dist/main.css +++ /dev/null @@ -1 +0,0 @@ -*,:after,:before{box-sizing:border-box;margin:0;padding:0}ol,ul{list-style:none}a{color:inherit;text-decoration:initial}button{background-color:initial}button,input{border:initial;font:inherit;outline:initial}.u-bold{font-weight:700}@-webkit-keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@-webkit-keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}@keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}.body{background-color:#dee3e9;color:#16181c;font-family:Inter,sans-serif;min-height:100vh;position:relative;scrollbar-width:none}.body::-webkit-scrollbar{display:none}.first-heading{color:#fff;display:none;font-size:1.9375rem;font-size:1.6875rem;font-weight:400}.second-heading{color:#fff;font-size:1.4375rem;font-weight:500}.third-heading{font-size:.8125rem;font-weight:600}.fourth-heading{font-size:1rem;font-weight:600;text-transform:capitalize}.background{background-image:linear-gradient(125deg,#1f74f1 -10%,#0850b9);box-shadow:0 .125rem .1875rem rgba(0,0,0,.1);height:13.125rem;left:0;overflow:hidden;position:absolute;top:0;width:100%;z-index:-2}.background:before{background-image:linear-gradient(-180deg,rgba(0,0,0,.025),rgba(0,0,0,.075) 99%);bottom:0;content:"";left:0;position:absolute;right:0;top:0}.background__pattern{left:0;position:absolute;top:-1.5625rem;-webkit-transform:scale(1.1);transform:scale(1.1);-webkit-user-select:none;-ms-user-select:none;user-select:none;z-index:-1}.header{margin-bottom:0;position:relative}.header__top{align-items:center;-webkit-animation:intro .3s;animation:intro .3s;display:flex;justify-content:center;margin:0 auto;max-width:62.5rem;padding:2.5rem .75rem 0;position:relative}.header__submit-job{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.75rem;margin-right:0;padding-left:.625rem;text-transform:lowercase;-webkit-transform:translateY(-.125rem);transform:translateY(-.125rem)}.header__submit-job:before{background-color:hsla(0,0%,100%,.25);content:"";display:inline-block;height:.8125rem;margin-right:.5rem;-webkit-transform:translateY(.1875rem);transform:translateY(.1875rem);width:.125rem}.logo{margin-left:-.5rem;-webkit-user-select:none;-ms-user-select:none;user-select:none}.logo__img{margin-bottom:-.3125rem}.bookmarks-btn{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.8125rem;height:2rem;margin-left:.8125rem;padding-left:.8125rem;position:relative;text-transform:lowercase;transition:all .2s}.bookmarks-btn--active,.bookmarks-btn:focus,.bookmarks-btn:hover{color:#fff}.bookmarks-btn--active .bookmarks-btn__icon,.bookmarks-btn:focus .bookmarks-btn__icon,.bookmarks-btn:hover .bookmarks-btn__icon{color:hsla(0,0%,100%,.8)}.bookmarks-btn:before{background-color:hsla(0,0%,100%,.3);content:"";display:block;height:.9375rem;left:0;position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:.125rem}.bookmarks-btn__icon{color:hsla(0,0%,100%,.6);font-size:.625rem;margin-left:.125rem;-webkit-transform:translateY(-.0625rem);transform:translateY(-.0625rem);transition:all .2s}.job-list{background-color:#fff;scrollbar-color:#cacdd0 #fff;scrollbar-width:thin}.job-list::-webkit-scrollbar{width:.25rem}.job-list::-webkit-scrollbar-track{background-color:#fff}.job-list::-webkit-scrollbar-thumb{background-color:#cacdd0;-webkit-transition:all .2s;transition:all .2s}.job-list::-webkit-scrollbar-thumb:hover{background-color:#b1b4b8}.job-list--bookmarks{border-radius:.25rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);left:50%;min-height:4.75rem;min-width:21.25rem;opacity:0;overflow:hidden;pointer-events:none;position:fixed;top:5.125rem;-webkit-transform:scale(.9) translateX(-50%);transform:scale(.9) translateX(-50%);-webkit-transform-origin:left;transform-origin:left;transition:all .2s;visibility:hidden;width:21.25rem;z-index:10}.job-list--bookmarks:hover{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-list--bookmarks:before{color:#616268;content:"Nothing bookmarked yet...";font-size:.8125rem;left:50%;position:absolute;top:49%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);z-index:-1}.job-list--visible{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-item{background-color:#fff;cursor:pointer;padding:.875rem 1.25rem;transition:all .2s}.job-item:not(:nth-child(7)){border-bottom:.0625rem solid #ebeff1}.job-item--active,.job-item:hover{background-color:#f4f5f7}.job-item__link{display:flex;height:100%;width:100%}.job-item__badge{align-items:center;background-color:#8dd335;border-radius:.3125rem;display:flex;font-size:.8125rem;font-weight:600;height:2.875rem;justify-content:center;margin-right:.8125rem;width:2.375rem}.job-item:nth-child(4n+2) .job-item__badge{background-color:#3d87f1}.job-item:nth-child(4n+3) .job-item__badge{background-color:#d2d631}.job-item:nth-child(4n+4) .job-item__badge{background-color:#d96a46}.job-item__company{font-size:.75rem;font-style:italic;margin-bottom:.125rem}.job-item__extras{-webkit-column-gap:.625rem;column-gap:.625rem;display:grid;grid-template-columns:4.0625rem 4.5rem 4.0625rem}.job-item__extra{color:#4d5054;font-size:.6875rem}.job-item__extra-icon{color:#bec5ce;font-size:.625rem;margin-right:.0625rem}.job-item__right{align-items:flex-end;display:flex;flex-direction:column;margin-left:auto}.job-item__bookmark-icon{color:#d7dbe0;cursor:pointer;font-size:.875rem;transition:all .2s}.job-item__bookmark-icon--bookmarked{color:#2671dd}.job-item__time{color:#515459;font-size:.625rem;margin-top:.25rem}.main{justify-content:center}.intro,.main{align-items:center;display:flex;flex-direction:column}.intro{margin-top:1.25rem;row-gap:2.0625rem;row-gap:1.25rem}.search{-webkit-animation:intro .3s .1s backwards;animation:intro .3s .1s backwards;position:relative}.search__submit-btn{cursor:pointer;left:1.5625rem;position:absolute;top:1.0625rem}.search__icon{color:rgba(0,0,0,.73);transition:all .2s}.search__icon:focus,.search__icon:hover{color:rgba(0,0,0,.93)}.search__input{background-color:hsla(0,0%,100%,.9);border-radius:.25rem;caret-color:rgba(0,0,0,.5);color:rgba(0,0,0,.9);height:3.5rem;padding-bottom:.125rem;padding-left:3.4375rem;padding-right:.9375rem;transition:all .2s,box-shadow .1s;width:38.125rem}.search__input::selection{background-color:rgba(0,0,0,.25)}.search__input:focus,.search__input:hover{background-color:#fff}.search__input:focus{box-shadow:0 0 0 .25rem hsla(0,0%,100%,.4)}.search__input::-webkit-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input:-ms-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input::placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input--invalid{box-shadow:0 0 0 .25rem rgba(47,19,19,.729)}.container{-webkit-animation:intro .3s .2s backwards;animation:intro .3s .2s backwards;background-color:#fff;border-radius:.5rem;border-top-right-radius:.5625rem;box-shadow:0 .1875rem .3125rem rgba(0,0,0,.07);display:flex;height:38.5rem;margin:2.5rem .75rem 0;width:61rem}.search-results{cursor:default;display:flex;flex-direction:column;position:relative;width:21.25rem}.search-results__top{align-items:center;border-bottom:.0625rem solid #e8edf0;display:flex;justify-content:space-between;padding:.625rem 1.25rem}.count{font-size:.75rem}.sorting__icon{color:#4c4f50;font-size:.6875rem;margin-right:.3125rem}.sorting__button{background-color:#e8edf0;border-radius:.1875rem;cursor:pointer;font-size:.625rem;margin-left:.125rem;padding:.375rem .5rem;text-transform:uppercase;transition:all .2s}.sorting__button:focus,.sorting__button:hover{background-color:#d0d5d8}.sorting__button--active,.sorting__button--active:focus,.sorting__button--active:hover{background-color:#3c4041;color:#fff}.pagination{align-items:center;border-top:.0625rem solid #e8edf0;display:flex;height:2.5rem;justify-content:space-between;margin-top:auto;padding:0 1.25rem .0625rem}.pagination__button{background-color:#eceff2;border-radius:31.25rem;color:#747c82;cursor:pointer;font-size:.625rem;padding:.25rem .625rem;transition:all .2s,visibility 0s}.pagination__button:focus,.pagination__button:hover{background-color:#dde2e6}.pagination__button--hidden{visibility:hidden}.pagination__number{font-weight:500}.pagination__icon{color:#9fa6b0;font-size:.5rem}.job-details{background-color:#eff2f5;border-bottom-right-radius:.5rem;border-top-right-radius:.75rem;flex:1;position:relative}.job-details__start-view{left:50%;position:absolute;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.job-details__start-text{color:#24292d;font-size:.875rem;opacity:.55;text-align:center;width:17.1875rem}.job-details__start-text--big{color:#0d1114;font-size:1.125rem;font-weight:600;margin-bottom:.625rem}.job-details__content{height:100%}.job-details__cover-img{border-top-right-radius:.5rem;height:10.875rem;-o-object-fit:cover;object-fit:cover;position:absolute;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none;width:100%;z-index:0}.job-details__footer{border-top:.0625rem solid #dce2e8;margin-left:2.625rem;margin-right:2.625rem;margin-top:2.0625rem;padding-top:.8125rem}.job-details__footer-text{color:#858b8f;font-size:.625rem}.apply-btn{align-items:center;background-color:#2671dd;border-radius:.1875rem;color:hsla(0,0%,100%,.92);cursor:pointer;display:flex;font-size:.6875rem;padding:.3125rem .625rem .375rem;position:absolute;right:.75rem;text-transform:uppercase;top:.75rem;transition:all .2s;z-index:2}.apply-btn:focus,.apply-btn:hover{background-color:#1d60bd;color:#fff}.apply-btn__icon{color:hsla(0,0%,100%,.65);font-size:.5rem;margin-left:.25rem;margin-top:-.0625rem}.job-info{-webkit-column-gap:1rem;column-gap:1rem;display:flex;margin-bottom:2.5rem;padding-top:7.5rem;position:relative;z-index:1}.job-info:before{background-image:linear-gradient(0deg,rgba(0,0,0,.7),rgba(0,0,0,.15));border-top-right-radius:.5rem;content:"";height:10.875rem;left:0;position:absolute;top:0;width:100%;z-index:-1}.job-info__left{padding-left:2.625rem}.job-info__right{padding-right:2.625rem}.job-info__badge{align-items:center;background-color:#d0d335;border-radius:.3125rem;display:flex;font-size:1.375rem;font-weight:600;height:4.375rem;justify-content:center;margin-bottom:.8125rem;width:3.4375rem}.job-info__below-badge{display:flex;justify-content:space-between}.job-info__time{color:#494d4f;font-size:.75rem;-webkit-transform:translateY(.0625rem);transform:translateY(.0625rem)}.job-info__bookmark-btn{cursor:pointer}.job-info__bookmark-btn:hover .job-info__bookmark-icon{color:#2671dd}.job-info__bookmark-icon{color:#d7dbe0;font-size:1.125rem;transition:all .2s}.job-info__bookmark-icon--bookmarked{color:#2671dd}.job-info__company{color:hsla(0,0%,100%,.8);font-size:.875rem;font-style:italic}.job-info__description{font-size:.875rem;line-height:1.4;margin-bottom:.75rem;margin-top:1.125rem}.job-info__extras{-webkit-column-gap:2.1875rem;column-gap:2.1875rem;display:flex}.job-info__extra{align-items:center;display:flex;font-size:.75rem}.job-info__extra-icon{align-items:center;background-color:#e4e9ed;border-radius:50%;color:#a1a8b0;display:flex;font-size:.5625rem;height:1.4375rem;justify-content:center;margin-right:.5rem;width:1.4375rem}.qualifications{display:flex;margin-bottom:1.875rem;padding-left:2.625rem;padding-right:2.625rem}.qualifications__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.qualifications__left{margin-right:2.1875rem}.qualifications__list{display:flex;flex-wrap:wrap;gap:.375rem}.qualifications__item{background-color:#e6ebee;border-radius:.125rem;color:#494d4f;font-size:.8125rem;padding:.375rem .625rem}.reviews{display:flex;padding-left:2.625rem;padding-right:2.625rem}.reviews__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.reviews__left{margin-right:2.1875rem}.reviews__list{-webkit-column-gap:1.25rem;column-gap:1.25rem;display:grid;flex:1;grid-template-columns:1fr 1fr;grid-template-rows:auto auto;row-gap:1.25rem}.reviews__item{color:#494d4f;font-size:.8125rem;font-style:italic;position:relative;-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.reviews__item:before{color:#d2d7db;content:"“";font-size:3.125rem;left:-.625rem;position:absolute;top:-.9375rem;-webkit-transform:translateZ(-.0625rem);transform:translateZ(-.0625rem)}.footer{align-items:center;color:#a4a9ac;display:flex;justify-content:space-between;margin:.9375rem auto 0;max-width:62.5rem;padding:0 .75rem}.copyright{font-size:.6875rem}.copyright ::selection{background-color:hsla(0,0%,100%,.1)}.copyright__text{line-height:1.2}.copyright__link{text-decoration:underline}.copyright__icon{color:#aeb3b6;font-size:.625rem;margin-left:.125rem;margin-right:.25rem}.jobs-available{align-self:flex-start;font-size:.6875rem}.spinner{-webkit-animation:spinner 1s linear infinite;animation:spinner 1s linear infinite;border-radius:50%;position:absolute;visibility:hidden}.spinner--search{border:.3125rem solid #e2e7e9;border-left-color:#ccd1d3;height:5.3125rem;left:50%;top:18%;width:5.3125rem}.spinner--job-details{border:.375rem solid #d5d9db;border-left-color:#bbc0c2;height:6.5625rem;left:50%;top:40%;width:6.5625rem}.spinner--visible{visibility:visible}.error{background:#fff;border-radius:.1875rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);display:flex;min-height:2.875rem;opacity:0;padding:.875rem 1.25rem;position:absolute;right:.9375rem;top:.9375rem;-webkit-transform:translateY(-7.5rem);transform:translateY(-7.5rem);transition:all .3s;visibility:hidden;width:17.5rem}.error--visible{opacity:1;-webkit-transform:initial;transform:none;visibility:initial}.error__icon{color:#7b4040;font-size:1rem;margin-top:.125rem}.error__right{margin-left:.625rem}.error__title{font-size:.75rem;font-weight:500;margin-bottom:.0625rem;text-transform:uppercase}.error__text{color:#616268;font-size:.8125rem}@media (max-height:925px) and (min-width:1010px){.body{padding-bottom:3.125rem}}@media (max-width:1179px){.job-list--bookmarks{right:0}}@media (max-width:1009px){.body{padding:0 .75rem 3.125rem}.header__top{max-width:50rem;padding-left:0;padding-right:0}.container{border-radius:.5rem;flex-direction:column;height:auto;max-width:50rem;overflow:hidden;width:100%}.search-results{width:100%}.job-details{display:none}.footer{max-width:50rem;padding-left:0;padding-right:0}}@media (max-width:660px){.intro,.search,.search__input{width:100%}.footer{justify-content:center}.copyright{text-align:center}.jobs-available{display:none;margin-left:.9375rem;text-align:right}.intro{row-gap:1.5625rem}.first-heading{max-width:25rem;text-align:center}}@media (max-width:370px){.job-list--bookmarks{min-width:auto;width:93vw}.job-item{width:100%}.job-item__badge{display:none}.error{left:50%;right:auto;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:93vw}} diff --git a/08 - rmtDev/Build Process (webpack)/dist/main.js b/08 - rmtDev/Build Process (webpack)/dist/main.js deleted file mode 100644 index eba8c55..0000000 --- a/08 - rmtDev/Build Process (webpack)/dist/main.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! For license information please see main.js.LICENSE.txt */ -!function(){var t={9662:function(t,r,e){var n=e(614),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a function")}},9483:function(t,r,e){var n=e(4411),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a constructor")}},6077:function(t,r,e){var n=e(614),o=String,i=TypeError;t.exports=function(t){if("object"==typeof t||n(t))return t;throw i("Can't set "+o(t)+" as a prototype")}},1223:function(t,r,e){var n=e(5112),o=e(30),i=e(3070).f,a=n("unscopables"),c=Array.prototype;null==c[a]&&i(c,a,{configurable:!0,value:o(null)}),t.exports=function(t){c[a][t]=!0}},5787:function(t,r,e){var n=e(7976),o=TypeError;t.exports=function(t,r){if(n(r,t))return t;throw o("Incorrect invocation")}},9670:function(t,r,e){var n=e(111),o=String,i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not an object")}},8533:function(t,r,e){"use strict";var n=e(2092).forEach,o=e(2133)("forEach");t.exports=o?[].forEach:function(t){return n(this,t,arguments.length>1?arguments[1]:void 0)}},8457:function(t,r,e){"use strict";var n=e(9974),o=e(6916),i=e(7908),a=e(3411),c=e(7659),u=e(4411),s=e(6244),f=e(6135),l=e(8554),p=e(1246),h=Array;t.exports=function(t){var r=i(t),e=u(this),v=arguments.length,d=v>1?arguments[1]:void 0,y=void 0!==d;y&&(d=n(d,v>2?arguments[2]:void 0));var m,g,b,x,w,_,S=p(r),j=0;if(!S||this===h&&c(S))for(m=s(r),g=e?new this(m):h(m);m>j;j++)_=y?d(r[j],j):r[j],f(g,j,_);else for(w=(x=l(r,S)).next,g=e?new this:[];!(b=o(w,x)).done;j++)_=y?a(x,d,[b.value,j],!0):b.value,f(g,j,_);return g.length=j,g}},1318:function(t,r,e){var n=e(5656),o=e(1400),i=e(6244),a=function(t){return function(r,e,a){var c,u=n(r),s=i(u),f=o(a,s);if(t&&e!=e){for(;s>f;)if((c=u[f++])!=c)return!0}else for(;s>f;f++)if((t||f in u)&&u[f]===e)return t||f||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},2092:function(t,r,e){var n=e(9974),o=e(1702),i=e(8361),a=e(7908),c=e(6244),u=e(5417),s=o([].push),f=function(t){var r=1==t,e=2==t,o=3==t,f=4==t,l=6==t,p=7==t,h=5==t||l;return function(v,d,y,m){for(var g,b,x=a(v),w=i(x),_=n(d,y),S=c(w),j=0,L=m||u,E=r?L(v,S):e||p?L(v,0):void 0;S>j;j++)if((h||j in w)&&(b=_(g=w[j],j,x),t))if(r)E[j]=b;else if(b)switch(t){case 3:return!0;case 5:return g;case 6:return j;case 2:s(E,g)}else switch(t){case 4:return!1;case 7:s(E,g)}return l?-1:o||f?f:E}};t.exports={forEach:f(0),map:f(1),filter:f(2),some:f(3),every:f(4),find:f(5),findIndex:f(6),filterReject:f(7)}},1194:function(t,r,e){var n=e(7293),o=e(5112),i=e(7392),a=o("species");t.exports=function(t){return i>=51||!n((function(){var r=[];return(r.constructor={})[a]=function(){return{foo:1}},1!==r[t](Boolean).foo}))}},2133:function(t,r,e){"use strict";var n=e(7293);t.exports=function(t,r){var e=[][t];return!!e&&n((function(){e.call(null,r||function(){return 1},1)}))}},1589:function(t,r,e){var n=e(1400),o=e(6244),i=e(6135),a=Array,c=Math.max;t.exports=function(t,r,e){for(var u=o(t),s=n(r,u),f=n(void 0===e?u:e,u),l=a(c(f-s,0)),p=0;s0;)t[n]=t[--n];n!==i++&&(t[n]=e)}return t},c=function(t,r,e,n){for(var o=r.length,i=e.length,a=0,c=0;a9007199254740991)throw r("Maximum allowed index exceeded");return t}},8324:function(t){t.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},8509:function(t,r,e){var n=e(317)("span").classList,o=n&&n.constructor&&n.constructor.prototype;t.exports=o===Object.prototype?void 0:o},8886:function(t,r,e){var n=e(8113).match(/firefox\/(\d+)/i);t.exports=!!n&&+n[1]},7871:function(t){t.exports="object"==typeof window&&"object"!=typeof Deno},256:function(t,r,e){var n=e(8113);t.exports=/MSIE|Trident/.test(n)},1528:function(t,r,e){var n=e(8113),o=e(7854);t.exports=/ipad|iphone|ipod/i.test(n)&&void 0!==o.Pebble},6833:function(t,r,e){var n=e(8113);t.exports=/(?:ipad|iphone|ipod).*applewebkit/i.test(n)},5268:function(t,r,e){var n=e(4326),o=e(7854);t.exports="process"==n(o.process)},1036:function(t,r,e){var n=e(8113);t.exports=/web0s(?!.*chrome)/i.test(n)},8113:function(t,r,e){var n=e(5005);t.exports=n("navigator","userAgent")||""},7392:function(t,r,e){var n,o,i=e(7854),a=e(8113),c=i.process,u=i.Deno,s=c&&c.versions||u&&u.version,f=s&&s.v8;f&&(o=(n=f.split("."))[0]>0&&n[0]<4?1:+(n[0]+n[1])),!o&&a&&(!(n=a.match(/Edge\/(\d+)/))||n[1]>=74)&&(n=a.match(/Chrome\/(\d+)/))&&(o=+n[1]),t.exports=o},8008:function(t,r,e){var n=e(8113).match(/AppleWebKit\/(\d+)\./);t.exports=!!n&&+n[1]},748:function(t){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(t,r,e){var n=e(7854),o=e(1236).f,i=e(8880),a=e(8052),c=e(3072),u=e(9920),s=e(4705);t.exports=function(t,r){var e,f,l,p,h,v=t.target,d=t.global,y=t.stat;if(e=d?n:y?n[v]||c(v,{}):(n[v]||{}).prototype)for(f in r){if(p=r[f],l=t.dontCallGetSet?(h=o(e,f))&&h.value:e[f],!s(d?f:v+(y?".":"#")+f,t.forced)&&void 0!==l){if(typeof p==typeof l)continue;u(p,l)}(t.sham||l&&l.sham)&&i(p,"sham",!0),a(e,f,p,t)}}},7293:function(t){t.exports=function(t){try{return!!t()}catch(t){return!0}}},2104:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.apply,a=o.call;t.exports="object"==typeof Reflect&&Reflect.apply||(n?a.bind(i):function(){return a.apply(i,arguments)})},9974:function(t,r,e){var n=e(1702),o=e(9662),i=e(4374),a=n(n.bind);t.exports=function(t,r){return o(t),void 0===r?t:i?a(t,r):function(){return t.apply(r,arguments)}}},4374:function(t,r,e){var n=e(7293);t.exports=!n((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))},6916:function(t,r,e){var n=e(4374),o=Function.prototype.call;t.exports=n?o.bind(o):function(){return o.apply(o,arguments)}},6530:function(t,r,e){var n=e(9781),o=e(2597),i=Function.prototype,a=n&&Object.getOwnPropertyDescriptor,c=o(i,"name"),u=c&&"something"===function(){}.name,s=c&&(!n||n&&a(i,"name").configurable);t.exports={EXISTS:c,PROPER:u,CONFIGURABLE:s}},1702:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.bind,a=o.call,c=n&&i.bind(a,a);t.exports=n?function(t){return t&&c(t)}:function(t){return t&&function(){return a.apply(t,arguments)}}},5005:function(t,r,e){var n=e(7854),o=e(614),i=function(t){return o(t)?t:void 0};t.exports=function(t,r){return arguments.length<2?i(n[t]):n[t]&&n[t][r]}},1246:function(t,r,e){var n=e(648),o=e(8173),i=e(7497),a=e(5112)("iterator");t.exports=function(t){if(null!=t)return o(t,a)||o(t,"@@iterator")||i[n(t)]}},8554:function(t,r,e){var n=e(6916),o=e(9662),i=e(9670),a=e(6330),c=e(1246),u=TypeError;t.exports=function(t,r){var e=arguments.length<2?c(t):r;if(o(e))return i(n(e,t));throw u(a(t)+" is not iterable")}},8173:function(t,r,e){var n=e(9662);t.exports=function(t,r){var e=t[r];return null==e?void 0:n(e)}},7854:function(t,r,e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e.g&&e.g)||function(){return this}()||Function("return this")()},2597:function(t,r,e){var n=e(1702),o=e(7908),i=n({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,r){return i(o(t),r)}},3501:function(t){t.exports={}},842:function(t,r,e){var n=e(7854);t.exports=function(t,r){var e=n.console;e&&e.error&&(1==arguments.length?e.error(t):e.error(t,r))}},490:function(t,r,e){var n=e(5005);t.exports=n("document","documentElement")},4664:function(t,r,e){var n=e(9781),o=e(7293),i=e(317);t.exports=!n&&!o((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},8361:function(t,r,e){var n=e(1702),o=e(7293),i=e(4326),a=Object,c=n("".split);t.exports=o((function(){return!a("z").propertyIsEnumerable(0)}))?function(t){return"String"==i(t)?c(t,""):a(t)}:a},2788:function(t,r,e){var n=e(1702),o=e(614),i=e(5465),a=n(Function.toString);o(i.inspectSource)||(i.inspectSource=function(t){return a(t)}),t.exports=i.inspectSource},9909:function(t,r,e){var n,o,i,a=e(8536),c=e(7854),u=e(1702),s=e(111),f=e(8880),l=e(2597),p=e(5465),h=e(6200),v=e(3501),d="Object already initialized",y=c.TypeError,m=c.WeakMap;if(a||p.state){var g=p.state||(p.state=new m),b=u(g.get),x=u(g.has),w=u(g.set);n=function(t,r){if(x(g,t))throw new y(d);return r.facade=t,w(g,t,r),r},o=function(t){return b(g,t)||{}},i=function(t){return x(g,t)}}else{var _=h("state");v[_]=!0,n=function(t,r){if(l(t,_))throw new y(d);return r.facade=t,f(t,_,r),r},o=function(t){return l(t,_)?t[_]:{}},i=function(t){return l(t,_)}}t.exports={set:n,get:o,has:i,enforce:function(t){return i(t)?o(t):n(t,{})},getterFor:function(t){return function(r){var e;if(!s(r)||(e=o(r)).type!==t)throw y("Incompatible receiver, "+t+" required");return e}}}},7659:function(t,r,e){var n=e(5112),o=e(7497),i=n("iterator"),a=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||a[i]===t)}},3157:function(t,r,e){var n=e(4326);t.exports=Array.isArray||function(t){return"Array"==n(t)}},614:function(t){t.exports=function(t){return"function"==typeof t}},4411:function(t,r,e){var n=e(1702),o=e(7293),i=e(614),a=e(648),c=e(5005),u=e(2788),s=function(){},f=[],l=c("Reflect","construct"),p=/^\s*(?:class|function)\b/,h=n(p.exec),v=!p.exec(s),d=function(t){if(!i(t))return!1;try{return l(s,f,t),!0}catch(t){return!1}},y=function(t){if(!i(t))return!1;switch(a(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return v||!!h(p,u(t))}catch(t){return!0}};y.sham=!0,t.exports=!l||o((function(){var t;return d(d.call)||!d(Object)||!d((function(){t=!0}))||t}))?y:d},4705:function(t,r,e){var n=e(7293),o=e(614),i=/#|\.prototype\./,a=function(t,r){var e=u[c(t)];return e==f||e!=s&&(o(r)?n(r):!!r)},c=a.normalize=function(t){return String(t).replace(i,".").toLowerCase()},u=a.data={},s=a.NATIVE="N",f=a.POLYFILL="P";t.exports=a},111:function(t,r,e){var n=e(614);t.exports=function(t){return"object"==typeof t?null!==t:n(t)}},1913:function(t){t.exports=!1},7850:function(t,r,e){var n=e(111),o=e(4326),i=e(5112)("match");t.exports=function(t){var r;return n(t)&&(void 0!==(r=t[i])?!!r:"RegExp"==o(t))}},2190:function(t,r,e){var n=e(5005),o=e(614),i=e(7976),a=e(3307),c=Object;t.exports=a?function(t){return"symbol"==typeof t}:function(t){var r=n("Symbol");return o(r)&&i(r.prototype,c(t))}},408:function(t,r,e){var n=e(9974),o=e(6916),i=e(9670),a=e(6330),c=e(7659),u=e(6244),s=e(7976),f=e(8554),l=e(1246),p=e(9212),h=TypeError,v=function(t,r){this.stopped=t,this.result=r},d=v.prototype;t.exports=function(t,r,e){var y,m,g,b,x,w,_,S=e&&e.that,j=!(!e||!e.AS_ENTRIES),L=!(!e||!e.IS_ITERATOR),E=!(!e||!e.INTERRUPTED),O=n(r,S),k=function(t){return y&&p(y,"normal",t),new v(!0,t)},P=function(t){return j?(i(t),E?O(t[0],t[1],k):O(t[0],t[1])):E?O(t,k):O(t)};if(L)y=t;else{if(!(m=l(t)))throw h(a(t)+" is not iterable");if(c(m)){for(g=0,b=u(t);b>g;g++)if((x=P(t[g]))&&s(d,x))return x;return new v(!1)}y=f(t,m)}for(w=y.next;!(_=o(w,y)).done;){try{x=P(_.value)}catch(t){p(y,"throw",t)}if("object"==typeof x&&x&&s(d,x))return x}return new v(!1)}},9212:function(t,r,e){var n=e(6916),o=e(9670),i=e(8173);t.exports=function(t,r,e){var a,c;o(t);try{if(!(a=i(t,"return"))){if("throw"===r)throw e;return e}a=n(a,t)}catch(t){c=!0,a=t}if("throw"===r)throw e;if(c)throw a;return o(a),e}},3383:function(t,r,e){"use strict";var n,o,i,a=e(7293),c=e(614),u=e(30),s=e(9518),f=e(8052),l=e(5112),p=e(1913),h=l("iterator"),v=!1;[].keys&&("next"in(i=[].keys())?(o=s(s(i)))!==Object.prototype&&(n=o):v=!0),null==n||a((function(){var t={};return n[h].call(t)!==t}))?n={}:p&&(n=u(n)),c(n[h])||f(n,h,(function(){return this})),t.exports={IteratorPrototype:n,BUGGY_SAFARI_ITERATORS:v}},7497:function(t){t.exports={}},6244:function(t,r,e){var n=e(7466);t.exports=function(t){return n(t.length)}},6339:function(t,r,e){var n=e(7293),o=e(614),i=e(2597),a=e(9781),c=e(6530).CONFIGURABLE,u=e(2788),s=e(9909),f=s.enforce,l=s.get,p=Object.defineProperty,h=a&&!n((function(){return 8!==p((function(){}),"length",{value:8}).length})),v=String(String).split("String"),d=t.exports=function(t,r,e){"Symbol("===String(r).slice(0,7)&&(r="["+String(r).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),e&&e.getter&&(r="get "+r),e&&e.setter&&(r="set "+r),(!i(t,"name")||c&&t.name!==r)&&p(t,"name",{value:r,configurable:!0}),h&&e&&i(e,"arity")&&t.length!==e.arity&&p(t,"length",{value:e.arity});try{e&&i(e,"constructor")&&e.constructor?a&&p(t,"prototype",{writable:!1}):t.prototype&&(t.prototype=void 0)}catch(t){}var n=f(t);return i(n,"source")||(n.source=v.join("string"==typeof r?r:"")),t};Function.prototype.toString=d((function(){return o(this)&&l(this).source||u(this)}),"toString")},4758:function(t){var r=Math.ceil,e=Math.floor;t.exports=Math.trunc||function(t){var n=+t;return(n>0?e:r)(n)}},5948:function(t,r,e){var n,o,i,a,c,u,s,f,l=e(7854),p=e(9974),h=e(1236).f,v=e(261).set,d=e(6833),y=e(1528),m=e(1036),g=e(5268),b=l.MutationObserver||l.WebKitMutationObserver,x=l.document,w=l.process,_=l.Promise,S=h(l,"queueMicrotask"),j=S&&S.value;j||(n=function(){var t,r;for(g&&(t=w.domain)&&t.exit();o;){r=o.fn,o=o.next;try{r()}catch(t){throw o?a():i=void 0,t}}i=void 0,t&&t.enter()},d||g||m||!b||!x?!y&&_&&_.resolve?((s=_.resolve(void 0)).constructor=_,f=p(s.then,s),a=function(){f(n)}):g?a=function(){w.nextTick(n)}:(v=p(v,l),a=function(){v(n)}):(c=!0,u=x.createTextNode(""),new b(n).observe(u,{characterData:!0}),a=function(){u.data=c=!c})),t.exports=j||function(t){var r={fn:t,next:void 0};i&&(i.next=r),o||(o=r,a()),i=r}},735:function(t,r,e){var n=e(133);t.exports=n&&!!Symbol.for&&!!Symbol.keyFor},133:function(t,r,e){var n=e(7392),o=e(7293);t.exports=!!Object.getOwnPropertySymbols&&!o((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&n&&n<41}))},8536:function(t,r,e){var n=e(7854),o=e(614),i=e(2788),a=n.WeakMap;t.exports=o(a)&&/native code/.test(i(a))},8523:function(t,r,e){"use strict";var n=e(9662),o=function(t){var r,e;this.promise=new t((function(t,n){if(void 0!==r||void 0!==e)throw TypeError("Bad Promise constructor");r=t,e=n})),this.resolve=n(r),this.reject=n(e)};t.exports.f=function(t){return new o(t)}},3929:function(t,r,e){var n=e(7850),o=TypeError;t.exports=function(t){if(n(t))throw o("The method doesn't accept regular expressions");return t}},30:function(t,r,e){var n,o=e(9670),i=e(6048),a=e(748),c=e(3501),u=e(490),s=e(317),f=e(6200)("IE_PROTO"),l=function(){},p=function(t){return" - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Build Process (webpack)/src/index.js b/08 - rmtDev/Build Process (webpack)/src/index.js deleted file mode 100644 index c7c6751..0000000 --- a/08 - rmtDev/Build Process (webpack)/src/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import './components/Bookmarks.js'; -import './components/Error.js'; -import './components/JobDetails.js'; -import './components/JobList.js'; -import './components/Pagination.js'; -import './components/Router.js'; -import './components/Search.js'; -import './components/Sorting.js'; -import './components/Spinner.js'; -import './components/Storage.js'; - -import './index.css'; \ No newline at end of file diff --git a/08 - rmtDev/Build Process (webpack)/webpack.config.js b/08 - rmtDev/Build Process (webpack)/webpack.config.js deleted file mode 100644 index b970c2c..0000000 --- a/08 - rmtDev/Build Process (webpack)/webpack.config.js +++ /dev/null @@ -1,27 +0,0 @@ -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); - -module.exports = { - mode: 'production', - entry: './src/index.js', - output: { - filename: 'main.js' - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: 'main.css' - }) - ], - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: ['babel-loader'] - }, - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] - } - ] - } -}; \ No newline at end of file diff --git a/08 - rmtDev/Error Component/index.css b/08 - rmtDev/Error Component/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Error Component/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Error Component/index.html b/08 - rmtDev/Error Component/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Error Component/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Error Component/index.js b/08 - rmtDev/Error Component/index.js deleted file mode 100644 index 1842071..0000000 --- a/08 - rmtDev/Error Component/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobList.js'; -import './src/components/Search.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Error Component/src/common.js b/08 - rmtDev/Error Component/src/common.js deleted file mode 100644 index 8494ea4..0000000 --- a/08 - rmtDev/Error Component/src/common.js +++ /dev/null @@ -1,20 +0,0 @@ -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/08 - rmtDev/Error Component/src/components/Error.js b/08 - rmtDev/Error Component/src/components/Error.js deleted file mode 100644 index ac0e9b2..0000000 --- a/08 - rmtDev/Error Component/src/components/Error.js +++ /dev/null @@ -1,14 +0,0 @@ -import { - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, 3500); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Error Component/src/components/JobList.js b/08 - rmtDev/Error Component/src/components/JobList.js deleted file mode 100644 index 1bace65..0000000 --- a/08 - rmtDev/Error Component/src/components/JobList.js +++ /dev/null @@ -1,105 +0,0 @@ -import { - jobListSearchEl, - jobDetailsContentEl -} from '../common.js'; -import renderSpinner from './Spinner.js'; - -const clickHandler = event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // fetch job item data - fetch(`https://bytegrad.com/course-assets/js/2/api/jobs/${id}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; - }) - .catch(error => console.log(error)); -}; - -jobListSearchEl.addEventListener('click', clickHandler); \ No newline at end of file diff --git a/08 - rmtDev/Error Component/src/components/Search.js b/08 - rmtDev/Error Component/src/components/Search.js deleted file mode 100644 index 8c30f60..0000000 --- a/08 - rmtDev/Error Component/src/components/Search.js +++ /dev/null @@ -1,82 +0,0 @@ -import { - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; - -const submitHandler = event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - renderSpinner('search'); - - // fetch search results - fetch(`https://bytegrad.com/course-assets/js/2/api/jobs?search=${searchText}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job items - const { jobItems } = data; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - jobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); - }) - .catch(error => console.log(error)); -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Error Component/src/components/Spinner.js b/08 - rmtDev/Error Component/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Error Component/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Improving Fetch Calls/index.css b/08 - rmtDev/Improving Fetch Calls/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Improving Fetch Calls/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Improving Fetch Calls/index.html b/08 - rmtDev/Improving Fetch Calls/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Improving Fetch Calls/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Improving Fetch Calls/index.js b/08 - rmtDev/Improving Fetch Calls/index.js deleted file mode 100644 index 65ddab7..0000000 --- a/08 - rmtDev/Improving Fetch Calls/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Search.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Improving Fetch Calls/src/common.js b/08 - rmtDev/Improving Fetch Calls/src/common.js deleted file mode 100644 index 39a7caf..0000000 --- a/08 - rmtDev/Improving Fetch Calls/src/common.js +++ /dev/null @@ -1,25 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/08 - rmtDev/Improving Fetch Calls/src/components/Error.js b/08 - rmtDev/Improving Fetch Calls/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Improving Fetch Calls/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Improving Fetch Calls/src/components/JobDetails.js b/08 - rmtDev/Improving Fetch Calls/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/Improving Fetch Calls/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Improving Fetch Calls/src/components/JobList.js b/08 - rmtDev/Improving Fetch Calls/src/components/JobList.js deleted file mode 100644 index 5dfee66..0000000 --- a/08 - rmtDev/Improving Fetch Calls/src/components/JobList.js +++ /dev/null @@ -1,84 +0,0 @@ -import { - BASE_API_URL, - jobListSearchEl, - jobDetailsContentEl -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; - -const renderJobList = jobItems => { - jobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // fetch job item data - fetch(`${BASE_API_URL}/jobs/${id}`) - .then(response => { - if (!response.ok) { // 4xx, 5xx status code - throw new Error('Resource issue (e.g. resource doesn\'t exist) or server issue'); - } - - return response.json(); - }) - .then(data => { - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - }) - .catch(error => { // network problem or other errors (e.g. trying to parse something not JSON as JSON) - renderSpinner('job-details'); - renderError(error.message); - }); -}; - -jobListSearchEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Improving Fetch Calls/src/components/Search.js b/08 - rmtDev/Improving Fetch Calls/src/components/Search.js deleted file mode 100644 index 259c3a8..0000000 --- a/08 - rmtDev/Improving Fetch Calls/src/components/Search.js +++ /dev/null @@ -1,64 +0,0 @@ -import { - BASE_API_URL, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; - -const submitHandler = event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - renderSpinner('search'); - - // fetch search results - fetch(`${BASE_API_URL}/jobs?search=${searchText}`) - .then(response => { - if (!response.ok) { // 4xx, 5xx status code - throw new Error('Resource issue (e.g. resource doesn\'t exist) or server issue'); - } - - return response.json(); - }) - .then(data => { - // extract job items - const { jobItems } = data; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - renderJobList(jobItems); - }) - .catch(error => { // network problem or other errors (e.g. trying to parse something not JSON as JSON) - renderSpinner('search'); - renderError(error.message); - }); -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Improving Fetch Calls/src/components/Spinner.js b/08 - rmtDev/Improving Fetch Calls/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Improving Fetch Calls/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Job List Component/index.css b/08 - rmtDev/Job List Component/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Job List Component/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Job List Component/index.html b/08 - rmtDev/Job List Component/index.html deleted file mode 100644 index 93ddb40..0000000 --- a/08 - rmtDev/Job List Component/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Job List Component/index.js b/08 - rmtDev/Job List Component/index.js deleted file mode 100644 index bed83f8..0000000 --- a/08 - rmtDev/Job List Component/index.js +++ /dev/null @@ -1,203 +0,0 @@ -// -- GLOBAL -- -const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -const errorEl = document.querySelector('.error'); -const errorTextEl = document.querySelector('.error__text'); -const jobDetailsEl = document.querySelector('.job-details'); -const jobDetailsContentEl = document.querySelector(".job-details__content"); -const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -const jobListSearchEl = document.querySelector(".job-list--search"); -const numberEl = document.querySelector(".count__number"); -const paginationEl = document.querySelector(".pagination"); -const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -const searchFormEl = document.querySelector(".search"); -const searchInputEl = document.querySelector(".search__input"); -const sortingEl = document.querySelector(".sorting"); -const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -const spinnerSearchEl = document.querySelector(".spinner--search"); -const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// -- SEARCH COMPONENT -- -const submitHandler = event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - errorTextEl.textContent = 'Your search may not contain numbers'; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, 3500); - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - spinnerSearchEl.classList.add('spinner--visible'); - - // fetch search results - fetch(`https://bytegrad.com/course-assets/js/2/api/jobs?search=${searchText}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job items - const { jobItems } = data; - - // remove spinner - spinnerSearchEl.classList.remove('spinner--visible'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - jobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); - }) - .catch(error => console.log(error)); -}; - -searchFormEl.addEventListener('submit', submitHandler); - -// -- JOB LIST COMPONENT -- -const clickHandler = event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - spinnerJobDetailsEl.classList.add('spinner--visible'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // fetch job item data - fetch(`https://bytegrad.com/course-assets/js/2/api/jobs/${id}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job item - const { jobItem } = data; - - // remove spinner - spinnerJobDetailsEl.classList.remove('spinner--visible'); - - // render job details - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; - }) - .catch(error => console.log(error)); -}; - -jobListSearchEl.addEventListener('click', clickHandler); - - - diff --git a/08 - rmtDev/Modularity with ES Modules/index.css b/08 - rmtDev/Modularity with ES Modules/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Modularity with ES Modules/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Modularity with ES Modules/index.html b/08 - rmtDev/Modularity with ES Modules/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Modularity with ES Modules/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Modularity with ES Modules/index.js b/08 - rmtDev/Modularity with ES Modules/index.js deleted file mode 100644 index 1842071..0000000 --- a/08 - rmtDev/Modularity with ES Modules/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobList.js'; -import './src/components/Search.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Modularity with ES Modules/src/common.js b/08 - rmtDev/Modularity with ES Modules/src/common.js deleted file mode 100644 index 8494ea4..0000000 --- a/08 - rmtDev/Modularity with ES Modules/src/common.js +++ /dev/null @@ -1,20 +0,0 @@ -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/08 - rmtDev/Modularity with ES Modules/src/components/Error.js b/08 - rmtDev/Modularity with ES Modules/src/components/Error.js deleted file mode 100644 index e69de29..0000000 diff --git a/08 - rmtDev/Modularity with ES Modules/src/components/JobList.js b/08 - rmtDev/Modularity with ES Modules/src/components/JobList.js deleted file mode 100644 index 85985cf..0000000 --- a/08 - rmtDev/Modularity with ES Modules/src/components/JobList.js +++ /dev/null @@ -1,105 +0,0 @@ -import { - jobListSearchEl, - jobDetailsContentEl, - spinnerJobDetailsEl -} from '../common.js'; - -const clickHandler = event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - spinnerJobDetailsEl.classList.add('spinner--visible'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // fetch job item data - fetch(`https://bytegrad.com/course-assets/js/2/api/jobs/${id}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job item - const { jobItem } = data; - - // remove spinner - spinnerJobDetailsEl.classList.remove('spinner--visible'); - - // render job details - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; - }) - .catch(error => console.log(error)); -}; - -jobListSearchEl.addEventListener('click', clickHandler); \ No newline at end of file diff --git a/08 - rmtDev/Modularity with ES Modules/src/components/Search.js b/08 - rmtDev/Modularity with ES Modules/src/components/Search.js deleted file mode 100644 index 21f06d5..0000000 --- a/08 - rmtDev/Modularity with ES Modules/src/components/Search.js +++ /dev/null @@ -1,84 +0,0 @@ -import { - searchInputEl, - searchFormEl, - spinnerSearchEl, - jobListSearchEl, - numberEl -} from '../common.js'; - -const submitHandler = event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - errorTextEl.textContent = 'Your search may not contain numbers'; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, 3500); - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - spinnerSearchEl.classList.add('spinner--visible'); - - // fetch search results - fetch(`https://bytegrad.com/course-assets/js/2/api/jobs?search=${searchText}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job items - const { jobItems } = data; - - // remove spinner - spinnerSearchEl.classList.remove('spinner--visible'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - jobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); - }) - .catch(error => console.log(error)); -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Modularity with ES Modules/src/components/Spinner.js b/08 - rmtDev/Modularity with ES Modules/src/components/Spinner.js deleted file mode 100644 index e69de29..0000000 diff --git a/08 - rmtDev/More Refactoring/index.css b/08 - rmtDev/More Refactoring/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/More Refactoring/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/More Refactoring/index.html b/08 - rmtDev/More Refactoring/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/More Refactoring/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/More Refactoring/index.js b/08 - rmtDev/More Refactoring/index.js deleted file mode 100644 index 65ddab7..0000000 --- a/08 - rmtDev/More Refactoring/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Search.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/More Refactoring/src/common.js b/08 - rmtDev/More Refactoring/src/common.js deleted file mode 100644 index 39a7caf..0000000 --- a/08 - rmtDev/More Refactoring/src/common.js +++ /dev/null @@ -1,25 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/08 - rmtDev/More Refactoring/src/components/Error.js b/08 - rmtDev/More Refactoring/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/More Refactoring/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/More Refactoring/src/components/JobDetails.js b/08 - rmtDev/More Refactoring/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/More Refactoring/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/More Refactoring/src/components/JobList.js b/08 - rmtDev/More Refactoring/src/components/JobList.js deleted file mode 100644 index d386a35..0000000 --- a/08 - rmtDev/More Refactoring/src/components/JobList.js +++ /dev/null @@ -1,82 +0,0 @@ -import { - BASE_API_URL, - jobListSearchEl, - jobDetailsContentEl -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; - -const renderJobList = jobItems => { - jobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // fetch job item data - fetch(`${BASE_API_URL}/jobs/${id}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - }) - .catch(error => console.log(error)); -}; - -jobListSearchEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/More Refactoring/src/components/Search.js b/08 - rmtDev/More Refactoring/src/components/Search.js deleted file mode 100644 index a4bd91a..0000000 --- a/08 - rmtDev/More Refactoring/src/components/Search.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - BASE_API_URL, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; - -const submitHandler = event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - renderSpinner('search'); - - // fetch search results - fetch(`${BASE_API_URL}/jobs?search=${searchText}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job items - const { jobItems } = data; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - renderJobList(jobItems); - }) - .catch(error => console.log(error)); -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/More Refactoring/src/components/Spinner.js b/08 - rmtDev/More Refactoring/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/More Refactoring/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Pagination Component/index.css b/08 - rmtDev/Pagination Component/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Pagination Component/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Pagination Component/index.html b/08 - rmtDev/Pagination Component/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Pagination Component/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Pagination Component/index.js b/08 - rmtDev/Pagination Component/index.js deleted file mode 100644 index d2d52d4..0000000 --- a/08 - rmtDev/Pagination Component/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Pagination.js'; -import './src/components/Search.js'; -import './src/components/Sorting.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Pagination Component/src/common.js b/08 - rmtDev/Pagination Component/src/common.js deleted file mode 100644 index 696b860..0000000 --- a/08 - rmtDev/Pagination Component/src/common.js +++ /dev/null @@ -1,44 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; -export const RESULTS_PER_PAGE = 7; - -// STATE -export const state = { - searchJobItems: [], - currentPage: 1 -}; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// HELPER / UTILITY FUNCTIONS -export const getData = async completeURL => { - const response = await fetch(completeURL); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - return data; -}; \ No newline at end of file diff --git a/08 - rmtDev/Pagination Component/src/components/Error.js b/08 - rmtDev/Pagination Component/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Pagination Component/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Pagination Component/src/components/JobDetails.js b/08 - rmtDev/Pagination Component/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/Pagination Component/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Pagination Component/src/components/JobList.js b/08 - rmtDev/Pagination Component/src/components/JobList.js deleted file mode 100644 index fd55aaf..0000000 --- a/08 - rmtDev/Pagination Component/src/components/JobList.js +++ /dev/null @@ -1,85 +0,0 @@ -import { - BASE_API_URL, - RESULTS_PER_PAGE, - state, - jobListSearchEl, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = () => { - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // display job items - state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Pagination Component/src/components/Pagination.js b/08 - rmtDev/Pagination Component/src/components/Pagination.js deleted file mode 100644 index 720f1c7..0000000 --- a/08 - rmtDev/Pagination Component/src/components/Pagination.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - RESULTS_PER_PAGE, - state, - paginationEl, - paginationNumberNextEl, - paginationNumberBackEl, - paginationBtnNextEl, - paginationBtnBackEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const renderPaginationButtons = () => { - // display back button if we are on page 2 or further - if (state.currentPage >= 2) { - paginationBtnBackEl.classList.remove('pagination__button--hidden'); - } else { - paginationBtnBackEl.classList.add('pagination__button--hidden'); - } - - // display next button if there are more job items on next page - if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { - paginationBtnNextEl.classList.add('pagination__button--hidden'); - } else { - paginationBtnNextEl.classList.remove('pagination__button--hidden'); - } - - // update page numbers - paginationNumberNextEl.textContent = state.currentPage + 1; - paginationNumberBackEl.textContent = state.currentPage - 1; - - // unfocus ('blur') buttons - paginationBtnNextEl.blur(); - paginationBtnBackEl.blur(); -}; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.pagination__button'); - - // stop function if null - if (!clickedButtonEl) return; - - // check if intention is next or back - const nextPage = clickedButtonEl.className.includes('--next') ? true : false; - - // update state - nextPage ? state.currentPage++ : state.currentPage--; - - // render pagination buttons - renderPaginationButtons(); - - // render job items for that page - renderJobList(); -}; - -paginationEl.addEventListener('click', clickHandler); - -export default renderPaginationButtons; \ No newline at end of file diff --git a/08 - rmtDev/Pagination Component/src/components/Search.js b/08 - rmtDev/Pagination Component/src/components/Search.js deleted file mode 100644 index e672606..0000000 --- a/08 - rmtDev/Pagination Component/src/components/Search.js +++ /dev/null @@ -1,73 +0,0 @@ -import { - BASE_API_URL, - state, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl, - sortingBtnRecentEl, - sortingBtnRelevantEl, - getData -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // reset sorting buttons - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - - // render spinner - renderSpinner('search'); - - try { - // fetch search results - const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); - - // extract job items - const { jobItems } = data; - - // update state - state.searchJobItems = jobItems; - state.currentPage = 1; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in search job list - renderJobList(); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Pagination Component/src/components/Sorting.js b/08 - rmtDev/Pagination Component/src/components/Sorting.js deleted file mode 100644 index cd08f17..0000000 --- a/08 - rmtDev/Pagination Component/src/components/Sorting.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - state, - sortingEl, - sortingBtnRecentEl, - sortingBtnRelevantEl -} from '../common.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.sorting__button'); - - // stop function if no clicked button element - if (!clickedButtonEl) return; - - // update state (reset to page 1) - state.currentPage = 1; - - // check if intention is recent or relevant sorting - const recent = clickedButtonEl.className.includes('--recent') ? true : false; - - // make sorting button look (in)active - if (recent) { - sortingBtnRecentEl.classList.add('sorting__button--active'); - sortingBtnRelevantEl.classList.remove('sorting__button--active'); - } else { - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - } - - // sort job items - // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same - if (recent) { - state.searchJobItems.sort((a, b) => { - return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. - }); - } else { - state.searchJobItems.sort((a, b) => { - return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. - }); - } - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in list - renderJobList(); -}; - -sortingEl.addEventListener('click', clickHandler); diff --git a/08 - rmtDev/Pagination Component/src/components/Spinner.js b/08 - rmtDev/Pagination Component/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Pagination Component/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Project Setup/index.css b/08 - rmtDev/Project Setup/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Project Setup/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Project Setup/index.html b/08 - rmtDev/Project Setup/index.html deleted file mode 100644 index 93ddb40..0000000 --- a/08 - rmtDev/Project Setup/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Project Setup/index.js b/08 - rmtDev/Project Setup/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/08 - rmtDev/Refactor Fetch Calls/index.css b/08 - rmtDev/Refactor Fetch Calls/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Refactor Fetch Calls/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Refactor Fetch Calls/index.html b/08 - rmtDev/Refactor Fetch Calls/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Refactor Fetch Calls/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Refactor Fetch Calls/index.js b/08 - rmtDev/Refactor Fetch Calls/index.js deleted file mode 100644 index 65ddab7..0000000 --- a/08 - rmtDev/Refactor Fetch Calls/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Search.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Refactor Fetch Calls/src/common.js b/08 - rmtDev/Refactor Fetch Calls/src/common.js deleted file mode 100644 index b433421..0000000 --- a/08 - rmtDev/Refactor Fetch Calls/src/common.js +++ /dev/null @@ -1,37 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// HELPER / UTILITY FUNCTIONS -export const getData = async completeURL => { - const response = await fetch(completeURL); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - return data; -}; \ No newline at end of file diff --git a/08 - rmtDev/Refactor Fetch Calls/src/components/Error.js b/08 - rmtDev/Refactor Fetch Calls/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Refactor Fetch Calls/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Refactor Fetch Calls/src/components/JobDetails.js b/08 - rmtDev/Refactor Fetch Calls/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/Refactor Fetch Calls/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Refactor Fetch Calls/src/components/JobList.js b/08 - rmtDev/Refactor Fetch Calls/src/components/JobList.js deleted file mode 100644 index 40cfa32..0000000 --- a/08 - rmtDev/Refactor Fetch Calls/src/components/JobList.js +++ /dev/null @@ -1,79 +0,0 @@ -import { - BASE_API_URL, - jobListSearchEl, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = jobItems => { - jobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Refactor Fetch Calls/src/components/Search.js b/08 - rmtDev/Refactor Fetch Calls/src/components/Search.js deleted file mode 100644 index 9874b96..0000000 --- a/08 - rmtDev/Refactor Fetch Calls/src/components/Search.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - BASE_API_URL, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl, - getData -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - renderSpinner('search'); - - try { - // fetch search results - const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); - - // extract job items - const { jobItems } = data; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - renderJobList(jobItems); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Refactor Fetch Calls/src/components/Spinner.js b/08 - rmtDev/Refactor Fetch Calls/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Refactor Fetch Calls/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Refactoring/index.css b/08 - rmtDev/Refactoring/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Refactoring/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Refactoring/index.html b/08 - rmtDev/Refactoring/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Refactoring/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Refactoring/index.js b/08 - rmtDev/Refactoring/index.js deleted file mode 100644 index 1842071..0000000 --- a/08 - rmtDev/Refactoring/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobList.js'; -import './src/components/Search.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Refactoring/src/common.js b/08 - rmtDev/Refactoring/src/common.js deleted file mode 100644 index 39a7caf..0000000 --- a/08 - rmtDev/Refactoring/src/common.js +++ /dev/null @@ -1,25 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); \ No newline at end of file diff --git a/08 - rmtDev/Refactoring/src/components/Error.js b/08 - rmtDev/Refactoring/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Refactoring/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Refactoring/src/components/JobList.js b/08 - rmtDev/Refactoring/src/components/JobList.js deleted file mode 100644 index 3bdf95c..0000000 --- a/08 - rmtDev/Refactoring/src/components/JobList.js +++ /dev/null @@ -1,106 +0,0 @@ -import { - BASE_API_URL, - jobListSearchEl, - jobDetailsContentEl -} from '../common.js'; -import renderSpinner from './Spinner.js'; - -const clickHandler = event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // fetch job item data - fetch(`${BASE_API_URL}/jobs/${id}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; - }) - .catch(error => console.log(error)); -}; - -jobListSearchEl.addEventListener('click', clickHandler); \ No newline at end of file diff --git a/08 - rmtDev/Refactoring/src/components/Search.js b/08 - rmtDev/Refactoring/src/components/Search.js deleted file mode 100644 index b6159cd..0000000 --- a/08 - rmtDev/Refactoring/src/components/Search.js +++ /dev/null @@ -1,83 +0,0 @@ -import { - BASE_API_URL, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; - -const submitHandler = event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - renderSpinner('search'); - - // fetch search results - fetch(`${BASE_API_URL}/jobs?search=${searchText}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job items - const { jobItems } = data; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - jobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); - }) - .catch(error => console.log(error)); -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Refactoring/src/components/Spinner.js b/08 - rmtDev/Refactoring/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Refactoring/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Router Component/index.css b/08 - rmtDev/Router Component/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Router Component/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Router Component/index.html b/08 - rmtDev/Router Component/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Router Component/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Router Component/index.js b/08 - rmtDev/Router Component/index.js deleted file mode 100644 index e88260f..0000000 --- a/08 - rmtDev/Router Component/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Pagination.js'; -import './src/components/Router.js'; -import './src/components/Search.js'; -import './src/components/Sorting.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Router Component/src/common.js b/08 - rmtDev/Router Component/src/common.js deleted file mode 100644 index 696b860..0000000 --- a/08 - rmtDev/Router Component/src/common.js +++ /dev/null @@ -1,44 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; -export const RESULTS_PER_PAGE = 7; - -// STATE -export const state = { - searchJobItems: [], - currentPage: 1 -}; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// HELPER / UTILITY FUNCTIONS -export const getData = async completeURL => { - const response = await fetch(completeURL); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - return data; -}; \ No newline at end of file diff --git a/08 - rmtDev/Router Component/src/components/Error.js b/08 - rmtDev/Router Component/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Router Component/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Router Component/src/components/JobDetails.js b/08 - rmtDev/Router Component/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/Router Component/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Router Component/src/components/JobList.js b/08 - rmtDev/Router Component/src/components/JobList.js deleted file mode 100644 index 83c625e..0000000 --- a/08 - rmtDev/Router Component/src/components/JobList.js +++ /dev/null @@ -1,88 +0,0 @@ -import { - BASE_API_URL, - RESULTS_PER_PAGE, - state, - jobListSearchEl, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = () => { - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // display job items - state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // add id to url - history.pushState(null, '', `/#${id}`); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Router Component/src/components/Pagination.js b/08 - rmtDev/Router Component/src/components/Pagination.js deleted file mode 100644 index 720f1c7..0000000 --- a/08 - rmtDev/Router Component/src/components/Pagination.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - RESULTS_PER_PAGE, - state, - paginationEl, - paginationNumberNextEl, - paginationNumberBackEl, - paginationBtnNextEl, - paginationBtnBackEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const renderPaginationButtons = () => { - // display back button if we are on page 2 or further - if (state.currentPage >= 2) { - paginationBtnBackEl.classList.remove('pagination__button--hidden'); - } else { - paginationBtnBackEl.classList.add('pagination__button--hidden'); - } - - // display next button if there are more job items on next page - if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { - paginationBtnNextEl.classList.add('pagination__button--hidden'); - } else { - paginationBtnNextEl.classList.remove('pagination__button--hidden'); - } - - // update page numbers - paginationNumberNextEl.textContent = state.currentPage + 1; - paginationNumberBackEl.textContent = state.currentPage - 1; - - // unfocus ('blur') buttons - paginationBtnNextEl.blur(); - paginationBtnBackEl.blur(); -}; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.pagination__button'); - - // stop function if null - if (!clickedButtonEl) return; - - // check if intention is next or back - const nextPage = clickedButtonEl.className.includes('--next') ? true : false; - - // update state - nextPage ? state.currentPage++ : state.currentPage--; - - // render pagination buttons - renderPaginationButtons(); - - // render job items for that page - renderJobList(); -}; - -paginationEl.addEventListener('click', clickHandler); - -export default renderPaginationButtons; \ No newline at end of file diff --git a/08 - rmtDev/Router Component/src/components/Router.js b/08 - rmtDev/Router Component/src/components/Router.js deleted file mode 100644 index 1900378..0000000 --- a/08 - rmtDev/Router Component/src/components/Router.js +++ /dev/null @@ -1,40 +0,0 @@ -import { - BASE_API_URL, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; - -const loadHashChangeHandler = async () => { - // get id from url - const id = window.location.hash.substring(1); - - if (id) { - // remove previous job details content - jobDetailsContentEl.innerHTML = ''; - - // add spinner - renderSpinner('job-details'); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } - } -}; - -window.addEventListener('DOMContentLoaded', loadHashChangeHandler); -window.addEventListener('hashchange', loadHashChangeHandler); \ No newline at end of file diff --git a/08 - rmtDev/Router Component/src/components/Search.js b/08 - rmtDev/Router Component/src/components/Search.js deleted file mode 100644 index e672606..0000000 --- a/08 - rmtDev/Router Component/src/components/Search.js +++ /dev/null @@ -1,73 +0,0 @@ -import { - BASE_API_URL, - state, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl, - sortingBtnRecentEl, - sortingBtnRelevantEl, - getData -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // reset sorting buttons - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - - // render spinner - renderSpinner('search'); - - try { - // fetch search results - const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); - - // extract job items - const { jobItems } = data; - - // update state - state.searchJobItems = jobItems; - state.currentPage = 1; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in search job list - renderJobList(); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Router Component/src/components/Sorting.js b/08 - rmtDev/Router Component/src/components/Sorting.js deleted file mode 100644 index cd08f17..0000000 --- a/08 - rmtDev/Router Component/src/components/Sorting.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - state, - sortingEl, - sortingBtnRecentEl, - sortingBtnRelevantEl -} from '../common.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.sorting__button'); - - // stop function if no clicked button element - if (!clickedButtonEl) return; - - // update state (reset to page 1) - state.currentPage = 1; - - // check if intention is recent or relevant sorting - const recent = clickedButtonEl.className.includes('--recent') ? true : false; - - // make sorting button look (in)active - if (recent) { - sortingBtnRecentEl.classList.add('sorting__button--active'); - sortingBtnRelevantEl.classList.remove('sorting__button--active'); - } else { - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - } - - // sort job items - // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same - if (recent) { - state.searchJobItems.sort((a, b) => { - return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. - }); - } else { - state.searchJobItems.sort((a, b) => { - return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. - }); - } - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in list - renderJobList(); -}; - -sortingEl.addEventListener('click', clickHandler); diff --git a/08 - rmtDev/Router Component/src/components/Spinner.js b/08 - rmtDev/Router Component/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Router Component/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Search Component/index.css b/08 - rmtDev/Search Component/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Search Component/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Search Component/index.html b/08 - rmtDev/Search Component/index.html deleted file mode 100644 index 93ddb40..0000000 --- a/08 - rmtDev/Search Component/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Search Component/index.js b/08 - rmtDev/Search Component/index.js deleted file mode 100644 index e22d115..0000000 --- a/08 - rmtDev/Search Component/index.js +++ /dev/null @@ -1,99 +0,0 @@ -// -- GLOBAL -- -const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -const errorEl = document.querySelector('.error'); -const errorTextEl = document.querySelector('.error__text'); -const jobDetailsEl = document.querySelector('.job-details'); -const jobDetailsContentEl = document.querySelector(".job-details__content"); -const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -const jobListSearchEl = document.querySelector(".job-list--search"); -const numberEl = document.querySelector(".count__number"); -const paginationEl = document.querySelector(".pagination"); -const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -const searchFormEl = document.querySelector(".search"); -const searchInputEl = document.querySelector(".search__input"); -const sortingEl = document.querySelector(".sorting"); -const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -const spinnerSearchEl = document.querySelector(".spinner--search"); -const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// -- SEARCH COMPONENT -- -const submitHandler = event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - errorTextEl.textContent = 'Your search may not contain numbers'; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, 3500); - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - spinnerSearchEl.classList.add('spinner--visible'); - - // fetch search results - fetch(`https://bytegrad.com/course-assets/js/2/api/jobs?search=${searchText}`) - .then(response => { - if (!response.ok) { - console.log('Something went wrong'); - return; - } - - return response.json(); - }) - .then(data => { - // extract job items - const { jobItems } = data; - - // remove spinner - spinnerSearchEl.classList.remove('spinner--visible'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - jobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); - }) - .catch(error => console.log(error)); -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component #2/index.css b/08 - rmtDev/Sorting Component #2/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Sorting Component #2/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Sorting Component #2/index.html b/08 - rmtDev/Sorting Component #2/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Sorting Component #2/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component #2/index.js b/08 - rmtDev/Sorting Component #2/index.js deleted file mode 100644 index b81c001..0000000 --- a/08 - rmtDev/Sorting Component #2/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Search.js'; -import './src/components/Sorting.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component #2/src/common.js b/08 - rmtDev/Sorting Component #2/src/common.js deleted file mode 100644 index e1a8785..0000000 --- a/08 - rmtDev/Sorting Component #2/src/common.js +++ /dev/null @@ -1,42 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; - -// STATE -export const state = { - searchJobItems: [] -}; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// HELPER / UTILITY FUNCTIONS -export const getData = async completeURL => { - const response = await fetch(completeURL); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - return data; -}; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component #2/src/components/Error.js b/08 - rmtDev/Sorting Component #2/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Sorting Component #2/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component #2/src/components/JobDetails.js b/08 - rmtDev/Sorting Component #2/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/Sorting Component #2/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component #2/src/components/JobList.js b/08 - rmtDev/Sorting Component #2/src/components/JobList.js deleted file mode 100644 index 6bb7252..0000000 --- a/08 - rmtDev/Sorting Component #2/src/components/JobList.js +++ /dev/null @@ -1,84 +0,0 @@ -import { - BASE_API_URL, - state, - jobListSearchEl, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = () => { - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // display job items - state.searchJobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component #2/src/components/Search.js b/08 - rmtDev/Sorting Component #2/src/components/Search.js deleted file mode 100644 index 133a652..0000000 --- a/08 - rmtDev/Sorting Component #2/src/components/Search.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - BASE_API_URL, - state, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl, - getData -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - renderSpinner('search'); - - try { - // fetch search results - const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); - - // extract job items - const { jobItems } = data; - - // update state - state.searchJobItems = jobItems; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - renderJobList(); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component #2/src/components/Sorting.js b/08 - rmtDev/Sorting Component #2/src/components/Sorting.js deleted file mode 100644 index a93ed06..0000000 --- a/08 - rmtDev/Sorting Component #2/src/components/Sorting.js +++ /dev/null @@ -1,44 +0,0 @@ -import { - state, - sortingEl, - sortingBtnRecentEl, - sortingBtnRelevantEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.sorting__button'); - - // stop function if no clicked button element - if (!clickedButtonEl) return; - - // check if intention is recent or relevant sorting - const recent = clickedButtonEl.className.includes('--recent') ? true : false; - - // make sorting button look (in)active - if (recent) { - sortingBtnRecentEl.classList.add('sorting__button--active'); - sortingBtnRelevantEl.classList.remove('sorting__button--active'); - } else { - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - } - - // sort job items - // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same - if (recent) { - state.searchJobItems.sort((a, b) => { - return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. - }); - } else { - state.searchJobItems.sort((a, b) => { - return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. - }); - } - - // render job items in list - renderJobList(); -}; - -sortingEl.addEventListener('click', clickHandler); diff --git a/08 - rmtDev/Sorting Component #2/src/components/Spinner.js b/08 - rmtDev/Sorting Component #2/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Sorting Component #2/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component/index.css b/08 - rmtDev/Sorting Component/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Sorting Component/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Sorting Component/index.html b/08 - rmtDev/Sorting Component/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Sorting Component/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component/index.js b/08 - rmtDev/Sorting Component/index.js deleted file mode 100644 index b81c001..0000000 --- a/08 - rmtDev/Sorting Component/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Search.js'; -import './src/components/Sorting.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component/src/common.js b/08 - rmtDev/Sorting Component/src/common.js deleted file mode 100644 index b433421..0000000 --- a/08 - rmtDev/Sorting Component/src/common.js +++ /dev/null @@ -1,37 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// HELPER / UTILITY FUNCTIONS -export const getData = async completeURL => { - const response = await fetch(completeURL); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - return data; -}; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component/src/components/Error.js b/08 - rmtDev/Sorting Component/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Sorting Component/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component/src/components/JobDetails.js b/08 - rmtDev/Sorting Component/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/Sorting Component/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component/src/components/JobList.js b/08 - rmtDev/Sorting Component/src/components/JobList.js deleted file mode 100644 index 40cfa32..0000000 --- a/08 - rmtDev/Sorting Component/src/components/JobList.js +++ /dev/null @@ -1,79 +0,0 @@ -import { - BASE_API_URL, - jobListSearchEl, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = jobItems => { - jobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component/src/components/Search.js b/08 - rmtDev/Sorting Component/src/components/Search.js deleted file mode 100644 index 9874b96..0000000 --- a/08 - rmtDev/Sorting Component/src/components/Search.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - BASE_API_URL, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl, - getData -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - renderSpinner('search'); - - try { - // fetch search results - const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); - - // extract job items - const { jobItems } = data; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - renderJobList(jobItems); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Sorting Component/src/components/Sorting.js b/08 - rmtDev/Sorting Component/src/components/Sorting.js deleted file mode 100644 index 69cf900..0000000 --- a/08 - rmtDev/Sorting Component/src/components/Sorting.js +++ /dev/null @@ -1,27 +0,0 @@ -import { - state, - sortingEl, - sortingBtnRecentEl, - sortingBtnRelevantEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.sorting__button'); - - // stop function if no clicked button element - if (!clickedButtonEl) return; - - // check if intention is recent or relevant sorting - const recent = clickedButtonEl.className.includes('--recent') ? true : false; - - // sort job items - if (recent) { - - } else { - - } -}; - -sortingEl.addEventListener('click', clickHandler); diff --git a/08 - rmtDev/Sorting Component/src/components/Spinner.js b/08 - rmtDev/Sorting Component/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Sorting Component/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/State/index.css b/08 - rmtDev/State/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/State/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/State/index.html b/08 - rmtDev/State/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/State/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/State/index.js b/08 - rmtDev/State/index.js deleted file mode 100644 index b81c001..0000000 --- a/08 - rmtDev/State/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Search.js'; -import './src/components/Sorting.js'; -import './src/components/Spinner.js'; \ No newline at end of file diff --git a/08 - rmtDev/State/src/common.js b/08 - rmtDev/State/src/common.js deleted file mode 100644 index e1a8785..0000000 --- a/08 - rmtDev/State/src/common.js +++ /dev/null @@ -1,42 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; - -// STATE -export const state = { - searchJobItems: [] -}; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// HELPER / UTILITY FUNCTIONS -export const getData = async completeURL => { - const response = await fetch(completeURL); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - return data; -}; \ No newline at end of file diff --git a/08 - rmtDev/State/src/components/Error.js b/08 - rmtDev/State/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/State/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/State/src/components/JobDetails.js b/08 - rmtDev/State/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/State/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/State/src/components/JobList.js b/08 - rmtDev/State/src/components/JobList.js deleted file mode 100644 index 8bf12b8..0000000 --- a/08 - rmtDev/State/src/components/JobList.js +++ /dev/null @@ -1,80 +0,0 @@ -import { - BASE_API_URL, - state, - jobListSearchEl, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = () => { - state.searchJobItems.slice(0, 7).forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListSearchEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job item - document.querySelector('.job-item--active')?.classList.remove('job-item--active'); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/State/src/components/Search.js b/08 - rmtDev/State/src/components/Search.js deleted file mode 100644 index 133a652..0000000 --- a/08 - rmtDev/State/src/components/Search.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - BASE_API_URL, - state, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl, - getData -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // render spinner - renderSpinner('search'); - - try { - // fetch search results - const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); - - // extract job items - const { jobItems } = data; - - // update state - state.searchJobItems = jobItems; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // render job items in search job list - renderJobList(); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/State/src/components/Sorting.js b/08 - rmtDev/State/src/components/Sorting.js deleted file mode 100644 index 61c8a83..0000000 --- a/08 - rmtDev/State/src/components/Sorting.js +++ /dev/null @@ -1,25 +0,0 @@ -import { - sortingEl, - sortingBtnRecentEl, - sortingBtnRelevantEl -} from '../common.js'; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.sorting__button'); - - // stop function if no clicked button element - if (!clickedButtonEl) return; - - // check if intention is recent or relevant sorting - const recent = clickedButtonEl.className.includes('--recent') ? true : false; - - // sort job items - if (recent) { - - } else { - - } -}; - -sortingEl.addEventListener('click', clickHandler); diff --git a/08 - rmtDev/State/src/components/Spinner.js b/08 - rmtDev/State/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/State/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/index.css b/08 - rmtDev/Storage Component/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Storage Component/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Storage Component/index.html b/08 - rmtDev/Storage Component/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Storage Component/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/index.js b/08 - rmtDev/Storage Component/index.js deleted file mode 100644 index e51a258..0000000 --- a/08 - rmtDev/Storage Component/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import './src/components/Bookmarks.js'; -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Pagination.js'; -import './src/components/Router.js'; -import './src/components/Search.js'; -import './src/components/Sorting.js'; -import './src/components/Spinner.js'; -import './src/components/Storage.js'; \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/src/common.js b/08 - rmtDev/Storage Component/src/common.js deleted file mode 100644 index 34e739a..0000000 --- a/08 - rmtDev/Storage Component/src/common.js +++ /dev/null @@ -1,46 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; -export const RESULTS_PER_PAGE = 7; - -// STATE -export const state = { - searchJobItems: [], - bookmarkJobItems: [], - activeJobItem: {}, - currentPage: 1 -}; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// HELPER / UTILITY FUNCTIONS -export const getData = async completeURL => { - const response = await fetch(completeURL); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - return data; -}; \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/src/components/Bookmarks.js b/08 - rmtDev/Storage Component/src/components/Bookmarks.js deleted file mode 100644 index 6e21073..0000000 --- a/08 - rmtDev/Storage Component/src/components/Bookmarks.js +++ /dev/null @@ -1,48 +0,0 @@ -import { - state, - bookmarksBtnEl, - jobDetailsEl, - jobListBookmarksEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const clickHandler = event => { - // don't continue if click was outside bookmark button - if (!event.target.className.includes('bookmark')) return; - - // update state - if (state.bookmarkJobItems.some(bookmarkJobItem => bookmarkJobItem.id === state.activeJobItem.id)) { - state.bookmarkJobItems = state.bookmarkJobItems.filter(bookmarkJobItem => bookmarkJobItem.id !== state.activeJobItem.id); - } else { - state.bookmarkJobItems.push(state.activeJobItem); - } - - // persist data with localstorage - localStorage.setItem('bookmarkJobItems', JSON.stringify(state.bookmarkJobItems)); - - // update bookmark icon - document.querySelector('.job-info__bookmark-icon').classList.toggle('job-info__bookmark-icon--bookmarked'); -}; - -const mouseEnterHandler = () => { - // make bookmarks button look active - bookmarksBtnEl.classList.add('bookmarks-btn--active'); - - // make job list visible - jobListBookmarksEl.classList.add('job-list--visible'); - - // render bookmarks job list - renderJobList('bookmarks'); -}; - -const mouseLeaveHandler = () => { - // make bookmarks button look inactive - bookmarksBtnEl.classList.remove('bookmarks-btn--active'); - - // make job list invisible - jobListBookmarksEl.classList.remove('job-list--visible'); -}; - -jobDetailsEl.addEventListener('click', clickHandler); -bookmarksBtnEl.addEventListener('mouseenter', mouseEnterHandler); -jobListBookmarksEl.addEventListener('mouseleave', mouseLeaveHandler); \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/src/components/Error.js b/08 - rmtDev/Storage Component/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Storage Component/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/src/components/JobDetails.js b/08 - rmtDev/Storage Component/src/components/JobDetails.js deleted file mode 100644 index e66277d..0000000 --- a/08 - rmtDev/Storage Component/src/components/JobDetails.js +++ /dev/null @@ -1,62 +0,0 @@ -import { - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/src/components/JobList.js b/08 - rmtDev/Storage Component/src/components/JobList.js deleted file mode 100644 index 58cdfe9..0000000 --- a/08 - rmtDev/Storage Component/src/components/JobList.js +++ /dev/null @@ -1,105 +0,0 @@ -import { - BASE_API_URL, - RESULTS_PER_PAGE, - state, - jobListSearchEl, - jobListBookmarksEl, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = (whichJobList = 'search') => { - // determine correct selector for job list (search results list or bookmarks list) - const jobListEl = whichJobList === 'search' ? jobListSearchEl : jobListBookmarksEl; - - // remove previous job items - jobListEl.innerHTML = ''; - - // determine the job items that should be rendered - let jobItems; - if (whichJobList === 'search') { - jobItems = state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE); - } else if (whichJobList === 'bookmarks') { - jobItems = state.bookmarkJobItems; - } - - // display job items - jobItems.forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job items - document.querySelectorAll('.job-item--active').forEach(jobItemWithActiveClass => jobItemWithActiveClass.classList.remove('job-item--active')); - - // add active class - jobItemEl.classList.add('job-item--active'); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // update state - const allJobItems = [...state.searchJobItems, ...state.bookmarkJobItems]; - state.activeJobItem = allJobItems.find(jobItem => jobItem.id === +id); - - // add id to url - history.pushState(null, '', `/#${id}`); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); -jobListBookmarksEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/src/components/Pagination.js b/08 - rmtDev/Storage Component/src/components/Pagination.js deleted file mode 100644 index 720f1c7..0000000 --- a/08 - rmtDev/Storage Component/src/components/Pagination.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - RESULTS_PER_PAGE, - state, - paginationEl, - paginationNumberNextEl, - paginationNumberBackEl, - paginationBtnNextEl, - paginationBtnBackEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const renderPaginationButtons = () => { - // display back button if we are on page 2 or further - if (state.currentPage >= 2) { - paginationBtnBackEl.classList.remove('pagination__button--hidden'); - } else { - paginationBtnBackEl.classList.add('pagination__button--hidden'); - } - - // display next button if there are more job items on next page - if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { - paginationBtnNextEl.classList.add('pagination__button--hidden'); - } else { - paginationBtnNextEl.classList.remove('pagination__button--hidden'); - } - - // update page numbers - paginationNumberNextEl.textContent = state.currentPage + 1; - paginationNumberBackEl.textContent = state.currentPage - 1; - - // unfocus ('blur') buttons - paginationBtnNextEl.blur(); - paginationBtnBackEl.blur(); -}; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.pagination__button'); - - // stop function if null - if (!clickedButtonEl) return; - - // check if intention is next or back - const nextPage = clickedButtonEl.className.includes('--next') ? true : false; - - // update state - nextPage ? state.currentPage++ : state.currentPage--; - - // render pagination buttons - renderPaginationButtons(); - - // render job items for that page - renderJobList(); -}; - -paginationEl.addEventListener('click', clickHandler); - -export default renderPaginationButtons; \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/src/components/Router.js b/08 - rmtDev/Storage Component/src/components/Router.js deleted file mode 100644 index b1d0be8..0000000 --- a/08 - rmtDev/Storage Component/src/components/Router.js +++ /dev/null @@ -1,45 +0,0 @@ -import { - BASE_API_URL, - state, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const loadHashChangeHandler = async () => { - // get id from url - const id = window.location.hash.substring(1); - - if (id) { - // remove previous job details content - jobDetailsContentEl.innerHTML = ''; - - // add spinner - renderSpinner('job-details'); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // update state - state.activeJobItem = jobItem; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } - } -}; - -window.addEventListener('DOMContentLoaded', loadHashChangeHandler); -window.addEventListener('hashchange', loadHashChangeHandler); \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/src/components/Search.js b/08 - rmtDev/Storage Component/src/components/Search.js deleted file mode 100644 index e672606..0000000 --- a/08 - rmtDev/Storage Component/src/components/Search.js +++ /dev/null @@ -1,73 +0,0 @@ -import { - BASE_API_URL, - state, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl, - sortingBtnRecentEl, - sortingBtnRelevantEl, - getData -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // reset sorting buttons - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - - // render spinner - renderSpinner('search'); - - try { - // fetch search results - const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); - - // extract job items - const { jobItems } = data; - - // update state - state.searchJobItems = jobItems; - state.currentPage = 1; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in search job list - renderJobList(); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/src/components/Sorting.js b/08 - rmtDev/Storage Component/src/components/Sorting.js deleted file mode 100644 index cd08f17..0000000 --- a/08 - rmtDev/Storage Component/src/components/Sorting.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - state, - sortingEl, - sortingBtnRecentEl, - sortingBtnRelevantEl -} from '../common.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.sorting__button'); - - // stop function if no clicked button element - if (!clickedButtonEl) return; - - // update state (reset to page 1) - state.currentPage = 1; - - // check if intention is recent or relevant sorting - const recent = clickedButtonEl.className.includes('--recent') ? true : false; - - // make sorting button look (in)active - if (recent) { - sortingBtnRecentEl.classList.add('sorting__button--active'); - sortingBtnRelevantEl.classList.remove('sorting__button--active'); - } else { - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - } - - // sort job items - // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same - if (recent) { - state.searchJobItems.sort((a, b) => { - return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. - }); - } else { - state.searchJobItems.sort((a, b) => { - return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. - }); - } - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in list - renderJobList(); -}; - -sortingEl.addEventListener('click', clickHandler); diff --git a/08 - rmtDev/Storage Component/src/components/Spinner.js b/08 - rmtDev/Storage Component/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Storage Component/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Storage Component/src/components/Storage.js b/08 - rmtDev/Storage Component/src/components/Storage.js deleted file mode 100644 index 2bb6fbe..0000000 --- a/08 - rmtDev/Storage Component/src/components/Storage.js +++ /dev/null @@ -1,8 +0,0 @@ -import { - state -} from '../common.js'; - -const storedJobItems = localStorage.getItem('bookmarkJobItems'); -if (storedJobItems) { - state.bookmarkJobItems = JSON.parse(storedJobItems); -} \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/index.css b/08 - rmtDev/Taking Care of Details/index.css deleted file mode 100644 index b624c33..0000000 --- a/08 - rmtDev/Taking Care of Details/index.css +++ /dev/null @@ -1,1130 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -input { - border: initial; - outline: initial; /* create alternative for focus state */ - font: inherit; -} - -/* UTILITIES */ -.u-bold { - font-weight: 700; -} - -/* KEYFRAMES */ -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -@keyframes intro { - 0% { - opacity: 0; - transform: translateY(-10px) scale(0.95); - } - - 100% { - opacity: 1; - transform: translateY(0px) scale(1); - } -} - -/* BASE */ -.body { - background-color: #dee3e9; - color: rgb(22, 24, 28); - min-height: 100vh; - font-family: "Inter", sans-serif; - position: relative; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -/* HEADINGS */ -.first-heading { - font-size: 31px; - font-weight: 400; - color: #fff; - - font-size: 27px; - display: none; -} - -.second-heading { - font-size: 23px; - color: #fff; - font-weight: 500; -} - -.third-heading { - font-size: 13px; - font-weight: 600; -} - -.fourth-heading { - font-size: 16px; - font-weight: 600; - text-transform: capitalize; -} - -/* BACKGROUND */ -.background { - position: absolute; - height: 210px; - width: 100%; - top: 0; - left: 0; - z-index: -2; - overflow: hidden; - background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); - box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); -} - -.background::before { - content: ""; - background-image: linear-gradient( - -180deg, - rgba(0, 0, 0, 0.025) 0, - rgba(0, 0, 0, 0.075) 99% - ); - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.background__pattern { - position: absolute; - top: -25px; - left: 0; - z-index: -1; - user-select: none; - - transform: scale(1.1); -} - -/* HEADER */ -.header { - margin-bottom: 13px; - position: relative; - - margin-bottom: 0; -} - -.header__top { - display: flex; - align-items: center; - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - padding-top: 12px; - position: relative; - justify-content: center; - padding-top: 40px; - - animation: intro 0.3s; -} - -.header__submit-job { - margin-right: auto; - font-size: 12px; - color: rgba(255, 255, 255, 0.75); - text-transform: lowercase; - padding-left: 10px; - transform: translateY(-2px); - cursor: pointer; - - margin-right: 0; -} - -.header__submit-job::before { - content: ""; - display: inline-block; - height: 13px; - width: 2px; - margin-right: 8px; - background-color: rgba(255, 255, 255, 0.25); - transform: translateY(3px); -} - -/* LOGO */ -.logo { - margin-left: -8px; - user-select: none; -} - -.logo__img { - margin-bottom: -5px; -} - -/* BOOKMARKS BTN */ -.bookmarks-btn { - text-transform: lowercase; - font-size: 13px; - color: rgba(255, 255, 255, 0.75); - margin-left: 13px; - padding-left: 13px; - position: relative; - cursor: pointer; - transition: all 0.2s; - height: 32px; -} - -.bookmarks-btn--active, -.bookmarks-btn:hover, -.bookmarks-btn:focus { - color: rgba(255, 255, 255, 1); -} - -.bookmarks-btn--active .bookmarks-btn__icon, -.bookmarks-btn:hover .bookmarks-btn__icon, -.bookmarks-btn:focus .bookmarks-btn__icon { - color: rgba(255, 255, 255, 0.8); -} - -.bookmarks-btn::before { - content: ""; - height: 15px; - width: 2px; - display: block; - position: absolute; - background-color: rgba(255, 255, 255, 0.3); - left: 0px; - top: 50%; - transform: translateY(-50%); -} - -.bookmarks-btn__icon { - font-size: 10px; - margin-left: 2px; - transform: translateY(-1px); - color: rgba(255, 255, 255, 0.6); - transition: all 0.2s; -} - -/* JOB LIST */ -.job-list { - background-color: #fff; - - scrollbar-color: #cacdd0 #ffffff; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.job-list::-webkit-scrollbar { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 4px; -} - -.job-list::-webkit-scrollbar-track { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #ffffff; -} - -.job-list::-webkit-scrollbar-thumb { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #cacdd0; - transition: all 0.2s; -} - -.job-list::-webkit-scrollbar-thumb:hover { - /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #b1b4b8; -} - -.job-list--search { -} - -.job-list--bookmarks { - visibility: hidden; - min-height: 76px; - min-width: 340px; - width: 340px; - border-radius: 4px; - overflow: hidden; - z-index: 10; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - opacity: 0; - transform: scale(0.9) translateX(-50%); - transform-origin: left; - transition: all 0.2s; - pointer-events: none; - - position: fixed; - left: 50%; - top: 82px; -} - -.job-list--bookmarks:hover { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -.job-list--bookmarks::before { - content: 'Nothing bookmarked yet...'; - position: absolute; - top: 49%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; - font-size: 13px; - color: rgb(97, 98, 104); -} - -.job-list--visible { - pointer-events: initial; - visibility: initial; - transform: scale(1) translateX(-50%); - opacity: 1; -} - -/* JOB ITEM */ -.job-item { - padding: 14px 20px; - cursor: pointer; - transition: all 0.2s; - background-color: #fff; -} - -.job-item:not(:nth-child(7)) { - border-bottom: 1px solid #ebeff1; -} - -.job-item:hover { - background-color: #f4f5f7; -} - -.job-item--active { - background-color: #f4f5f7; -} - -.job-item__link { - height: 100%; - width: 100%; - display: flex; -} - -.job-item__badge { - font-size: 13px; - height: 46px; - width: 38px; - background-color: #8dd335; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 600; - margin-right: 13px; -} - -.job-item:nth-child(4n+2) .job-item__badge { - background-color: #3D87F1; -} - -.job-item:nth-child(4n+3) .job-item__badge { - background-color: #D2D631; -} - -.job-item:nth-child(4n+4) .job-item__badge { - background-color: #D96A46; -} - -.job-item__middle { -} - -.job-item__company { - font-size: 12px; - margin-bottom: 2px; - font-style: italic; -} - -.job-item__extras { - display: grid; - grid-template-columns: 65px 72px 65px; - column-gap: 10px; -} - -.job-item__extra { - color: #4d5054; - font-size: 11px; -} - -.job-item__extra-icon { - color: #bec5ce; - font-size: 10px; - margin-right: 1px; -} - -.job-item__right { - margin-left: auto; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -.job-item__bookmark-icon { - font-size: 14px; - cursor: pointer; - color: #d7dbe0; - transition: all 0.2s; -} - -.job-item__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-item__time { - font-size: 10px; - margin-top: 4px; - color: #515459; -} - -/* MAIN */ -.main { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* INTRO */ -.intro { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 33px; - - margin-top: 20px; - row-gap: 20px; -} - -/* SEARCH */ -.search { - position: relative; - animation: intro 0.3s 0.1s backwards; -} - -.search__submit-btn { - cursor: pointer; - position: absolute; - top: 17px; - left: 25px; -} - -.search__icon { - transition: all 0.2s; - color: rgba(0, 0, 0, 0.73); -} - -.search__icon:hover, -.search__icon:focus { - color: rgba(0, 0, 0, 0.93); -} - -.search__input { - height: 56px; - width: 610px; - border-radius: 4px; - padding-left: 55px; - padding-right: 15px; - padding-bottom: 2px; - color: rgba(0, 0, 0, 0.9); - caret-color: rgba(0, 0, 0, 0.5); - background-color: rgba(255, 255, 255, 0.9); - transition: all 0.2s, box-shadow 0.1s; -} - -.search__input::selection { - background-color: rgba(0, 0, 0, 0.25); -} - -.search__input:hover, -.search__input:focus { - background-color: rgba(255, 255, 255, 1); -} - -.search__input:focus { - box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); -} - -.search__input::placeholder { - color: rgba(0, 0, 0, 0.7); - font-weight: 500; - font-size: 15px; -} - -.search__input--invalid { - box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729) -} - -/* CONTAINER */ -.container { - margin: 0 12px; - margin-top: 27px; - height: 616px; - width: 976px; - background-color: #fff; - border-radius: 8px; - display: flex; - border-top-right-radius: 9px; - box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); - margin-top: 40px; - animation: intro 0.3s 0.2s backwards; -} - -/* SEARCH RESULTS */ -.search-results { - width: 340px; - display: flex; - flex-direction: column; - position: relative; - cursor: default; -} - -.search-results__top { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - border-bottom: 1px solid #e8edf0; -} - -/* COUNT */ -.count { - font-size: 12px; -} - -.count__number { -} - -/* SORTING */ -.sorting { -} - -.sorting__icon { - font-size: 11px; - color: #4c4f50; - margin-right: 5px; -} - -.sorting__button { - font-size: 10px; - text-transform: uppercase; - background-color: #e8edf0; - padding: 6px 8px; - border-radius: 3px; - margin-left: 2px; - cursor: pointer; - transition: all 0.2s; -} - -.sorting__button:hover, -.sorting__button:focus { - background-color: #d0d5d8; -} - -.sorting__button--active, -.sorting__button--active:hover, -.sorting__button--active:focus { - color: #fff; - background-color: #3c4041; -} - -/* PAGINATION */ -.pagination { - height: 40px; - margin-top: auto; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px 1px; - border-top: 1px solid #e8edf0; -} - -.pagination__button { - font-size: 10px; - padding: 4px 10px; - border-radius: 500px; - color: #747c82; - background-color: #eceff2; - cursor: pointer; - transition: all 0.2s, visibility 0s; -} - -.pagination__button:hover, -.pagination__button:focus { - background-color: #dde2e6; -} - -.pagination__button--hidden { - visibility: hidden; -} - -.pagination__button--back { -} - -.pagination__button--next { -} - -.pagination__number { - font-weight: 500; -} - -.pagination__number--back { -} - -.pagination__number--next { -} - -.pagination__icon { - font-size: 8px; - color: #9fa6b0; -} - -/* JOB DETAILS */ -.job-details { - flex: 1; - background-color: #eff2f5; - position: relative; - border-top-right-radius: 12px; - border-bottom-right-radius: 8px; -} - -.job-details__start-view { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.job-details__start-text { - opacity: 0.55; - color: #24292d; - font-size: 14px; - width: 275px; - text-align: center; -} - -.job-details__start-text--big { - color: #0d1114; - font-size: 18px; - font-weight: 600; - margin-bottom: 10px; -} - -.job-details__content { - height: 100%; -} - -.job-details__cover-img { - width: 100%; - height: 174px; - position: absolute; - z-index: 0; - top: 0; - object-fit: cover; - border-top-right-radius: 8px; - user-select: none; -} - -.job-details__other { -} - -.job-details__footer { - margin-left: 42px; - margin-right: 42px; - margin-top: 33px; - padding-top: 13px; - border-top: 1px solid #dce2e8; -} - -.job-details__footer-text { - color: #858b8f; - font-size: 10px; -} - -/* APPLY BTN */ -.apply-btn { - position: absolute; - background-color: #2671dd; - z-index: 2; - color: rgba(255, 255, 255, 0.92); - font-size: 11px; - padding: 5px 10px 6px; - border-radius: 3px; - top: 12px; - right: 12px; - cursor: pointer; - text-transform: uppercase; - transition: all 0.2s; - display: flex; - align-items: center; -} - -.apply-btn:hover, -.apply-btn:focus { - background-color: #1d60bd; - color: rgba(255, 255, 255, 1); -} - -.apply-btn__icon { - color: rgba(255, 255, 255, 0.65); - font-size: 8px; - margin-left: 4px; - margin-top: -1px; -} - -/* JOB INFO */ -.job-info { - position: relative; - z-index: 1; - margin-bottom: 40px; - display: flex; - column-gap: 16px; - padding-top: 120px; -} - -.job-info::before { - content: ""; - position: absolute; - width: 100%; - height: 174px; - top: 0; - left: 0; - background-image: linear-gradient( - to top, - rgba(0, 0, 0, 0.7), - rgba(0, 0, 0, 0.15) - ); - z-index: -1; - border-top-right-radius: 8px; -} - -.job-info__left { - padding-left: 42px; -} - -.job-info__right { - padding-right: 42px; -} - -.job-info__badge { - width: 55px; - height: 70px; - background-color: #d0d335; - border-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - font-weight: 600; - margin-bottom: 13px; -} - -.job-info__below-badge { - display: flex; - justify-content: space-between; -} - -.job-info__time { - font-size: 12px; - transform: translateY(1px); - color: #494d4f; -} - -.job-info__bookmark-btn { - cursor: pointer; -} - -.job-info__bookmark-btn:hover .job-info__bookmark-icon { - color: #2671dd; -} - -.job-info__bookmark-icon { - color: #d7dbe0; - transition: all 0.2s; - font-size: 18px; -} - -.job-info__bookmark-icon--bookmarked { - color: #2671dd; -} - -.job-info__company { - font-size: 14px; - font-style: italic; - color: rgba(255, 255, 255, 0.8); -} - -.job-info__description { - font-size: 14px; - margin-top: 18px; - margin-bottom: 12px; - line-height: 1.4; -} - -.job-info__extras { - display: flex; - column-gap: 35px; -} - -.job-info__extra { - font-size: 12px; - display: flex; - align-items: center; -} - -.job-info__extra-icon { - height: 23px; - width: 23px; - border-radius: 50%; - background-color: #e4e9ed; - display: flex; - justify-content: center; - align-items: center; - font-size: 9px; - margin-right: 8px; - color: #a1a8b0; -} - -/* QUALIFICATIONS */ -.qualifications { - display: flex; - padding-left: 42px; - padding-right: 42px; - margin-bottom: 30px; -} - -.qualifications__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.qualifications__left { - margin-right: 35px; -} - -.qualifications__list { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.qualifications__item { - font-size: 13px; - background-color: #e6ebee; - border-radius: 2px; - padding: 6px 10px; - color: #494d4f; -} - -/* REVIEWS */ -.reviews { - display: flex; - padding-left: 42px; - padding-right: 42px; -} - -.reviews__sub-text { - font-size: 13px; - width: 157px; - margin-top: 3px; -} - -.reviews__left { - margin-right: 35px; -} - -.reviews__list { - flex: 1; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto auto; - column-gap: 20px; - row-gap: 20px; -} - -.reviews__item { - font-size: 13px; - font-style: italic; - color: #494d4f; - position: relative; - transform-style: preserve-3d; -} - -.reviews__item::before { - content: '“'; - position: absolute; - font-size: 50px; - top: -15px; - left: -10px; - color: #d2d7db; - transform: translateZ(-1px); -} - -/* FOOTER */ -.footer { - max-width: 1000px; - padding: 0 12px; - margin: 0 auto; - margin-top: 15px; - display: flex; - align-items: center; - justify-content: space-between; - color: #a4a9ac; -} - -/* COPYRIGHT */ -.copyright { - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - line-height: 1.2; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - margin-right: 4px; - margin-left: 2px; - color: #aeb3b6; -} - -/* JOBS AVAILABLE */ -.jobs-available { - font-size: 11px; - align-self: flex-start; -} - -/* SPINNER */ -.spinner { - position: absolute; - border-radius: 50%; - animation: spinner 1s infinite linear; - visibility: hidden; -} - -.spinner--search { - left: 50%; - top: 18%; - width: 85px; - height: 85px; - border-top: 5px solid #e2e7e9; - border-right: 5px solid #e2e7e9; - border-bottom: 5px solid #e2e7e9; - border-left: 5px solid #ccd1d3; -} - -.spinner--job-details { - left: 50%; - top: 40%; - width: 105px; - height: 105px; - border-top: 6px solid #d5d9db; - border-right: 6px solid #d5d9db; - border-bottom: 6px solid #d5d9db; - border-left: 6px solid #bbc0c2; -} - -.spinner--visible { - visibility: visible; -} - -/* ERROR */ -.error { - background: #fff; - padding: 14px 20px; - width: 280px; - min-height: 46px; - border-radius: 3px; - box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); - position: absolute; - top: 15px; - right: 15px; - display: flex; - opacity: 0; - visibility: hidden; - transform: translateY(-120px); - transition: all 0.3s; -} - -.error--visible { - opacity: 1; - transform: initial; - visibility: initial; -} - -.error__icon { - font-size: 16px; - color: rgb(123, 64, 64); - margin-top: 2px; -} - -.error__right { - margin-left: 10px; -} - -.error__title { - text-transform: uppercase; - font-size: 12px; - margin-bottom: 1px; - font-weight: 500; -} - -.error__text { - font-size: 13px; - color: rgb(97, 98, 104); -} - -/* MEDIA QUERIES */ -@media (max-height: 925px) and (min-width: 1010px) { - .body { - padding-bottom: 50px; - } -} - -@media (max-width: 1179px) { - .job-list--bookmarks { - right: 0; - } -} - -@media (max-width: 1009px) { - .body { - padding: 0 12px; - padding-bottom: 50px; - } - - .header__top { - padding-left: 0; - padding-right: 0; - max-width: 800px; - } - - .container { - flex-direction: column; - height: initial; - width: 100%; - max-width: 800px; - border-radius: 8px; - overflow: hidden; - } - - .search-results { - width: 100%; - } - - .job-details { - display: none; - } - - .footer { - max-width: 800px; - padding-left: 0; - padding-right: 0; - } -} - -@media (max-width: 660px) { - .intro { - width: 100%; - } - - .search { - width: 100%; - } - - .search__input { - width: 100%; - } - - .footer { - justify-content: center; - } - - .copyright { - text-align: center; - } - - .jobs-available { - margin-left: 15px; - text-align: right; - display: none; - } - - .intro { - row-gap: 25px; - } - - .first-heading { - text-align: center; - max-width: 400px; - } -} - -@media (max-width: 370px) { - .job-list--bookmarks { - width: 93vw; - min-width: initial; - } - - .job-item { - width: 100%; - } - - .job-item__badge { - display: none; - } - - .error { - width: 93vw; - right: initial; - left: 50%; - transform: translateX(-50%); - } -} diff --git a/08 - rmtDev/Taking Care of Details/index.html b/08 - rmtDev/Taking Care of Details/index.html deleted file mode 100644 index d930bd5..0000000 --- a/08 - rmtDev/Taking Care of Details/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/index.js b/08 - rmtDev/Taking Care of Details/index.js deleted file mode 100644 index e51a258..0000000 --- a/08 - rmtDev/Taking Care of Details/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import './src/components/Bookmarks.js'; -import './src/components/Error.js'; -import './src/components/JobDetails.js'; -import './src/components/JobList.js'; -import './src/components/Pagination.js'; -import './src/components/Router.js'; -import './src/components/Search.js'; -import './src/components/Sorting.js'; -import './src/components/Spinner.js'; -import './src/components/Storage.js'; \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/src/common.js b/08 - rmtDev/Taking Care of Details/src/common.js deleted file mode 100644 index 34e739a..0000000 --- a/08 - rmtDev/Taking Care of Details/src/common.js +++ /dev/null @@ -1,46 +0,0 @@ -// CONSTANTS -export const BASE_API_URL = 'https://bytegrad.com/course-assets/js/2/api'; -export const DEFAULT_DISPLAY_TIME = 3500; -export const RESULTS_PER_PAGE = 7; - -// STATE -export const state = { - searchJobItems: [], - bookmarkJobItems: [], - activeJobItem: {}, - currentPage: 1 -}; - -// SELECTORS -export const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -export const errorEl = document.querySelector('.error'); -export const errorTextEl = document.querySelector('.error__text'); -export const jobDetailsEl = document.querySelector('.job-details'); -export const jobDetailsContentEl = document.querySelector(".job-details__content"); -export const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -export const jobListSearchEl = document.querySelector(".job-list--search"); -export const numberEl = document.querySelector(".count__number"); -export const paginationEl = document.querySelector(".pagination"); -export const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -export const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -export const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -export const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -export const searchFormEl = document.querySelector(".search"); -export const searchInputEl = document.querySelector(".search__input"); -export const sortingEl = document.querySelector(".sorting"); -export const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -export const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -export const spinnerSearchEl = document.querySelector(".spinner--search"); -export const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -// HELPER / UTILITY FUNCTIONS -export const getData = async completeURL => { - const response = await fetch(completeURL); - const data = await response.json(); - - if (!response.ok) { // 4xx, 5xx status code - throw new Error(data.description); - } - - return data; -}; \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/src/components/Bookmarks.js b/08 - rmtDev/Taking Care of Details/src/components/Bookmarks.js deleted file mode 100644 index 0e4dfa4..0000000 --- a/08 - rmtDev/Taking Care of Details/src/components/Bookmarks.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - state, - bookmarksBtnEl, - jobDetailsEl, - jobListBookmarksEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const clickHandler = event => { - // don't continue if click was outside bookmark button - if (!event.target.className.includes('bookmark')) return; - - // update state - if (state.bookmarkJobItems.some(bookmarkJobItem => bookmarkJobItem.id === state.activeJobItem.id)) { - state.bookmarkJobItems = state.bookmarkJobItems.filter(bookmarkJobItem => bookmarkJobItem.id !== state.activeJobItem.id); - } else { - state.bookmarkJobItems.push(state.activeJobItem); - } - - // persist data with localstorage - localStorage.setItem('bookmarkJobItems', JSON.stringify(state.bookmarkJobItems)); - - // update bookmark icon - document.querySelector('.job-info__bookmark-icon').classList.toggle('job-info__bookmark-icon--bookmarked'); - - // render search job list - renderJobList(); -}; - -const mouseEnterHandler = () => { - // make bookmarks button look active - bookmarksBtnEl.classList.add('bookmarks-btn--active'); - - // make job list visible - jobListBookmarksEl.classList.add('job-list--visible'); - - // render bookmarks job list - renderJobList('bookmarks'); -}; - -const mouseLeaveHandler = () => { - // make bookmarks button look inactive - bookmarksBtnEl.classList.remove('bookmarks-btn--active'); - - // make job list invisible - jobListBookmarksEl.classList.remove('job-list--visible'); -}; - -jobDetailsEl.addEventListener('click', clickHandler); -bookmarksBtnEl.addEventListener('mouseenter', mouseEnterHandler); -jobListBookmarksEl.addEventListener('mouseleave', mouseLeaveHandler); \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/src/components/Error.js b/08 - rmtDev/Taking Care of Details/src/components/Error.js deleted file mode 100644 index 80177ed..0000000 --- a/08 - rmtDev/Taking Care of Details/src/components/Error.js +++ /dev/null @@ -1,15 +0,0 @@ -import { - DEFAULT_DISPLAY_TIME, - errorTextEl, - errorEl -} from '../common.js'; - -const renderError = (message = 'Something went wrong') => { - errorTextEl.textContent = message; - errorEl.classList.add('error--visible'); - setTimeout(() => { - errorEl.classList.remove('error--visible'); - }, DEFAULT_DISPLAY_TIME); -}; - -export default renderError; \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/src/components/JobDetails.js b/08 - rmtDev/Taking Care of Details/src/components/JobDetails.js deleted file mode 100644 index 9c66267..0000000 --- a/08 - rmtDev/Taking Care of Details/src/components/JobDetails.js +++ /dev/null @@ -1,63 +0,0 @@ -import { - state, - jobDetailsContentEl -} from '../common.js'; - -const renderJobDetails = jobItem => { - const jobDetailsHTML = ` - # - - Apply - -
    -
    -
    ${jobItem.badgeLetters}
    -
    - - -
    -
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -

    ${jobItem.description}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      - ${jobItem.qualifications.map(qualificationText => `
    • ${qualificationText}
    • `).join('')} -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      - ${jobItem.reviews.map(reviewText => `
    • ${reviewText}
    • `).join('')} -
    -
    -
    - -
    - -
    - `; - jobDetailsContentEl.innerHTML = jobDetailsHTML; -}; - -export default renderJobDetails; \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/src/components/JobList.js b/08 - rmtDev/Taking Care of Details/src/components/JobList.js deleted file mode 100644 index afd0376..0000000 --- a/08 - rmtDev/Taking Care of Details/src/components/JobList.js +++ /dev/null @@ -1,105 +0,0 @@ -import { - BASE_API_URL, - RESULTS_PER_PAGE, - state, - jobListSearchEl, - jobListBookmarksEl, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; - -const renderJobList = (whichJobList = 'search') => { - // determine correct selector for job list (search results list or bookmarks list) - const jobListEl = whichJobList === 'search' ? jobListSearchEl : jobListBookmarksEl; - - // remove previous job items - jobListEl.innerHTML = ''; - - // determine the job items that should be rendered - let jobItems; - if (whichJobList === 'search') { - jobItems = state.searchJobItems.slice(state.currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, state.currentPage * RESULTS_PER_PAGE); - } else if (whichJobList === 'bookmarks') { - jobItems = state.bookmarkJobItems; - } - - // display job items - jobItems.forEach(jobItem => { - const newJobItemHTML = ` -
  • - -
    ${jobItem.badgeLetters}
    -
    -

    ${jobItem.title}

    -

    ${jobItem.company}

    -
    -

    ${jobItem.duration}

    -

    ${jobItem.salary}

    -

    ${jobItem.location}

    -
    -
    -
    - - -
    -
    -
  • - `; - jobListEl.insertAdjacentHTML('beforeend', newJobItemHTML); - }); -}; - -const clickHandler = async event => { - // prevent default behavior (navigation) - event.preventDefault(); - - // get clicked job item element - const jobItemEl = event.target.closest('.job-item'); - - // remove the active class from previously active job items - document.querySelectorAll('.job-item--active').forEach(jobItemWithActiveClass => jobItemWithActiveClass.classList.remove('job-item--active')); - - // empty the job details section - jobDetailsContentEl.innerHTML = ''; - - // render spinner - renderSpinner('job-details'); - - // get the id - const id = jobItemEl.children[0].getAttribute('href'); - - // update state - const allJobItems = [...state.searchJobItems, ...state.bookmarkJobItems]; - state.activeJobItem = allJobItems.find(jobItem => jobItem.id === +id); - - // render search job list - renderJobList(); - - // add id to url - history.pushState(null, '', `/#${id}`); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } -}; - -jobListSearchEl.addEventListener('click', clickHandler); -jobListBookmarksEl.addEventListener('click', clickHandler); - -export default renderJobList; \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/src/components/Pagination.js b/08 - rmtDev/Taking Care of Details/src/components/Pagination.js deleted file mode 100644 index 720f1c7..0000000 --- a/08 - rmtDev/Taking Care of Details/src/components/Pagination.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - RESULTS_PER_PAGE, - state, - paginationEl, - paginationNumberNextEl, - paginationNumberBackEl, - paginationBtnNextEl, - paginationBtnBackEl -} from '../common.js'; -import renderJobList from './JobList.js'; - -const renderPaginationButtons = () => { - // display back button if we are on page 2 or further - if (state.currentPage >= 2) { - paginationBtnBackEl.classList.remove('pagination__button--hidden'); - } else { - paginationBtnBackEl.classList.add('pagination__button--hidden'); - } - - // display next button if there are more job items on next page - if ((state.searchJobItems.length - state.currentPage * RESULTS_PER_PAGE) <= 0) { - paginationBtnNextEl.classList.add('pagination__button--hidden'); - } else { - paginationBtnNextEl.classList.remove('pagination__button--hidden'); - } - - // update page numbers - paginationNumberNextEl.textContent = state.currentPage + 1; - paginationNumberBackEl.textContent = state.currentPage - 1; - - // unfocus ('blur') buttons - paginationBtnNextEl.blur(); - paginationBtnBackEl.blur(); -}; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.pagination__button'); - - // stop function if null - if (!clickedButtonEl) return; - - // check if intention is next or back - const nextPage = clickedButtonEl.className.includes('--next') ? true : false; - - // update state - nextPage ? state.currentPage++ : state.currentPage--; - - // render pagination buttons - renderPaginationButtons(); - - // render job items for that page - renderJobList(); -}; - -paginationEl.addEventListener('click', clickHandler); - -export default renderPaginationButtons; \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/src/components/Router.js b/08 - rmtDev/Taking Care of Details/src/components/Router.js deleted file mode 100644 index 62bd46e..0000000 --- a/08 - rmtDev/Taking Care of Details/src/components/Router.js +++ /dev/null @@ -1,52 +0,0 @@ -import { - BASE_API_URL, - state, - jobDetailsContentEl, - getData -} from '../common.js'; -import renderSpinner from './Spinner.js'; -import renderJobDetails from './JobDetails.js'; -import renderError from './Error.js'; -import renderJobList from './JobList.js'; - -const loadHashChangeHandler = async () => { - // get id from url - const id = window.location.hash.substring(1); - - if (id) { - // remove the active class from previously active job items - document.querySelectorAll('.job-item--active').forEach(jobItemWithActiveClass => jobItemWithActiveClass.classList.remove('job-item--active')); - - // remove previous job details content - jobDetailsContentEl.innerHTML = ''; - - // add spinner - renderSpinner('job-details'); - - try { - // fetch job item data - const data = await getData(`${BASE_API_URL}/jobs/${id}`); - - // extract job item - const { jobItem } = data; - - // update state - state.activeJobItem = jobItem; - - // render search job list - renderJobList(); - - // remove spinner - renderSpinner('job-details'); - - // render job details - renderJobDetails(jobItem); - } catch (error) { - renderSpinner('job-details'); - renderError(error.message); - } - } -}; - -window.addEventListener('DOMContentLoaded', loadHashChangeHandler); -window.addEventListener('hashchange', loadHashChangeHandler); \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/src/components/Search.js b/08 - rmtDev/Taking Care of Details/src/components/Search.js deleted file mode 100644 index e672606..0000000 --- a/08 - rmtDev/Taking Care of Details/src/components/Search.js +++ /dev/null @@ -1,73 +0,0 @@ -import { - BASE_API_URL, - state, - searchInputEl, - searchFormEl, - jobListSearchEl, - numberEl, - sortingBtnRecentEl, - sortingBtnRelevantEl, - getData -} from '../common.js'; -import renderError from './Error.js'; -import renderSpinner from './Spinner.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const submitHandler = async event => { - // prevent default behavior - event.preventDefault(); - - // get search text - const searchText = searchInputEl.value; - - // validation (regular expression example) - const forbiddenPattern = /[0-9]/; - const patternMatch = forbiddenPattern.test(searchText); - if (patternMatch) { - renderError('Your search may not contain numbers'); - return; - } - - // blur input - searchInputEl.blur(); - - // remove previous job items - jobListSearchEl.innerHTML = ''; - - // reset sorting buttons - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - - // render spinner - renderSpinner('search'); - - try { - // fetch search results - const data = await getData(`${BASE_API_URL}/jobs?search=${searchText}`); - - // extract job items - const { jobItems } = data; - - // update state - state.searchJobItems = jobItems; - state.currentPage = 1; - - // remove spinner - renderSpinner('search'); - - // render number of results - numberEl.textContent = jobItems.length; - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in search job list - renderJobList(); - } catch (error) { - renderSpinner('search'); - renderError(error.message); - } -}; - -searchFormEl.addEventListener('submit', submitHandler); \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/src/components/Sorting.js b/08 - rmtDev/Taking Care of Details/src/components/Sorting.js deleted file mode 100644 index cd08f17..0000000 --- a/08 - rmtDev/Taking Care of Details/src/components/Sorting.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - state, - sortingEl, - sortingBtnRecentEl, - sortingBtnRelevantEl -} from '../common.js'; -import renderJobList from './JobList.js'; -import renderPaginationButtons from './Pagination.js'; - -const clickHandler = event => { - // get clicked button element - const clickedButtonEl = event.target.closest('.sorting__button'); - - // stop function if no clicked button element - if (!clickedButtonEl) return; - - // update state (reset to page 1) - state.currentPage = 1; - - // check if intention is recent or relevant sorting - const recent = clickedButtonEl.className.includes('--recent') ? true : false; - - // make sorting button look (in)active - if (recent) { - sortingBtnRecentEl.classList.add('sorting__button--active'); - sortingBtnRelevantEl.classList.remove('sorting__button--active'); - } else { - sortingBtnRecentEl.classList.remove('sorting__button--active'); - sortingBtnRelevantEl.classList.add('sorting__button--active'); - } - - // sort job items - // how [].sort works: return positive number to sort b higher than a, return negative number to sort a higher than b, return 0 to stay same - if (recent) { - state.searchJobItems.sort((a, b) => { - return a.daysAgo - b.daysAgo; // e.g. if a.daysAgo = 10 and b.daysAgo = 5, then b is more recent. b should be sorted higher than a. return a positive number. - }); - } else { - state.searchJobItems.sort((a, b) => { - return b.relevanceScore - a.relevanceScore; // e.g. if a.relevanceScore = 94 and b.relevanceScore = 78, then a is more relevant. a should be sorted higher than b. return a negative number. - }); - } - - // reset pagination buttons - renderPaginationButtons(); - - // render job items in list - renderJobList(); -}; - -sortingEl.addEventListener('click', clickHandler); diff --git a/08 - rmtDev/Taking Care of Details/src/components/Spinner.js b/08 - rmtDev/Taking Care of Details/src/components/Spinner.js deleted file mode 100644 index cb25d4f..0000000 --- a/08 - rmtDev/Taking Care of Details/src/components/Spinner.js +++ /dev/null @@ -1,11 +0,0 @@ -import { - spinnerSearchEl, - spinnerJobDetailsEl -} from '../common.js'; - -const renderSpinner = whichSpinner => { - const spinnerEl = whichSpinner === 'search' ? spinnerSearchEl : spinnerJobDetailsEl; - spinnerEl.classList.toggle('spinner--visible'); -}; - -export default renderSpinner; \ No newline at end of file diff --git a/08 - rmtDev/Taking Care of Details/src/components/Storage.js b/08 - rmtDev/Taking Care of Details/src/components/Storage.js deleted file mode 100644 index 2bb6fbe..0000000 --- a/08 - rmtDev/Taking Care of Details/src/components/Storage.js +++ /dev/null @@ -1,8 +0,0 @@ -import { - state -} from '../common.js'; - -const storedJobItems = localStorage.getItem('bookmarkJobItems'); -if (storedJobItems) { - state.bookmarkJobItems = JSON.parse(storedJobItems); -} \ No newline at end of file diff --git a/08 - rmtDev/resources.txt b/08 - rmtDev/resources.txt deleted file mode 100644 index a7e7bd4..0000000 --- a/08 - rmtDev/resources.txt +++ /dev/null @@ -1,101 +0,0 @@ -####### Selectors - -const bookmarksBtnEl = document.querySelector('.bookmarks-btn'); -const errorEl = document.querySelector('.error'); -const errorTextEl = document.querySelector('.error__text'); -const jobDetailsEl = document.querySelector('.job-details'); -const jobDetailsContentEl = document.querySelector(".job-details__content"); -const jobListBookmarksEl = document.querySelector('.job-list--bookmarks'); -const jobListSearchEl = document.querySelector(".job-list--search"); -const numberEl = document.querySelector(".count__number"); -const paginationEl = document.querySelector(".pagination"); -const paginationBtnNextEl = document.querySelector(".pagination__button--next"); -const paginationBtnBackEl = document.querySelector(".pagination__button--back"); -const paginationNumberNextEl = document.querySelector(".pagination__number--next"); -const paginationNumberBackEl = document.querySelector(".pagination__number--back"); -const searchFormEl = document.querySelector(".search"); -const searchInputEl = document.querySelector(".search__input"); -const sortingEl = document.querySelector(".sorting"); -const sortingBtnRelevantEl = document.querySelector(".sorting__button--relevant"); -const sortingBtnRecentEl = document.querySelector(".sorting__button--recent"); -const spinnerSearchEl = document.querySelector(".spinner--search"); -const spinnerJobDetailsEl = document.querySelector(".spinner--job-details"); - -####### fetch URL - -https://bytegrad.com/course-assets/js/2/api/jobs - -####### Job Item HTML Template - -
  • - -
    LO
    -
    -

    Full-Stack Developer

    -

    LakeOperations

    -
    -

    Full-Time

    -

    $80,000+

    -

    Global

    -
    -
    -
    - - -
    -
    -
  • - -####### Job Details HTML Template - -# - -Apply - -
    -
    -
    VN
    -
    - - -
    -
    -
    -

    JS Developer

    -

    Verso Networks

    -

    Developers are responsible for developing and designing front end web architecture, ensuring the responsiveness of applications, and working alongside graphic designers for web design features, among other duties.

    -
    -

    Full-Time

    -

    $80,000+

    -

    Global

    -
    -
    -
    - -
    -
    -
    -

    Qualifications

    -

    Other qualifications may apply

    -
    -
      -
    • Node.js
    • -
    -
    - -
    -
    -

    Company reviews

    -

    Recent things people are saying

    -
    -
      -
    • Only job I liked going to.
    • -
    -
    -
    - -
    - -
    \ No newline at end of file diff --git a/09 - Supplemental/Formatting in JS/babel.config.js b/09 - Supplemental/Formatting in JS/babel.config.js deleted file mode 100644 index 9c22988..0000000 --- a/09 - Supplemental/Formatting in JS/babel.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - presets: [ - [ - '@babel/preset-env', - { - useBuiltIns: 'usage', - corejs: '3.0' - } - ] - ] -}; \ No newline at end of file diff --git a/09 - Supplemental/Formatting in JS/dist/index.html b/09 - Supplemental/Formatting in JS/dist/index.html deleted file mode 100644 index 217a364..0000000 --- a/09 - Supplemental/Formatting in JS/dist/index.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/09 - Supplemental/Formatting in JS/dist/main.css b/09 - Supplemental/Formatting in JS/dist/main.css deleted file mode 100644 index 56d663d..0000000 --- a/09 - Supplemental/Formatting in JS/dist/main.css +++ /dev/null @@ -1 +0,0 @@ -*,:after,:before{box-sizing:border-box;margin:0;padding:0}ol,ul{list-style:none}a{color:inherit;text-decoration:initial}button{background-color:initial}button,input{border:initial;font:inherit;outline:initial}.u-bold{font-weight:700}@-webkit-keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@keyframes spinner{0%{-webkit-transform:translateX(-50%) rotate(0deg);transform:translateX(-50%) rotate(0deg)}to{-webkit-transform:translateX(-50%) rotate(1turn);transform:translateX(-50%) rotate(1turn)}}@-webkit-keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}@keyframes intro{0%{opacity:0;-webkit-transform:translateY(-.625rem) scale(.95);transform:translateY(-.625rem) scale(.95)}to{opacity:1;-webkit-transform:translateY(0) scale(1);transform:translateY(0) scale(1)}}.body{background-color:#dee3e9;color:#16181c;font-family:Inter,sans-serif;min-height:100vh;position:relative;scrollbar-width:none}.body::-webkit-scrollbar{display:none}.first-heading{color:#fff;display:none;font-size:1.9375rem;font-size:1.6875rem;font-weight:400}.second-heading{color:#fff;font-size:1.4375rem;font-weight:500}.third-heading{font-size:.8125rem;font-weight:600}.fourth-heading{font-size:1rem;font-weight:600;text-transform:capitalize}.background{background-image:linear-gradient(125deg,#1f74f1 -10%,#0850b9);box-shadow:0 .125rem .1875rem rgba(0,0,0,.1);height:13.125rem;left:0;overflow:hidden;position:absolute;top:0;width:100%;z-index:-2}.background:before{background-image:linear-gradient(-180deg,rgba(0,0,0,.025),rgba(0,0,0,.075) 99%);bottom:0;content:"";left:0;position:absolute;right:0;top:0}.background__pattern{left:0;position:absolute;top:-1.5625rem;-webkit-transform:scale(1.1);transform:scale(1.1);-webkit-user-select:none;-ms-user-select:none;user-select:none;z-index:-1}.header{margin-bottom:0;position:relative}.header__top{align-items:center;-webkit-animation:intro .3s;animation:intro .3s;display:flex;justify-content:center;margin:0 auto;max-width:62.5rem;padding:2.5rem .75rem 0;position:relative}.header__submit-job{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.75rem;margin-right:0;padding-left:.625rem;text-transform:lowercase;-webkit-transform:translateY(-.125rem);transform:translateY(-.125rem)}.header__submit-job:before{background-color:hsla(0,0%,100%,.25);content:"";display:inline-block;height:.8125rem;margin-right:.5rem;-webkit-transform:translateY(.1875rem);transform:translateY(.1875rem);width:.125rem}.logo{margin-left:-.5rem;-webkit-user-select:none;-ms-user-select:none;user-select:none}.logo__img{margin-bottom:-.3125rem}.bookmarks-btn{color:hsla(0,0%,100%,.75);cursor:pointer;font-size:.8125rem;height:2rem;margin-left:.8125rem;padding-left:.8125rem;position:relative;text-transform:lowercase;transition:all .2s}.bookmarks-btn--active,.bookmarks-btn:focus,.bookmarks-btn:hover{color:#fff}.bookmarks-btn--active .bookmarks-btn__icon,.bookmarks-btn:focus .bookmarks-btn__icon,.bookmarks-btn:hover .bookmarks-btn__icon{color:hsla(0,0%,100%,.8)}.bookmarks-btn:before{background-color:hsla(0,0%,100%,.3);content:"";display:block;height:.9375rem;left:0;position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);width:.125rem}.bookmarks-btn__icon{color:hsla(0,0%,100%,.6);font-size:.625rem;margin-left:.125rem;-webkit-transform:translateY(-.0625rem);transform:translateY(-.0625rem);transition:all .2s}.job-list{background-color:#fff;scrollbar-color:#cacdd0 #fff;scrollbar-width:thin}.job-list::-webkit-scrollbar{width:.25rem}.job-list::-webkit-scrollbar-track{background-color:#fff}.job-list::-webkit-scrollbar-thumb{background-color:#cacdd0;-webkit-transition:all .2s;transition:all .2s}.job-list::-webkit-scrollbar-thumb:hover{background-color:#b1b4b8}.job-list--bookmarks{border-radius:.25rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);left:50%;min-height:4.75rem;min-width:21.25rem;opacity:0;overflow:hidden;pointer-events:none;position:fixed;top:5.125rem;-webkit-transform:scale(.9) translateX(-50%);transform:scale(.9) translateX(-50%);-webkit-transform-origin:left;transform-origin:left;transition:all .2s;visibility:hidden;width:21.25rem;z-index:10}.job-list--bookmarks:hover{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-list--bookmarks:before{color:#616268;content:"Nothing bookmarked yet...";font-size:.8125rem;left:50%;position:absolute;top:49%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);z-index:-1}.job-list--visible{opacity:1;pointer-events:auto;-webkit-transform:scale(1) translateX(-50%);transform:scale(1) translateX(-50%);visibility:initial}.job-item{background-color:#fff;cursor:pointer;padding:.875rem 1.25rem;transition:all .2s}.job-item:not(:nth-child(7)){border-bottom:.0625rem solid #ebeff1}.job-item--active,.job-item:hover{background-color:#f4f5f7}.job-item__link{display:flex;height:100%;width:100%}.job-item__badge{align-items:center;background-color:#8dd335;border-radius:.3125rem;display:flex;font-size:.8125rem;font-weight:600;height:2.875rem;justify-content:center;margin-right:.8125rem;width:2.375rem}.job-item:nth-child(4n+2) .job-item__badge{background-color:#3d87f1}.job-item:nth-child(4n+3) .job-item__badge{background-color:#d2d631}.job-item:nth-child(4n+4) .job-item__badge{background-color:#d96a46}.job-item__company{font-size:.75rem;font-style:italic;margin-bottom:.125rem}.job-item__extras{-webkit-column-gap:.625rem;column-gap:.625rem;display:grid;grid-template-columns:4.0625rem 4.5rem 4.0625rem}.job-item__extra{color:#4d5054;font-size:.6875rem}.job-item__extra-icon{color:#bec5ce;font-size:.625rem;margin-right:.0625rem}.job-item__right{align-items:flex-end;display:flex;flex-direction:column;margin-left:auto}.job-item__bookmark-icon{color:#d7dbe0;cursor:pointer;font-size:.875rem;transition:all .2s}.job-item__bookmark-icon--bookmarked{color:#2671dd}.job-item__time{color:#515459;font-size:.625rem;margin-top:.25rem}.main{justify-content:center}.intro,.main{align-items:center;display:flex;flex-direction:column}.intro{margin-top:1.25rem;row-gap:2.0625rem;row-gap:1.25rem}.search{-webkit-animation:intro .3s .1s backwards;animation:intro .3s .1s backwards;position:relative}.search__submit-btn{cursor:pointer;left:1.5625rem;position:absolute;top:1.0625rem}.search__icon{color:rgba(0,0,0,.73);transition:all .2s}.search__icon:focus,.search__icon:hover{color:rgba(0,0,0,.93)}.search__input{background-color:hsla(0,0%,100%,.9);border-radius:.25rem;caret-color:rgba(0,0,0,.5);color:rgba(0,0,0,.9);height:3.5rem;padding-bottom:.125rem;padding-left:3.4375rem;padding-right:.9375rem;transition:all .2s,box-shadow .1s;width:38.125rem}.search__input::selection{background-color:rgba(0,0,0,.25)}.search__input:focus,.search__input:hover{background-color:#fff}.search__input:focus{box-shadow:0 0 0 .25rem hsla(0,0%,100%,.4)}.search__input::-webkit-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input:-ms-input-placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input::placeholder{color:rgba(0,0,0,.7);font-size:.9375rem;font-weight:500}.search__input--invalid{box-shadow:0 0 0 .25rem rgba(47,19,19,.729)}.container{-webkit-animation:intro .3s .2s backwards;animation:intro .3s .2s backwards;background-color:#fff;border-radius:.5rem;border-top-right-radius:.5625rem;box-shadow:0 .1875rem .3125rem rgba(0,0,0,.07);display:flex;height:38.5rem;margin:2.5rem .75rem 0;width:61rem}.search-results{cursor:default;display:flex;flex-direction:column;position:relative;width:21.25rem}.search-results__top{align-items:center;border-bottom:.0625rem solid #e8edf0;display:flex;justify-content:space-between;padding:.625rem 1.25rem}.count{font-size:.75rem}.sorting__icon{color:#4c4f50;font-size:.6875rem;margin-right:.3125rem}.sorting__button{background-color:#e8edf0;border-radius:.1875rem;cursor:pointer;font-size:.625rem;margin-left:.125rem;padding:.375rem .5rem;text-transform:uppercase;transition:all .2s}.sorting__button:focus,.sorting__button:hover{background-color:#d0d5d8}.sorting__button--active,.sorting__button--active:focus,.sorting__button--active:hover{background-color:#3c4041;color:#fff}.pagination{align-items:center;border-top:.0625rem solid #e8edf0;display:flex;height:2.5rem;justify-content:space-between;margin-top:auto;padding:0 1.25rem .0625rem}.pagination__button{background-color:#eceff2;border-radius:31.25rem;color:#747c82;cursor:pointer;font-size:.625rem;padding:.25rem .625rem;transition:all .2s,visibility 0s}.pagination__button:focus,.pagination__button:hover{background-color:#dde2e6}.pagination__button--hidden{visibility:hidden}.pagination__number{font-weight:500}.pagination__icon{color:#9fa6b0;font-size:.5rem}.job-details{background-color:#eff2f5;border-bottom-right-radius:.5rem;border-top-right-radius:.75rem;flex:1;position:relative}.job-details__start-view{left:50%;position:absolute;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.job-details__start-text{color:#24292d;font-size:.875rem;opacity:.55;text-align:center;width:17.1875rem}.job-details__start-text--big{color:#0d1114;font-size:1.125rem;font-weight:600;margin-bottom:.625rem}.job-details__content{height:100%}.job-details__cover-img{border-top-right-radius:.5rem;height:10.875rem;-o-object-fit:cover;object-fit:cover;position:absolute;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none;width:100%;z-index:0}.job-details__footer{border-top:.0625rem solid #dce2e8;margin-left:2.625rem;margin-right:2.625rem;margin-top:2.0625rem;padding-top:.8125rem}.job-details__footer-text{color:#858b8f;font-size:.625rem}.apply-btn{align-items:center;background-color:#2671dd;border-radius:.1875rem;color:hsla(0,0%,100%,.92);cursor:pointer;display:flex;font-size:.6875rem;padding:.3125rem .625rem .375rem;position:absolute;right:.75rem;text-transform:uppercase;top:.75rem;transition:all .2s;z-index:2}.apply-btn:focus,.apply-btn:hover{background-color:#1d60bd;color:#fff}.apply-btn__icon{color:hsla(0,0%,100%,.65);font-size:.5rem;margin-left:.25rem;margin-top:-.0625rem}.job-info{-webkit-column-gap:1rem;column-gap:1rem;display:flex;margin-bottom:2.5rem;padding-top:7.5rem;position:relative;z-index:1}.job-info:before{background-image:linear-gradient(0deg,rgba(0,0,0,.7),rgba(0,0,0,.15));border-top-right-radius:.5rem;content:"";height:10.875rem;left:0;position:absolute;top:0;width:100%;z-index:-1}.job-info__left{padding-left:2.625rem}.job-info__right{padding-right:2.625rem}.job-info__badge{align-items:center;background-color:#d0d335;border-radius:.3125rem;display:flex;font-size:1.375rem;font-weight:600;height:4.375rem;justify-content:center;margin-bottom:.8125rem;width:3.4375rem}.job-info__below-badge{display:flex;justify-content:space-between}.job-info__time{color:#494d4f;font-size:.75rem;-webkit-transform:translateY(.0625rem);transform:translateY(.0625rem)}.job-info__bookmark-btn{cursor:pointer}.job-info__bookmark-btn:hover .job-info__bookmark-icon{color:#2671dd}.job-info__bookmark-icon{color:#d7dbe0;font-size:1.125rem;transition:all .2s}.job-info__bookmark-icon--bookmarked{color:#2671dd}.job-info__company{color:hsla(0,0%,100%,.8);font-size:.875rem;font-style:italic}.job-info__description{font-size:.875rem;line-height:1.4;margin-bottom:.75rem;margin-top:1.125rem}.job-info__extras{-webkit-column-gap:2.1875rem;column-gap:2.1875rem;display:flex}.job-info__extra{align-items:center;display:flex;font-size:.75rem}.job-info__extra-icon{align-items:center;background-color:#e4e9ed;border-radius:50%;color:#a1a8b0;display:flex;font-size:.5625rem;height:1.4375rem;justify-content:center;margin-right:.5rem;width:1.4375rem}.qualifications{display:flex;margin-bottom:1.875rem;padding-left:2.625rem;padding-right:2.625rem}.qualifications__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.qualifications__left{margin-right:2.1875rem}.qualifications__list{display:flex;flex-wrap:wrap;gap:.375rem}.qualifications__item{background-color:#e6ebee;border-radius:.125rem;color:#494d4f;font-size:.8125rem;padding:.375rem .625rem}.reviews{display:flex;padding-left:2.625rem;padding-right:2.625rem}.reviews__sub-text{font-size:.8125rem;margin-top:.1875rem;width:9.8125rem}.reviews__left{margin-right:2.1875rem}.reviews__list{-webkit-column-gap:1.25rem;column-gap:1.25rem;display:grid;flex:1;grid-template-columns:1fr 1fr;grid-template-rows:auto auto;row-gap:1.25rem}.reviews__item{color:#494d4f;font-size:.8125rem;font-style:italic;position:relative;-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.reviews__item:before{color:#d2d7db;content:"“";font-size:3.125rem;left:-.625rem;position:absolute;top:-.9375rem;-webkit-transform:translateZ(-.0625rem);transform:translateZ(-.0625rem)}.footer{align-items:center;color:#a4a9ac;display:flex;justify-content:space-between;margin:.9375rem auto 0;max-width:62.5rem;padding:0 .75rem}.copyright{font-size:.6875rem}.copyright ::selection{background-color:hsla(0,0%,100%,.1)}.copyright__text{line-height:1.2}.copyright__link{text-decoration:underline}.copyright__icon{color:#aeb3b6;font-size:.625rem;margin-left:.125rem;margin-right:.25rem}.jobs-available{align-self:flex-start;font-size:.6875rem}.spinner{-webkit-animation:spinner 1s linear infinite;animation:spinner 1s linear infinite;border-radius:50%;position:absolute;visibility:hidden}.spinner--search{border:.3125rem solid #e2e7e9;border-left-color:#ccd1d3;height:5.3125rem;left:50%;top:18%;width:5.3125rem}.spinner--job-details{border:.375rem solid #d5d9db;border-left-color:#bbc0c2;height:6.5625rem;left:50%;top:40%;width:6.5625rem}.spinner--visible{visibility:visible}.error{background:#fff;border-radius:.1875rem;box-shadow:0 .3125rem .5rem rgba(0,0,0,.15);display:flex;min-height:2.875rem;opacity:0;padding:.875rem 1.25rem;position:absolute;right:.9375rem;top:.9375rem;-webkit-transform:translateY(-7.5rem);transform:translateY(-7.5rem);transition:all .3s;visibility:hidden;width:17.5rem}.error--visible{opacity:1;-webkit-transform:initial;transform:none;visibility:initial}.error__icon{color:#7b4040;font-size:1rem;margin-top:.125rem}.error__right{margin-left:.625rem}.error__title{font-size:.75rem;font-weight:500;margin-bottom:.0625rem;text-transform:uppercase}.error__text{color:#616268;font-size:.8125rem}@media (max-height:925px) and (min-width:1010px){.body{padding-bottom:3.125rem}}@media (max-width:1179px){.job-list--bookmarks{right:0}}@media (max-width:1009px){.body{padding:0 .75rem 3.125rem}.header__top{max-width:50rem;padding-left:0;padding-right:0}.container{border-radius:.5rem;flex-direction:column;height:auto;max-width:50rem;overflow:hidden;width:100%}.search-results{width:100%}.job-details{display:none}.footer{max-width:50rem;padding-left:0;padding-right:0}}@media (max-width:660px){.intro,.search,.search__input{width:100%}.footer{justify-content:center}.copyright{text-align:center}.jobs-available{display:none;margin-left:.9375rem;text-align:right}.intro{row-gap:1.5625rem}.first-heading{max-width:25rem;text-align:center}}@media (max-width:370px){.job-list--bookmarks{min-width:auto;width:93vw}.job-item{width:100%}.job-item__badge{display:none}.error{left:50%;right:auto;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:93vw}} diff --git a/09 - Supplemental/Formatting in JS/dist/main.js b/09 - Supplemental/Formatting in JS/dist/main.js deleted file mode 100644 index eba8c55..0000000 --- a/09 - Supplemental/Formatting in JS/dist/main.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! For license information please see main.js.LICENSE.txt */ -!function(){var t={9662:function(t,r,e){var n=e(614),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a function")}},9483:function(t,r,e){var n=e(4411),o=e(6330),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a constructor")}},6077:function(t,r,e){var n=e(614),o=String,i=TypeError;t.exports=function(t){if("object"==typeof t||n(t))return t;throw i("Can't set "+o(t)+" as a prototype")}},1223:function(t,r,e){var n=e(5112),o=e(30),i=e(3070).f,a=n("unscopables"),c=Array.prototype;null==c[a]&&i(c,a,{configurable:!0,value:o(null)}),t.exports=function(t){c[a][t]=!0}},5787:function(t,r,e){var n=e(7976),o=TypeError;t.exports=function(t,r){if(n(r,t))return t;throw o("Incorrect invocation")}},9670:function(t,r,e){var n=e(111),o=String,i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not an object")}},8533:function(t,r,e){"use strict";var n=e(2092).forEach,o=e(2133)("forEach");t.exports=o?[].forEach:function(t){return n(this,t,arguments.length>1?arguments[1]:void 0)}},8457:function(t,r,e){"use strict";var n=e(9974),o=e(6916),i=e(7908),a=e(3411),c=e(7659),u=e(4411),s=e(6244),f=e(6135),l=e(8554),p=e(1246),h=Array;t.exports=function(t){var r=i(t),e=u(this),v=arguments.length,d=v>1?arguments[1]:void 0,y=void 0!==d;y&&(d=n(d,v>2?arguments[2]:void 0));var m,g,b,x,w,_,S=p(r),j=0;if(!S||this===h&&c(S))for(m=s(r),g=e?new this(m):h(m);m>j;j++)_=y?d(r[j],j):r[j],f(g,j,_);else for(w=(x=l(r,S)).next,g=e?new this:[];!(b=o(w,x)).done;j++)_=y?a(x,d,[b.value,j],!0):b.value,f(g,j,_);return g.length=j,g}},1318:function(t,r,e){var n=e(5656),o=e(1400),i=e(6244),a=function(t){return function(r,e,a){var c,u=n(r),s=i(u),f=o(a,s);if(t&&e!=e){for(;s>f;)if((c=u[f++])!=c)return!0}else for(;s>f;f++)if((t||f in u)&&u[f]===e)return t||f||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},2092:function(t,r,e){var n=e(9974),o=e(1702),i=e(8361),a=e(7908),c=e(6244),u=e(5417),s=o([].push),f=function(t){var r=1==t,e=2==t,o=3==t,f=4==t,l=6==t,p=7==t,h=5==t||l;return function(v,d,y,m){for(var g,b,x=a(v),w=i(x),_=n(d,y),S=c(w),j=0,L=m||u,E=r?L(v,S):e||p?L(v,0):void 0;S>j;j++)if((h||j in w)&&(b=_(g=w[j],j,x),t))if(r)E[j]=b;else if(b)switch(t){case 3:return!0;case 5:return g;case 6:return j;case 2:s(E,g)}else switch(t){case 4:return!1;case 7:s(E,g)}return l?-1:o||f?f:E}};t.exports={forEach:f(0),map:f(1),filter:f(2),some:f(3),every:f(4),find:f(5),findIndex:f(6),filterReject:f(7)}},1194:function(t,r,e){var n=e(7293),o=e(5112),i=e(7392),a=o("species");t.exports=function(t){return i>=51||!n((function(){var r=[];return(r.constructor={})[a]=function(){return{foo:1}},1!==r[t](Boolean).foo}))}},2133:function(t,r,e){"use strict";var n=e(7293);t.exports=function(t,r){var e=[][t];return!!e&&n((function(){e.call(null,r||function(){return 1},1)}))}},1589:function(t,r,e){var n=e(1400),o=e(6244),i=e(6135),a=Array,c=Math.max;t.exports=function(t,r,e){for(var u=o(t),s=n(r,u),f=n(void 0===e?u:e,u),l=a(c(f-s,0)),p=0;s0;)t[n]=t[--n];n!==i++&&(t[n]=e)}return t},c=function(t,r,e,n){for(var o=r.length,i=e.length,a=0,c=0;a9007199254740991)throw r("Maximum allowed index exceeded");return t}},8324:function(t){t.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},8509:function(t,r,e){var n=e(317)("span").classList,o=n&&n.constructor&&n.constructor.prototype;t.exports=o===Object.prototype?void 0:o},8886:function(t,r,e){var n=e(8113).match(/firefox\/(\d+)/i);t.exports=!!n&&+n[1]},7871:function(t){t.exports="object"==typeof window&&"object"!=typeof Deno},256:function(t,r,e){var n=e(8113);t.exports=/MSIE|Trident/.test(n)},1528:function(t,r,e){var n=e(8113),o=e(7854);t.exports=/ipad|iphone|ipod/i.test(n)&&void 0!==o.Pebble},6833:function(t,r,e){var n=e(8113);t.exports=/(?:ipad|iphone|ipod).*applewebkit/i.test(n)},5268:function(t,r,e){var n=e(4326),o=e(7854);t.exports="process"==n(o.process)},1036:function(t,r,e){var n=e(8113);t.exports=/web0s(?!.*chrome)/i.test(n)},8113:function(t,r,e){var n=e(5005);t.exports=n("navigator","userAgent")||""},7392:function(t,r,e){var n,o,i=e(7854),a=e(8113),c=i.process,u=i.Deno,s=c&&c.versions||u&&u.version,f=s&&s.v8;f&&(o=(n=f.split("."))[0]>0&&n[0]<4?1:+(n[0]+n[1])),!o&&a&&(!(n=a.match(/Edge\/(\d+)/))||n[1]>=74)&&(n=a.match(/Chrome\/(\d+)/))&&(o=+n[1]),t.exports=o},8008:function(t,r,e){var n=e(8113).match(/AppleWebKit\/(\d+)\./);t.exports=!!n&&+n[1]},748:function(t){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(t,r,e){var n=e(7854),o=e(1236).f,i=e(8880),a=e(8052),c=e(3072),u=e(9920),s=e(4705);t.exports=function(t,r){var e,f,l,p,h,v=t.target,d=t.global,y=t.stat;if(e=d?n:y?n[v]||c(v,{}):(n[v]||{}).prototype)for(f in r){if(p=r[f],l=t.dontCallGetSet?(h=o(e,f))&&h.value:e[f],!s(d?f:v+(y?".":"#")+f,t.forced)&&void 0!==l){if(typeof p==typeof l)continue;u(p,l)}(t.sham||l&&l.sham)&&i(p,"sham",!0),a(e,f,p,t)}}},7293:function(t){t.exports=function(t){try{return!!t()}catch(t){return!0}}},2104:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.apply,a=o.call;t.exports="object"==typeof Reflect&&Reflect.apply||(n?a.bind(i):function(){return a.apply(i,arguments)})},9974:function(t,r,e){var n=e(1702),o=e(9662),i=e(4374),a=n(n.bind);t.exports=function(t,r){return o(t),void 0===r?t:i?a(t,r):function(){return t.apply(r,arguments)}}},4374:function(t,r,e){var n=e(7293);t.exports=!n((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))},6916:function(t,r,e){var n=e(4374),o=Function.prototype.call;t.exports=n?o.bind(o):function(){return o.apply(o,arguments)}},6530:function(t,r,e){var n=e(9781),o=e(2597),i=Function.prototype,a=n&&Object.getOwnPropertyDescriptor,c=o(i,"name"),u=c&&"something"===function(){}.name,s=c&&(!n||n&&a(i,"name").configurable);t.exports={EXISTS:c,PROPER:u,CONFIGURABLE:s}},1702:function(t,r,e){var n=e(4374),o=Function.prototype,i=o.bind,a=o.call,c=n&&i.bind(a,a);t.exports=n?function(t){return t&&c(t)}:function(t){return t&&function(){return a.apply(t,arguments)}}},5005:function(t,r,e){var n=e(7854),o=e(614),i=function(t){return o(t)?t:void 0};t.exports=function(t,r){return arguments.length<2?i(n[t]):n[t]&&n[t][r]}},1246:function(t,r,e){var n=e(648),o=e(8173),i=e(7497),a=e(5112)("iterator");t.exports=function(t){if(null!=t)return o(t,a)||o(t,"@@iterator")||i[n(t)]}},8554:function(t,r,e){var n=e(6916),o=e(9662),i=e(9670),a=e(6330),c=e(1246),u=TypeError;t.exports=function(t,r){var e=arguments.length<2?c(t):r;if(o(e))return i(n(e,t));throw u(a(t)+" is not iterable")}},8173:function(t,r,e){var n=e(9662);t.exports=function(t,r){var e=t[r];return null==e?void 0:n(e)}},7854:function(t,r,e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e.g&&e.g)||function(){return this}()||Function("return this")()},2597:function(t,r,e){var n=e(1702),o=e(7908),i=n({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,r){return i(o(t),r)}},3501:function(t){t.exports={}},842:function(t,r,e){var n=e(7854);t.exports=function(t,r){var e=n.console;e&&e.error&&(1==arguments.length?e.error(t):e.error(t,r))}},490:function(t,r,e){var n=e(5005);t.exports=n("document","documentElement")},4664:function(t,r,e){var n=e(9781),o=e(7293),i=e(317);t.exports=!n&&!o((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},8361:function(t,r,e){var n=e(1702),o=e(7293),i=e(4326),a=Object,c=n("".split);t.exports=o((function(){return!a("z").propertyIsEnumerable(0)}))?function(t){return"String"==i(t)?c(t,""):a(t)}:a},2788:function(t,r,e){var n=e(1702),o=e(614),i=e(5465),a=n(Function.toString);o(i.inspectSource)||(i.inspectSource=function(t){return a(t)}),t.exports=i.inspectSource},9909:function(t,r,e){var n,o,i,a=e(8536),c=e(7854),u=e(1702),s=e(111),f=e(8880),l=e(2597),p=e(5465),h=e(6200),v=e(3501),d="Object already initialized",y=c.TypeError,m=c.WeakMap;if(a||p.state){var g=p.state||(p.state=new m),b=u(g.get),x=u(g.has),w=u(g.set);n=function(t,r){if(x(g,t))throw new y(d);return r.facade=t,w(g,t,r),r},o=function(t){return b(g,t)||{}},i=function(t){return x(g,t)}}else{var _=h("state");v[_]=!0,n=function(t,r){if(l(t,_))throw new y(d);return r.facade=t,f(t,_,r),r},o=function(t){return l(t,_)?t[_]:{}},i=function(t){return l(t,_)}}t.exports={set:n,get:o,has:i,enforce:function(t){return i(t)?o(t):n(t,{})},getterFor:function(t){return function(r){var e;if(!s(r)||(e=o(r)).type!==t)throw y("Incompatible receiver, "+t+" required");return e}}}},7659:function(t,r,e){var n=e(5112),o=e(7497),i=n("iterator"),a=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||a[i]===t)}},3157:function(t,r,e){var n=e(4326);t.exports=Array.isArray||function(t){return"Array"==n(t)}},614:function(t){t.exports=function(t){return"function"==typeof t}},4411:function(t,r,e){var n=e(1702),o=e(7293),i=e(614),a=e(648),c=e(5005),u=e(2788),s=function(){},f=[],l=c("Reflect","construct"),p=/^\s*(?:class|function)\b/,h=n(p.exec),v=!p.exec(s),d=function(t){if(!i(t))return!1;try{return l(s,f,t),!0}catch(t){return!1}},y=function(t){if(!i(t))return!1;switch(a(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return v||!!h(p,u(t))}catch(t){return!0}};y.sham=!0,t.exports=!l||o((function(){var t;return d(d.call)||!d(Object)||!d((function(){t=!0}))||t}))?y:d},4705:function(t,r,e){var n=e(7293),o=e(614),i=/#|\.prototype\./,a=function(t,r){var e=u[c(t)];return e==f||e!=s&&(o(r)?n(r):!!r)},c=a.normalize=function(t){return String(t).replace(i,".").toLowerCase()},u=a.data={},s=a.NATIVE="N",f=a.POLYFILL="P";t.exports=a},111:function(t,r,e){var n=e(614);t.exports=function(t){return"object"==typeof t?null!==t:n(t)}},1913:function(t){t.exports=!1},7850:function(t,r,e){var n=e(111),o=e(4326),i=e(5112)("match");t.exports=function(t){var r;return n(t)&&(void 0!==(r=t[i])?!!r:"RegExp"==o(t))}},2190:function(t,r,e){var n=e(5005),o=e(614),i=e(7976),a=e(3307),c=Object;t.exports=a?function(t){return"symbol"==typeof t}:function(t){var r=n("Symbol");return o(r)&&i(r.prototype,c(t))}},408:function(t,r,e){var n=e(9974),o=e(6916),i=e(9670),a=e(6330),c=e(7659),u=e(6244),s=e(7976),f=e(8554),l=e(1246),p=e(9212),h=TypeError,v=function(t,r){this.stopped=t,this.result=r},d=v.prototype;t.exports=function(t,r,e){var y,m,g,b,x,w,_,S=e&&e.that,j=!(!e||!e.AS_ENTRIES),L=!(!e||!e.IS_ITERATOR),E=!(!e||!e.INTERRUPTED),O=n(r,S),k=function(t){return y&&p(y,"normal",t),new v(!0,t)},P=function(t){return j?(i(t),E?O(t[0],t[1],k):O(t[0],t[1])):E?O(t,k):O(t)};if(L)y=t;else{if(!(m=l(t)))throw h(a(t)+" is not iterable");if(c(m)){for(g=0,b=u(t);b>g;g++)if((x=P(t[g]))&&s(d,x))return x;return new v(!1)}y=f(t,m)}for(w=y.next;!(_=o(w,y)).done;){try{x=P(_.value)}catch(t){p(y,"throw",t)}if("object"==typeof x&&x&&s(d,x))return x}return new v(!1)}},9212:function(t,r,e){var n=e(6916),o=e(9670),i=e(8173);t.exports=function(t,r,e){var a,c;o(t);try{if(!(a=i(t,"return"))){if("throw"===r)throw e;return e}a=n(a,t)}catch(t){c=!0,a=t}if("throw"===r)throw e;if(c)throw a;return o(a),e}},3383:function(t,r,e){"use strict";var n,o,i,a=e(7293),c=e(614),u=e(30),s=e(9518),f=e(8052),l=e(5112),p=e(1913),h=l("iterator"),v=!1;[].keys&&("next"in(i=[].keys())?(o=s(s(i)))!==Object.prototype&&(n=o):v=!0),null==n||a((function(){var t={};return n[h].call(t)!==t}))?n={}:p&&(n=u(n)),c(n[h])||f(n,h,(function(){return this})),t.exports={IteratorPrototype:n,BUGGY_SAFARI_ITERATORS:v}},7497:function(t){t.exports={}},6244:function(t,r,e){var n=e(7466);t.exports=function(t){return n(t.length)}},6339:function(t,r,e){var n=e(7293),o=e(614),i=e(2597),a=e(9781),c=e(6530).CONFIGURABLE,u=e(2788),s=e(9909),f=s.enforce,l=s.get,p=Object.defineProperty,h=a&&!n((function(){return 8!==p((function(){}),"length",{value:8}).length})),v=String(String).split("String"),d=t.exports=function(t,r,e){"Symbol("===String(r).slice(0,7)&&(r="["+String(r).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),e&&e.getter&&(r="get "+r),e&&e.setter&&(r="set "+r),(!i(t,"name")||c&&t.name!==r)&&p(t,"name",{value:r,configurable:!0}),h&&e&&i(e,"arity")&&t.length!==e.arity&&p(t,"length",{value:e.arity});try{e&&i(e,"constructor")&&e.constructor?a&&p(t,"prototype",{writable:!1}):t.prototype&&(t.prototype=void 0)}catch(t){}var n=f(t);return i(n,"source")||(n.source=v.join("string"==typeof r?r:"")),t};Function.prototype.toString=d((function(){return o(this)&&l(this).source||u(this)}),"toString")},4758:function(t){var r=Math.ceil,e=Math.floor;t.exports=Math.trunc||function(t){var n=+t;return(n>0?e:r)(n)}},5948:function(t,r,e){var n,o,i,a,c,u,s,f,l=e(7854),p=e(9974),h=e(1236).f,v=e(261).set,d=e(6833),y=e(1528),m=e(1036),g=e(5268),b=l.MutationObserver||l.WebKitMutationObserver,x=l.document,w=l.process,_=l.Promise,S=h(l,"queueMicrotask"),j=S&&S.value;j||(n=function(){var t,r;for(g&&(t=w.domain)&&t.exit();o;){r=o.fn,o=o.next;try{r()}catch(t){throw o?a():i=void 0,t}}i=void 0,t&&t.enter()},d||g||m||!b||!x?!y&&_&&_.resolve?((s=_.resolve(void 0)).constructor=_,f=p(s.then,s),a=function(){f(n)}):g?a=function(){w.nextTick(n)}:(v=p(v,l),a=function(){v(n)}):(c=!0,u=x.createTextNode(""),new b(n).observe(u,{characterData:!0}),a=function(){u.data=c=!c})),t.exports=j||function(t){var r={fn:t,next:void 0};i&&(i.next=r),o||(o=r,a()),i=r}},735:function(t,r,e){var n=e(133);t.exports=n&&!!Symbol.for&&!!Symbol.keyFor},133:function(t,r,e){var n=e(7392),o=e(7293);t.exports=!!Object.getOwnPropertySymbols&&!o((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&n&&n<41}))},8536:function(t,r,e){var n=e(7854),o=e(614),i=e(2788),a=n.WeakMap;t.exports=o(a)&&/native code/.test(i(a))},8523:function(t,r,e){"use strict";var n=e(9662),o=function(t){var r,e;this.promise=new t((function(t,n){if(void 0!==r||void 0!==e)throw TypeError("Bad Promise constructor");r=t,e=n})),this.resolve=n(r),this.reject=n(e)};t.exports.f=function(t){return new o(t)}},3929:function(t,r,e){var n=e(7850),o=TypeError;t.exports=function(t){if(n(t))throw o("The method doesn't accept regular expressions");return t}},30:function(t,r,e){var n,o=e(9670),i=e(6048),a=e(748),c=e(3501),u=e(490),s=e(317),f=e(6200)("IE_PROTO"),l=function(){},p=function(t){return" - - rmtDev -- Find your remote developer job - - - - -
    - Background pattern -
    - -
    -
    - - -
      - -
    -
    -
    - -
    -
    -

    Find your remote developer job

    - -
    - -
    -
    - - -
    -

    - 0 results -

    - -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    -
    - -
    - -
    -

    What are you looking for?

    -

    Start by searching for any technology - your ideal job is working with

    -
    - -
    -
    -
    -
    - -
    - - - - -

    109573 total jobs available

    -
    - -
    - -
    -

    Something went wrong

    -

    Lorem, ipsum dolor sit amet consectetur adipisicing elit.

    -
    -
    - - - - \ No newline at end of file diff --git a/09 - Supplemental/Formatting in JS/src/index.js b/09 - Supplemental/Formatting in JS/src/index.js deleted file mode 100644 index c7c6751..0000000 --- a/09 - Supplemental/Formatting in JS/src/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import './components/Bookmarks.js'; -import './components/Error.js'; -import './components/JobDetails.js'; -import './components/JobList.js'; -import './components/Pagination.js'; -import './components/Router.js'; -import './components/Search.js'; -import './components/Sorting.js'; -import './components/Spinner.js'; -import './components/Storage.js'; - -import './index.css'; \ No newline at end of file diff --git a/09 - Supplemental/Formatting in JS/webpack.config.js b/09 - Supplemental/Formatting in JS/webpack.config.js deleted file mode 100644 index b970c2c..0000000 --- a/09 - Supplemental/Formatting in JS/webpack.config.js +++ /dev/null @@ -1,27 +0,0 @@ -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); - -module.exports = { - mode: 'production', - entry: './src/index.js', - output: { - filename: 'main.js' - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: 'main.css' - }) - ], - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: ['babel-loader'] - }, - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] - } - ] - } -}; \ No newline at end of file diff --git a/09 - Supplemental/Has the For Loop Been Replaced by forEach/index.html b/09 - Supplemental/Has the For Loop Been Replaced by forEach/index.html deleted file mode 100644 index b4a3c9b..0000000 --- a/09 - Supplemental/Has the For Loop Been Replaced by forEach/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - CorpComment -- Give Feedback, Publicly - - - - -
    -
    - - - - -
    - -
    -
    - pattern - -

    Give Feedback. Publicly.

    -
    - - -
    -

    150

    - -
    -
    -
    - -
      -
      -
    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - - - - - \ No newline at end of file diff --git a/09 - Supplemental/Has the For Loop Been Replaced by forEach/script.js b/09 - Supplemental/Has the For Loop Been Replaced by forEach/script.js deleted file mode 100644 index 311ea99..0000000 --- a/09 - Supplemental/Has the For Loop Been Replaced by forEach/script.js +++ /dev/null @@ -1,223 +0,0 @@ -// -- GLOBAL -- -const MAX_CHARS = 150; -const BASE_API_URL = "https://bytegrad.com/course-assets/js/1/api"; - -const textareaEl = document.querySelector(".form__textarea"); -const counterEl = document.querySelector(".counter"); -const formEl = document.querySelector(".form"); -const feedbackListEl = document.querySelector(".feedbacks"); -const submitBtnEl = document.querySelector(".submit-btn"); -const spinnerEl = document.querySelector(".spinner"); -const hashtagListEl = document.querySelector(".hashtags"); - -const renderFeedbackItem = (feedbackItem) => { - // new feedback item HTML - const feedbackItemHTML = ` - - `; - - // insert new feedback item in list - feedbackListEl.insertAdjacentHTML("beforeend", feedbackItemHTML); -}; - -// -- COUNTER COMPONENT -- -(() => { - const inputHandler = () => { - // determine maximum number of characters - const maxNrChars = MAX_CHARS; - - // determine number of characters currently typed - const nrCharsTyped = textareaEl.value.length; - - // calculate number of characters left (maximum minus currently typed) - const charsLeft = maxNrChars - nrCharsTyped; - - // show number of characters left - counterEl.textContent = charsLeft; - }; - - textareaEl.addEventListener("input", inputHandler); -})(); - -// -- FORM COMPONENT -- -(() => { - const showVisualIndicator = (textCheck) => { - const className = textCheck === "valid" ? "form--valid" : "form--invalid"; - - // show valid indicator - formEl.classList.add(className); - - // remove visual indicator - setTimeout(() => { - formEl.classList.remove(className); - }, 2000); - }; - - const submitHandler = (event) => { - // prevent default browser action (submitting form data to 'action'-address and refreshing page) - event.preventDefault(); - - // get text from textarea - const text = textareaEl.value; - - // validate text (e.g. check if #hashtag is present and text is long enough) - if (text.includes("#") && text.length >= 5) { - showVisualIndicator("valid"); - } else { - showVisualIndicator("invalid"); - - // focus textarea - textareaEl.focus(); - - // stop this function execution - return; - } - - // we have text, now extract other info from text - const hashtag = text.split(" ").find((word) => word.includes("#")); - const company = hashtag.substring(1); - const badgeLetter = company.substring(0, 1).toUpperCase(); - const upvoteCount = 0; - const daysAgo = 0; - - // render feedback item in list - const feedbackItem = { - upvoteCount: upvoteCount, - company: company, - badgeLetter: badgeLetter, - daysAgo: daysAgo, - text: text, - }; - renderFeedbackItem(feedbackItem); - - // send feedback item to server - fetch(`${BASE_API_URL}/feedbacks`, { - method: "POST", - body: JSON.stringify(feedbackItem), - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - }) - .then((response) => { - if (!response.ok) { - console.log("Something went wrong"); - return; - } - - console.log("Successfully submitted"); - }) - .catch((error) => console.log(error)); - - // clear textarea - textareaEl.value = ""; - - // blur submit button - submitBtnEl.blur(); - - // reset counter - counterEl.textContent = MAX_CHARS; - }; - - formEl.addEventListener("submit", submitHandler); -})(); - -// -- FEEDBACK LIST COMPONENT -- -(() => { - const clickHandler = (event) => { - // get clicked HTML-element - const clickedEl = event.target; - - // determine if user intended to upvote or expand - const upvoteIntention = clickedEl.className.includes("upvote"); - - // run the appropriate logic - if (upvoteIntention) { - // get the closest upvote button - const upvoteBtnEl = clickedEl.closest(".upvote"); - - // disable upvote button (prevent double-clicks, spam) - upvoteBtnEl.disabled = true; - - // select the upvote count element within the upvote button - const upvoteCountEl = upvoteBtnEl.querySelector(".upvote__count"); - - // get currently displayed upvote count as number (+) - let upvoteCount = +upvoteCountEl.textContent; - - // set upvote count incremented by 1 - upvoteCountEl.textContent = ++upvoteCount; - } else { - // expand the clicked feedback item - clickedEl.closest(".feedback").classList.toggle("feedback--expand"); - } - }; - - feedbackListEl.addEventListener("click", clickHandler); - - fetch(`${BASE_API_URL}/feedbacks`) - .then((response) => response.json()) - .then((data) => { - // remove spinner - spinnerEl.remove(); - - // iterate over each element in feedbacks array and render it in list - data.feedbacks.forEach((feedbackItem) => - renderFeedbackItem(feedbackItem) - ); - }) - .catch((error) => { - feedbackListEl.textContent = `Failed to fetch feedback items. Error message: ${error.message}`; - }); -})(); - -// -- HASHTAG LIST COMPONENT -- -(() => { - const clickHandler = (event) => { - // get the clicked element - const clickedEl = event.target; - - // stop function if click happened in list, but outside buttons - if (clickedEl.className === "hashtags") return; - - // extract company name - const companyNameFromHashtag = clickedEl.textContent - .substring(1) - .toLowerCase() - .trim(); - - // iterate over each feedback item in the list - feedbackListEl.childNodes.forEach((childNode) => { - // stop this iteration if it's a text node - if (childNode.nodeType === 3) return; - - // extract company name - const companyNameFromFeedbackItem = childNode - .querySelector(".feedback__company") - .textContent.toLowerCase() - .trim(); - - // remove feedback item from list if company names are not equal - if (companyNameFromHashtag !== companyNameFromFeedbackItem) { - childNode.remove(); - } - }); - }; - - hashtagListEl.addEventListener("click", clickHandler); -})(); diff --git a/09 - Supplemental/Has the For Loop Been Replaced by forEach/style.css b/09 - Supplemental/Has the For Loop Been Replaced by forEach/style.css deleted file mode 100644 index fd4b37f..0000000 --- a/09 - Supplemental/Has the For Loop Been Replaced by forEach/style.css +++ /dev/null @@ -1,671 +0,0 @@ -/* RESET */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -ul, -ol { - list-style: none; -} - -a { - color: inherit; - text-decoration: initial; -} - -textarea { - font: inherit; - border: initial; - resize: none; - outline: initial; /* create alternative for focus state */ -} - -button { - font: inherit; - border: initial; - outline: initial; /* create alternative for focus state */ - background-color: initial; -} - -/* UTILITIES */ -.u-bold { - font-weight: 600; -} - -.u-medium { - font-weight: 500; -} - -.u-italic { - font-style: italic; -} - -.u-transparent { - color: rgba(255, 255, 255, 0.8); -} - -/* KEYFRAMES */ -@keyframes intro { - 0% { - transform: scale(0.8); - } - - 100% { - transform: scale(1); - } -} - -@keyframes spinner { - 0% { - transform: translateX(-50%) rotate(0deg); - } - - 100% { - transform: translateX(-50%) rotate(360deg); - } -} - -/* BASE */ -.body { - background-image: linear-gradient(200deg, #654a86, #534292); - min-height: 100vh; - font-family: 'Inter', sans-serif; - display: flex; - justify-content: center; - align-items: center; - - scrollbar-width: none; /* Firefox */ -} - -.body::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - display: none; -} - -.body__container { - position: relative; - display: flex; - justify-content: center; - height: 850px; -} - -/* HEADINGS */ -.first-heading { - font-weight: 700; - font-size: 36px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, 0.93); -} - -.first-heading::selection, -.first-heading *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* APP */ -.app { - width: 715px; - height: 100%; - border-radius: 6px; - overflow: hidden; - animation: intro 0.4s; -} - -/* HEADER */ -.header { - height: 277px; - background-color: #121618; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - z-index: 999; - backface-visibility: hidden; - transform: translateZ(0); - box-shadow: rgba(12, 17, 21, 0.5) 0px 0px 50px; - padding-bottom: 3px; -} - -.header__pattern { - position: absolute; - top: 0; - z-index: 0; -} - -.header__pattern::selection { - background-color: initial; -} - -/* LOGO */ -.logo { - height: 27.5px; - -} - -.logo::selection { - background-color: initial; -} - -.logo__img { - position: relative; - z-index: 1; - backface-visibility: hidden; - transform: translateZ(0); - -} - -/* FORM */ -.form { - margin-top: 20px; - height: 123px; - width: 470px; - background-color: rgba(255, 255, 255, 0.04); - border-radius: 6px; - overflow: hidden; - position: relative; - transition: background-color 0.2s, box-shadow 0.2s; -} - -.form:hover, -.form:focus { - background-color: rgba(255, 255, 255, 0.055); -} - -.form:focus-within { - outline: 2px solid rgba(255, 255, 255, 0.125); -} - -.form--invalid { - box-shadow: 0 0 0 2px #8a3d2c; -} - -.form--valid { - box-shadow: 0 0 0 2px #2c8a5e; -} - -.form__textarea { - width: 100%; - height: 85px; - background-color: transparent; - color: rgba(255, 255, 255, 0.8); - font-size: 14px; - padding: 15px 20px; - caret-color: rgba(255, 255, 255, 0.25); -} - -.form__textarea::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__textarea::placeholder { - opacity: 0; -} - -.form__textarea:focus + .form__label, -.form__textarea:not(:placeholder-shown) + .form__label { - opacity: 0; -} - -.form__label { - position: absolute; - top: 13.5px; - left: 20px; - color: #fff; - opacity: 0.42; - pointer-events: none; -} - -.form__label::selection, -.form__label *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -.form__icon { - font-size: 12px; - margin-left: 4px; - color: #fff; - opacity: 0.5; -} - -.form__bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 20px; - margin-top: -8px; -} - -/* COUNTER */ -.counter { - font-weight: 500; - font-size: 13px; - color: rgba(255, 255, 255, 0.25); - pointer-events: none; -} - -.counter::selection, -.counter > *::selection { - color: rgba(255, 255, 255, 0.85); - background-color: rgba(255, 255, 255, 0.05); -} - -/* SUBMIT BTN */ -.submit-btn { - color: #161921; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - padding: 4px 16px 9px; - border-radius: 500px; - font-weight: 600; - background-color: #fff; - cursor: pointer; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(-2px) translateZ(0) rotate(0); -} - -.submit-btn:hover, -.submit-btn:focus { - transform: translateY(-2px) translateZ(0) scale(1.15); -} - -.submit-btn:active { - transform: translateY(-2px) translateZ(0) scale(1.07); -} - -.submit-btn:hover .submit-btn__text { - transform: translateY(2px) translateZ(0); -} - -.submit-btn__text { - display: block; - transition: all 0.2s; - backface-visibility: hidden; - transform: translateY(2px) translateZ(0) rotate(0); -} - -.submit-btn__text::selection { - color: rgba(0, 0, 0, 0.85); - background-color: rgba(0, 0, 0, 0.1); -} - -/* FEEDBACKS */ -.feedbacks { - height: 573px; - overflow-y: scroll; - overflow-x: hidden; - /* background-color: #F3F6F8; */ - background-color: #f7f8f9; - - scrollbar-color: #979ca0 #dbdfe4; /* Firefox */ - scrollbar-width: thin; /* Firefox */ -} - -.feedbacks::-webkit-scrollbar { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - width: 7px; -} - -.feedbacks::-webkit-scrollbar-track { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #dbdfe4; -} - -.feedbacks::-webkit-scrollbar-thumb { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #979ca0; - transition: all 0.2s; -} - -.feedbacks::-webkit-scrollbar-thumb:hover { /* Chrome, Safari, Edge, Opera, All browsers on iOS */ - background-color: #787c80; -} - -/* FEEDBACK ITEM */ -.feedback { - display: grid; - grid-template-columns: 40px 85px 6fr 1fr; - align-items: center; - height: 82px; - padding-right: 35px; - padding-left: 30px; - cursor: pointer; - border-bottom: 1px solid #e4e7eb; - transition: all 0.2s; - color: #3a3c42; -} - -.feedback--expand { - height: 100px; - background-color: #fff; -} - -.feedback--expand .feedback__text { - -webkit-line-clamp: initial !important; - -webkit-box-orient: initial !important; - overflow: initial !important; -} - -.feedback *::selection { - background-color: rgba(0, 0, 0, 0.1); -} - -.feedback:hover { - background-color: #fff; -} - -.feedback:hover .upvote, -.feedback:hover .feedback__badge, -.feedback:hover .feedback__content, -.feedback:hover .feedback__date { - transform: translateX(5px); -} - -.feedback__badge { - height: 49px; - width: 49px; - border-radius: 6px; - background-color: #564989; - display: flex; - justify-content: center; - align-items: center; - margin-right: 16px; - transition: all 0.2s; - margin-left: 20px; -} - -.feedback:nth-child(6n+2) .feedback__badge { - background-color: #6D4989; -} - -.feedback:nth-child(6n+3) .feedback__badge { - background-color: #3c7789; -} - -.feedback:nth-child(6n+4) .feedback__badge { - background-color: #897749; -} - -.feedback:nth-child(6n+5) .feedback__badge { - background-color: #4a8b6b; -} - -.feedback:nth-child(6n+6) .feedback__badge { - background-color: #495789; -} - -.feedback__letter { - font-size: 24px; - color: #fff; - font-weight: 700; - margin-right: -2px; -} - -.feedback__content { - transition: all 0.2s; -} - -.feedback__company { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - color: #898D96; - margin-top: -1px; - display: block; - transition: all 0.2s; -} - -.feedback__text { - color: #141518; - font-size: 13px; - margin-top: 1px; - transition: all 0.2s; - - display: -webkit-box; - max-width: 100%; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.feedback__date { - font-size: 12px; - color: #898b92; - margin-left: auto; - transition: all 0.2s; -} - -/* UPVOTE BTN */ -.upvote { - cursor: pointer; - height: 40px; - width: 40px; - border-radius: 6px; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.upvote:hover { - background-color: #F3F6F8; -} - -.upvote:hover .upvote__icon, -.upvote:hover .upvote__count { - color: #784a86; -} - -.upvote:disabled .upvote__icon { - display: none; -} - -.upvote:disabled { - pointer-events: none; -} - -.upvote__icon { - color: #D7DBE2; - font-size: 19px; - display: block; - transition: all 0.2s; -} - -.upvote__count { - color: #6C6F76; - font-size: 11px; - margin-top: -1px; -} - -/* HASHTAGS */ -.hashtags { - position: absolute; - right: -137px; - top: 0px; -} - -.hashtags__item { - max-width: max-content; - margin-bottom: 11px; -} - -/* HASHTAG */ -.hashtag { - align-self: flex-start; - font-size: 13px; - font-weight: 400; - color: rgba(255, 255, 255, 0.6); - padding: 7px 14px 9px; - background-color: rgba(255, 255, 255, 0.06); - border-radius: 500px; - cursor: pointer; - transition: all 0.2s; -} - -.hashtag:hover, -.hashtag:focus { - color: #fff; - transform: scale(1.11); -} - -.hashtag:active { - transform: scale(1.06); -} - -.hashtag::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -/* FOOTER */ -.footer { - position: absolute; - transform: rotate(-90deg); - left: -225px; - bottom: 174px; -} - -/* COPYRIGHT */ -.copyright { - color: #A6ADB5; - font-size: 11px; -} - -.copyright *::selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.copyright__text { - opacity: 0.4; -} - -.copyright__link { - text-decoration: underline; -} - -.copyright__icon { - font-size: 10px; - opacity: 0.5; - margin-right: 4px; - margin-left: 2px; -} - -/* SPINNER */ -.spinner { - position: absolute; - left: 50%; - top: 46%; - transform: translateX(-50%) translateZ(0); - border-radius: 50%; - width: 100px; - height: 100px; - border-top: 7px solid #e2e7e9; - border-right: 7px solid #e2e7e9; - border-bottom: 7px solid #e2e7e9; - border-left: 7px solid #ccd1d3; - animation: spinner 1s infinite linear; -} - - -/* MEDIA QUERIES */ -@media (max-height: 925px) { - .body { - padding: 60px 0; - } -} - -@media (max-width: 1050px) { - .body__container { - flex-direction: column-reverse; - height: initial; - } - - .hashtags { - display: none; - } - - .footer { - transform: initial; - position: relative; - left: initial; - bottom: initial; - text-align: center; - margin-top: 20px; - } -} - -@media (max-width: 775px) { - .body { - padding-top: 0; - padding-bottom: 20px; - align-items: initial; - } - - .body__container { - width: 100vw; - } - - .app { - width: 100%; - border-radius: 0; - } - - .feedback__badge { - min-width: 49px; - } - - .feedback__content { - padding-right: 25px; - } - - .feedback__date { - margin-left: auto; - } -} - -@media (max-width: 525px) { - .header { - padding: 35px 15px; - height: initial; - } - - .first-heading { - text-align: center; - } - - .form { - width: initial; - align-self: stretch; - } - - .form__label { - padding-right: 20px; - } - - .feedback { - grid-template-columns: 40px 85px 1fr; - padding-right: 15px; - padding-left: 15px; - } - - .feedback--expand { - height: initial; - padding-top: 10px; - padding-bottom: 10px; - } - - .feedback__date { - display: none; - } - - .footer { - padding: 0 15px; - } -} \ No newline at end of file diff --git a/bugs.md b/bugs.md deleted file mode 100644 index 008c4f3..0000000 --- a/bugs.md +++ /dev/null @@ -1,2 +0,0 @@ -# Fixes for possible bugs you may have - diff --git a/readme.md b/readme.md index fafb2dc..3eaa936 100644 --- a/readme.md +++ b/readme.md @@ -1,18 +1,17 @@ -# Professional JavaScript Course -- [See course here](https://bytegrad.com/courses/professional-javascript) +# Professional React & Next.js Course -- [See course here](https://bytegrad.com/courses/professional-react-nextjs) -**You can find the final code for each video in the project folders**. Use that if you get stuck. There is also a bugs file, a slides file and a readme file (you're reading it right now). +**Welcome to the course!**. Here, you can find all the resources you will need. ## Something is not working! What should I do? Follow these steps: -1. Check if you made any typos. Also, check your terminals (if we're using those in the videos) to see if there are errors, fix those and try again. This will fix your issue 99% of the time so make sure you've done this thoroughly. -2. Still not working? Check the file in this repo called 'bugs' and see if your problem is present and follow the steps there. -3. Still not working? Copy and paste the final code of the video you're stuck at (you can find the final code in the project folders). If you change the **package.json** file, run the command `npm install` to make sure you have the correct dependencies installed. -4. Still not working? Please open an issue in this GitHub repository with a clear description of what's not working. I will help you as quickly as I can. But from my experience: the vast majority of your problems will be because of typos / simple mistakes. +1. Check if you made any typos. Also, check the browser console and terminals (if we're using those in the videos) to see if there are errors, fix those and try again. This will fix your issue 99% of the time so make sure you've done this thoroughly. +2. Still not working? Copy and paste the final code of the project for the files you're stuck at. If you change the **package.json** file, run the command `npm install` to make sure you have the correct dependencies installed. +3. Still not working? Please open an issue in this GitHub repository or submit a message in chat with a clear description of what's not working, ideally with screenshots of your code and error messages. I will help you as quickly as I can. But from my experience: the vast majority of your problems will be because of typos / simple mistakes. I'm online throughout most of the day and will help you as quickly as I can! In the meantime, I apologize for the inconvenience. Sometimes things break after recording the videos, but I try to be quick with any bugs that come up. ## How can I contact you? -Please open an issue in this GitHub repo or contact me on the private Discord (see 'resources' below video to find link). You can also contact me at support@bytegrad.com. +Please open an issue in this GitHub repo or contact me on the private Discord. You can also contact me at support@bytegrad.com. diff --git a/slides.md b/slides.md deleted file mode 100644 index 5bae2c7..0000000 --- a/slides.md +++ /dev/null @@ -1,7 +0,0 @@ -![](https://bytegrad.com/course-assets/js/slides/1.png) -![](https://bytegrad.com/course-assets/js/slides/2.png?v=3) -![](https://bytegrad.com/course-assets/js/slides/3.png?v=3) -![](https://bytegrad.com/course-assets/js/slides/4.png?v=3) -![](https://bytegrad.com/course-assets/js/slides/5.png) -![](https://bytegrad.com/course-assets/js/slides/6.png) -![](https://bytegrad.com/course-assets/js/slides/7.png) \ No newline at end of file From 984b43f9d1cfd713c192c85bae75a40f688143fd Mon Sep 17 00:00:00 2001 From: ByteGrad Date: Wed, 22 Nov 2023 12:49:24 +0100 Subject: [PATCH 26/40] readme --- readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index 3eaa936..9deec59 100644 --- a/readme.md +++ b/readme.md @@ -1,17 +1,17 @@ -# Professional React & Next.js Course -- [See course here](https://bytegrad.com/courses/professional-react-nextjs) +# Professional React & Next.js Course ([See course here](https://bytegrad.com/courses/professional-react-nextjs)) -**Welcome to the course!**. Here, you can find all the resources you will need. +**Welcome to the course!** Here, you can find all the resources you will need. ## Something is not working! What should I do? Follow these steps: -1. Check if you made any typos. Also, check the browser console and terminals (if we're using those in the videos) to see if there are errors, fix those and try again. This will fix your issue 99% of the time so make sure you've done this thoroughly. -2. Still not working? Copy and paste the final code of the project for the files you're stuck at. If you change the **package.json** file, run the command `npm install` to make sure you have the correct dependencies installed. +1. Check if you made any typos. Also, check the browser console and terminal (if we're using that in the video) to see if there are errors, fix those and try again. This will fix your issue 99% of the time so make sure you've done this thoroughly. +2. Still not working? Copy and paste the final code of the project for the file you're stuck at. If you change the **package.json** file, run the command `npm install` to make sure you have the correct dependencies installed. 3. Still not working? Please open an issue in this GitHub repository or submit a message in chat with a clear description of what's not working, ideally with screenshots of your code and error messages. I will help you as quickly as I can. But from my experience: the vast majority of your problems will be because of typos / simple mistakes. I'm online throughout most of the day and will help you as quickly as I can! In the meantime, I apologize for the inconvenience. Sometimes things break after recording the videos, but I try to be quick with any bugs that come up. ## How can I contact you? -Please open an issue in this GitHub repo or contact me on the private Discord. You can also contact me at support@bytegrad.com. +Please open an issue in this GitHub repo or post a message in the private Discord. You can also contact me at support@bytegrad.com. From 357da6dc9b470f217049c464ba80e4ebf69bc663 Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 22 Nov 2023 13:17:07 +0100 Subject: [PATCH 27/40] add final code projects --- corp-comment/final-code/.eslintrc.cjs | 18 + corp-comment/final-code/.gitignore | 24 + corp-comment/final-code/README.md | 27 + corp-comment/final-code/index.html | 19 + corp-comment/final-code/package-lock.json | 3000 +++++++++++ corp-comment/final-code/package.json | 30 + .../final-code/src/components/App.tsx | 27 + .../src/components/ErrorMessage.tsx | 3 + .../final-code/src/components/Logo.tsx | 7 + .../final-code/src/components/PageHeading.tsx | 7 + .../final-code/src/components/Pattern.tsx | 9 + .../final-code/src/components/Spinner.tsx | 3 + .../src/components/feedback/FeedbackForm.tsx | 66 + .../src/components/feedback/FeedbackItem.tsx | 41 + .../src/components/feedback/FeedbackList.tsx | 24 + .../src/components/hashtag/HashtagItem.tsx | 15 + .../src/components/hashtag/HashtagList.tsx | 19 + .../src/components/layout/Container.tsx | 11 + .../src/components/layout/Footer.tsx | 19 + .../src/components/layout/Header.tsx | 18 + .../contexts/FeedbackItemsContextProvider.tsx | 95 + corp-comment/final-code/src/index.css | 675 +++ corp-comment/final-code/src/lib/constants.ts | 1 + corp-comment/final-code/src/lib/hooks.ts | 51 + corp-comment/final-code/src/lib/types.ts | 8 + corp-comment/final-code/src/main.tsx | 10 + .../src/stores/feedbackItemsStore.ts | 101 + corp-comment/final-code/src/vite-env.d.ts | 1 + corp-comment/final-code/tsconfig.json | 25 + corp-comment/final-code/tsconfig.node.json | 10 + corp-comment/final-code/vite.config.ts | 7 + corp-comment/resources.txt | 0 evento/final-code/.eslintrc.json | 3 + evento/final-code/.gitignore | 37 + evento/final-code/README.md | 36 + evento/final-code/next.config.js | 17 + evento/final-code/package-lock.json | 4615 +++++++++++++++++ evento/final-code/package.json | 40 + evento/final-code/postcss.config.js | 6 + evento/final-code/prisma/schema.prisma | 26 + evento/final-code/prisma/seed.ts | 278 + evento/final-code/public/next.svg | 1 + evento/final-code/public/vercel.svg | 1 + evento/final-code/src/app/error.tsx | 32 + .../src/app/event/[slug]/loading.tsx | 11 + .../src/app/event/[slug]/opengraph-image.tsx | 21 + .../final-code/src/app/event/[slug]/page.tsx | 112 + .../src/app/events/[city]/loading.tsx | 11 + .../final-code/src/app/events/[city]/page.tsx | 51 + evento/final-code/src/app/favicon.ico | Bin 0 -> 730 bytes evento/final-code/src/app/globals.css | 31 + evento/final-code/src/app/layout.tsx | 33 + evento/final-code/src/app/not-found.tsx | 3 + evento/final-code/src/app/page.tsx | 29 + .../src/app/privacy-policy/page.tsx | 3 + .../src/app/terms-conditions/page.tsx | 3 + .../final-code/src/components/container.tsx | 11 + .../final-code/src/components/event-card.tsx | 70 + .../final-code/src/components/events-list.tsx | 26 + evento/final-code/src/components/footer.tsx | 30 + evento/final-code/src/components/h1.tsx | 16 + evento/final-code/src/components/header.tsx | 54 + evento/final-code/src/components/logo.tsx | 15 + .../src/components/pagination-controls.tsx | 35 + .../final-code/src/components/search-form.tsx | 29 + .../src/components/skeleton-card.tsx | 11 + evento/final-code/src/components/skeleton.tsx | 16 + evento/final-code/src/lib/db.ts | 17 + evento/final-code/src/lib/server-utils.ts | 48 + evento/final-code/src/lib/types.ts | 1 + evento/final-code/src/lib/utils.ts | 16 + evento/final-code/src/middleware.ts | 9 + evento/final-code/tailwind.config.ts | 18 + evento/final-code/tsconfig.json | 27 + evento/resources.txt | 0 fancy-counter/final-code/.eslintrc.cjs | 21 + fancy-counter/final-code/.gitignore | 24 + fancy-counter/final-code/README.md | 8 + fancy-counter/final-code/index.html | 15 + fancy-counter/final-code/package-lock.json | 3836 ++++++++++++++ fancy-counter/final-code/package.json | 27 + fancy-counter/final-code/public/vite.svg | 1 + fancy-counter/final-code/src/App.jsx | 11 + .../final-code/src/ButtonContainer.jsx | 3 + fancy-counter/final-code/src/Card.jsx | 42 + fancy-counter/final-code/src/Count.jsx | 3 + fancy-counter/final-code/src/CountButton.jsx | 33 + fancy-counter/final-code/src/ResetButton.jsx | 14 + fancy-counter/final-code/src/Title.jsx | 13 + fancy-counter/final-code/src/index.css | 138 + fancy-counter/final-code/src/main.jsx | 10 + fancy-counter/final-code/vite.config.js | 7 + fancy-counter/resources.txt | 0 trekbag/final-code/.eslintrc.cjs | 21 + trekbag/final-code/.gitignore | 24 + trekbag/final-code/README.md | 8 + trekbag/final-code/index.html | 19 + trekbag/final-code/package-lock.json | 4193 +++++++++++++++ trekbag/final-code/package.json | 28 + .../final-code/src/components/AddItemForm.jsx | 36 + trekbag/final-code/src/components/App.jsx | 23 + .../src/components/BackgroundHeading.jsx | 3 + trekbag/final-code/src/components/Button.jsx | 10 + .../final-code/src/components/ButtonGroup.jsx | 43 + trekbag/final-code/src/components/Counter.jsx | 7 + .../final-code/src/components/EmptyView.jsx | 8 + trekbag/final-code/src/components/Footer.jsx | 10 + trekbag/final-code/src/components/Header.jsx | 17 + .../final-code/src/components/ItemList.jsx | 86 + trekbag/final-code/src/components/Logo.jsx | 3 + trekbag/final-code/src/components/Sidebar.jsx | 17 + trekbag/final-code/src/index.css | 306 ++ trekbag/final-code/src/lib/constants.js | 17 + trekbag/final-code/src/lib/hooks.js | 14 + trekbag/final-code/src/main.jsx | 10 + trekbag/final-code/src/stores/itemsStore.js | 65 + trekbag/final-code/vite.config.js | 7 + trekbag/resources.txt | 0 118 files changed, 19401 insertions(+) create mode 100644 corp-comment/final-code/.eslintrc.cjs create mode 100644 corp-comment/final-code/.gitignore create mode 100644 corp-comment/final-code/README.md create mode 100644 corp-comment/final-code/index.html create mode 100644 corp-comment/final-code/package-lock.json create mode 100644 corp-comment/final-code/package.json create mode 100644 corp-comment/final-code/src/components/App.tsx create mode 100644 corp-comment/final-code/src/components/ErrorMessage.tsx create mode 100644 corp-comment/final-code/src/components/Logo.tsx create mode 100644 corp-comment/final-code/src/components/PageHeading.tsx create mode 100644 corp-comment/final-code/src/components/Pattern.tsx create mode 100644 corp-comment/final-code/src/components/Spinner.tsx create mode 100644 corp-comment/final-code/src/components/feedback/FeedbackForm.tsx create mode 100644 corp-comment/final-code/src/components/feedback/FeedbackItem.tsx create mode 100644 corp-comment/final-code/src/components/feedback/FeedbackList.tsx create mode 100644 corp-comment/final-code/src/components/hashtag/HashtagItem.tsx create mode 100644 corp-comment/final-code/src/components/hashtag/HashtagList.tsx create mode 100644 corp-comment/final-code/src/components/layout/Container.tsx create mode 100644 corp-comment/final-code/src/components/layout/Footer.tsx create mode 100644 corp-comment/final-code/src/components/layout/Header.tsx create mode 100644 corp-comment/final-code/src/contexts/FeedbackItemsContextProvider.tsx create mode 100644 corp-comment/final-code/src/index.css create mode 100644 corp-comment/final-code/src/lib/constants.ts create mode 100644 corp-comment/final-code/src/lib/hooks.ts create mode 100644 corp-comment/final-code/src/lib/types.ts create mode 100644 corp-comment/final-code/src/main.tsx create mode 100644 corp-comment/final-code/src/stores/feedbackItemsStore.ts create mode 100644 corp-comment/final-code/src/vite-env.d.ts create mode 100644 corp-comment/final-code/tsconfig.json create mode 100644 corp-comment/final-code/tsconfig.node.json create mode 100644 corp-comment/final-code/vite.config.ts create mode 100644 corp-comment/resources.txt create mode 100644 evento/final-code/.eslintrc.json create mode 100644 evento/final-code/.gitignore create mode 100644 evento/final-code/README.md create mode 100644 evento/final-code/next.config.js create mode 100644 evento/final-code/package-lock.json create mode 100644 evento/final-code/package.json create mode 100644 evento/final-code/postcss.config.js create mode 100644 evento/final-code/prisma/schema.prisma create mode 100644 evento/final-code/prisma/seed.ts create mode 100644 evento/final-code/public/next.svg create mode 100644 evento/final-code/public/vercel.svg create mode 100644 evento/final-code/src/app/error.tsx create mode 100644 evento/final-code/src/app/event/[slug]/loading.tsx create mode 100644 evento/final-code/src/app/event/[slug]/opengraph-image.tsx create mode 100644 evento/final-code/src/app/event/[slug]/page.tsx create mode 100644 evento/final-code/src/app/events/[city]/loading.tsx create mode 100644 evento/final-code/src/app/events/[city]/page.tsx create mode 100644 evento/final-code/src/app/favicon.ico create mode 100644 evento/final-code/src/app/globals.css create mode 100644 evento/final-code/src/app/layout.tsx create mode 100644 evento/final-code/src/app/not-found.tsx create mode 100644 evento/final-code/src/app/page.tsx create mode 100644 evento/final-code/src/app/privacy-policy/page.tsx create mode 100644 evento/final-code/src/app/terms-conditions/page.tsx create mode 100644 evento/final-code/src/components/container.tsx create mode 100644 evento/final-code/src/components/event-card.tsx create mode 100644 evento/final-code/src/components/events-list.tsx create mode 100644 evento/final-code/src/components/footer.tsx create mode 100644 evento/final-code/src/components/h1.tsx create mode 100644 evento/final-code/src/components/header.tsx create mode 100644 evento/final-code/src/components/logo.tsx create mode 100644 evento/final-code/src/components/pagination-controls.tsx create mode 100644 evento/final-code/src/components/search-form.tsx create mode 100644 evento/final-code/src/components/skeleton-card.tsx create mode 100644 evento/final-code/src/components/skeleton.tsx create mode 100644 evento/final-code/src/lib/db.ts create mode 100644 evento/final-code/src/lib/server-utils.ts create mode 100644 evento/final-code/src/lib/types.ts create mode 100644 evento/final-code/src/lib/utils.ts create mode 100644 evento/final-code/src/middleware.ts create mode 100644 evento/final-code/tailwind.config.ts create mode 100644 evento/final-code/tsconfig.json create mode 100644 evento/resources.txt create mode 100644 fancy-counter/final-code/.eslintrc.cjs create mode 100644 fancy-counter/final-code/.gitignore create mode 100644 fancy-counter/final-code/README.md create mode 100644 fancy-counter/final-code/index.html create mode 100644 fancy-counter/final-code/package-lock.json create mode 100644 fancy-counter/final-code/package.json create mode 100644 fancy-counter/final-code/public/vite.svg create mode 100644 fancy-counter/final-code/src/App.jsx create mode 100644 fancy-counter/final-code/src/ButtonContainer.jsx create mode 100644 fancy-counter/final-code/src/Card.jsx create mode 100644 fancy-counter/final-code/src/Count.jsx create mode 100644 fancy-counter/final-code/src/CountButton.jsx create mode 100644 fancy-counter/final-code/src/ResetButton.jsx create mode 100644 fancy-counter/final-code/src/Title.jsx create mode 100644 fancy-counter/final-code/src/index.css create mode 100644 fancy-counter/final-code/src/main.jsx create mode 100644 fancy-counter/final-code/vite.config.js create mode 100644 fancy-counter/resources.txt create mode 100644 trekbag/final-code/.eslintrc.cjs create mode 100644 trekbag/final-code/.gitignore create mode 100644 trekbag/final-code/README.md create mode 100644 trekbag/final-code/index.html create mode 100644 trekbag/final-code/package-lock.json create mode 100644 trekbag/final-code/package.json create mode 100644 trekbag/final-code/src/components/AddItemForm.jsx create mode 100644 trekbag/final-code/src/components/App.jsx create mode 100644 trekbag/final-code/src/components/BackgroundHeading.jsx create mode 100644 trekbag/final-code/src/components/Button.jsx create mode 100644 trekbag/final-code/src/components/ButtonGroup.jsx create mode 100644 trekbag/final-code/src/components/Counter.jsx create mode 100644 trekbag/final-code/src/components/EmptyView.jsx create mode 100644 trekbag/final-code/src/components/Footer.jsx create mode 100644 trekbag/final-code/src/components/Header.jsx create mode 100644 trekbag/final-code/src/components/ItemList.jsx create mode 100644 trekbag/final-code/src/components/Logo.jsx create mode 100644 trekbag/final-code/src/components/Sidebar.jsx create mode 100644 trekbag/final-code/src/index.css create mode 100644 trekbag/final-code/src/lib/constants.js create mode 100644 trekbag/final-code/src/lib/hooks.js create mode 100644 trekbag/final-code/src/main.jsx create mode 100644 trekbag/final-code/src/stores/itemsStore.js create mode 100644 trekbag/final-code/vite.config.js create mode 100644 trekbag/resources.txt diff --git a/corp-comment/final-code/.eslintrc.cjs b/corp-comment/final-code/.eslintrc.cjs new file mode 100644 index 0000000..d6c9537 --- /dev/null +++ b/corp-comment/final-code/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/corp-comment/final-code/.gitignore b/corp-comment/final-code/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/corp-comment/final-code/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/corp-comment/final-code/README.md b/corp-comment/final-code/README.md new file mode 100644 index 0000000..1ebe379 --- /dev/null +++ b/corp-comment/final-code/README.md @@ -0,0 +1,27 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname, + }, +``` + +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/corp-comment/final-code/index.html b/corp-comment/final-code/index.html new file mode 100644 index 0000000..ce7d897 --- /dev/null +++ b/corp-comment/final-code/index.html @@ -0,0 +1,19 @@ + + + + + + + + + CorpComment -- Give Feedback, Publicly + + +
    + + + diff --git a/corp-comment/final-code/package-lock.json b/corp-comment/final-code/package-lock.json new file mode 100644 index 0000000..41ec8b9 --- /dev/null +++ b/corp-comment/final-code/package-lock.json @@ -0,0 +1,3000 @@ +{ + "name": "corpcomment", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "corpcomment", + "version": "0.0.0", + "dependencies": { + "@radix-ui/react-icons": "^1.3.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "zustand": "^4.4.4" + }, + "devDependencies": { + "@types/react": "18.2.15", + "@types/react-dom": "18.2.7", + "@typescript-eslint/eslint-plugin": "6.0.0", + "@typescript-eslint/parser": "6.0.0", + "@vitejs/plugin-react": "4.0.3", + "eslint": "8.45.0", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-refresh": "0.4.3", + "typescript": "5.0.2", + "vite": "4.4.5" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", + "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", + "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", + "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", + "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", + "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.9", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.9.tgz", + "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==", + "devOptional": true + }, + "node_modules/@types/react": { + "version": "18.2.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.15.tgz", + "integrity": "sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA==", + "devOptional": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", + "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.5.tgz", + "integrity": "sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==", + "devOptional": true + }, + "node_modules/@types/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.0.0.tgz", + "integrity": "sha512-xuv6ghKGoiq856Bww/yVYnXGsKa588kY3M0XK7uUW/3fJNNULKRfZfSBkMTSpqGG/8ZCXCadfh8G/z/B4aqS/A==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.0", + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/type-utils": "6.0.0", + "@typescript-eslint/utils": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.5.0", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.0.0.tgz", + "integrity": "sha512-TNaufYSPrr1U8n+3xN+Yp9g31vQDJqhXzzPSHfQDLcaO4tU+mCfODPxCwf4H530zo7aUBE3QIdxCXamEnG04Tg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/typescript-estree": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.0.0.tgz", + "integrity": "sha512-o4q0KHlgCZTqjuaZ25nw5W57NeykZT9LiMEG4do/ovwvOcPnDO1BI5BQdCsUkjxFyrCL0cSzLjvIMfR9uo7cWg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.0.0.tgz", + "integrity": "sha512-ah6LJvLgkoZ/pyJ9GAdFkzeuMZ8goV6BH7eC9FPmojrnX9yNCIsfjB+zYcnex28YO3RFvBkV6rMV6WpIqkPvoQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.0.0", + "@typescript-eslint/utils": "6.0.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.0.0.tgz", + "integrity": "sha512-Zk9KDggyZM6tj0AJWYYKgF0yQyrcnievdhG0g5FqyU3Y2DRxJn4yWY21sJC0QKBckbsdKKjYDV2yVrrEvuTgxg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.0.0.tgz", + "integrity": "sha512-2zq4O7P6YCQADfmJ5OTDQTP3ktajnXIRrYAtHM9ofto/CJZV3QfJ89GEaM2BNGeSr1KgmBuLhEkz5FBkS2RQhQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.0", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.0.0.tgz", + "integrity": "sha512-SOr6l4NB6HE4H/ktz0JVVWNXqCJTOo/mHnvIte1ZhBQ0Cvd04x5uKZa3zT6tiodL06zf5xxdK8COiDvPnQ27JQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.3.0", + "@types/json-schema": "^7.0.11", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/typescript-estree": "6.0.0", + "eslint-scope": "^5.1.1", + "semver": "^7.5.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.0.0.tgz", + "integrity": "sha512-cvJ63l8c0yXdeT5POHpL0Q1cZoRcmRKFCtSjNGJxPkcP571EfZMcNbzWAc7oK3D1dRzm/V5EwtkANTZxqvuuUA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.0.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.3.tgz", + "integrity": "sha512-pwXDog5nwwvSIzwrvYYmA2Ljcd/ZNlcsSG2Q9CNDBwnsd55UGAyr2doXtB5j+2uymRCnCfExlznzzSFbBRcoCg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.22.5", + "@babel/plugin-transform-react-jsx-self": "^7.22.5", + "@babel/plugin-transform-react-jsx-source": "^7.22.5", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0" + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001559", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz", + "integrity": "sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "devOptional": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.572", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.572.tgz", + "integrity": "sha512-RlFobl4D3ieetbnR+2EpxdzFl9h0RAJkPK3pfiwMug2nhBin2ZCsGIAJWdpNniLz43sgXam/CgipOmvTA+rUiA==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", + "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.6.0", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.3.tgz", + "integrity": "sha512-Hh0wv8bUNY877+sI0BlCUlsS0TYYQqvzEwJsJJPM2WF4RnTStSnSR3zdJYa2nPOJgg3UghXi54lVyMSmpCalzA==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", + "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/vite": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.5.tgz", + "integrity": "sha512-4m5kEtAWHYr0O1Fu7rZp64CfO1PsRGZlD3TAB32UmQlpd7qg15VF7ROqGN5CyqN7HFuwr7ICNM2+fDWRqFEKaA==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.26", + "rollup": "^3.25.2" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.4.tgz", + "integrity": "sha512-5UTUIAiHMNf5+mFp7/AnzJXS7+XxktULFN0+D1sCiZWyX7ZG+AQpqs2qpYrynRij4QvoDdCD+U+bmg/cG3Ucxw==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/corp-comment/final-code/package.json b/corp-comment/final-code/package.json new file mode 100644 index 0000000..ae8a6af --- /dev/null +++ b/corp-comment/final-code/package.json @@ -0,0 +1,30 @@ +{ + "name": "corpcomment", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@radix-ui/react-icons": "^1.3.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "zustand": "^4.4.4" + }, + "devDependencies": { + "@types/react": "18.2.15", + "@types/react-dom": "18.2.7", + "@typescript-eslint/eslint-plugin": "6.0.0", + "@typescript-eslint/parser": "6.0.0", + "@vitejs/plugin-react": "4.0.3", + "eslint": "8.45.0", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-refresh": "0.4.3", + "typescript": "5.0.2", + "vite": "4.4.5" + } +} diff --git a/corp-comment/final-code/src/components/App.tsx b/corp-comment/final-code/src/components/App.tsx new file mode 100644 index 0000000..7fb3894 --- /dev/null +++ b/corp-comment/final-code/src/components/App.tsx @@ -0,0 +1,27 @@ +import Container from "./layout/Container"; +import Footer from "./layout/Footer"; +import HashtagList from "./hashtag/HashtagList"; +import { useFeedbackItemsStore } from "../stores/feedbackItemsStore"; +import { useEffect } from "react"; + +function App() { + const fetchFeedbackItems = useFeedbackItemsStore( + (state) => state.fetchFeedbackItems + ); + + useEffect(() => { + fetchFeedbackItems(); + }, [fetchFeedbackItems]); + + return ( +
    +
    + + + + +
    + ); +} + +export default App; diff --git a/corp-comment/final-code/src/components/ErrorMessage.tsx b/corp-comment/final-code/src/components/ErrorMessage.tsx new file mode 100644 index 0000000..83cb4b9 --- /dev/null +++ b/corp-comment/final-code/src/components/ErrorMessage.tsx @@ -0,0 +1,3 @@ +export default function ErrorMessage({ message }) { + return
    {message}
    ; +} diff --git a/corp-comment/final-code/src/components/Logo.tsx b/corp-comment/final-code/src/components/Logo.tsx new file mode 100644 index 0000000..b59fd07 --- /dev/null +++ b/corp-comment/final-code/src/components/Logo.tsx @@ -0,0 +1,7 @@ +export default function Logo() { + return ( + + logo + + ); +} diff --git a/corp-comment/final-code/src/components/PageHeading.tsx b/corp-comment/final-code/src/components/PageHeading.tsx new file mode 100644 index 0000000..2b7e656 --- /dev/null +++ b/corp-comment/final-code/src/components/PageHeading.tsx @@ -0,0 +1,7 @@ +export default function PageHeading() { + return ( +

    + Give Feedback. Publicly. +

    + ); +} diff --git a/corp-comment/final-code/src/components/Pattern.tsx b/corp-comment/final-code/src/components/Pattern.tsx new file mode 100644 index 0000000..eacd094 --- /dev/null +++ b/corp-comment/final-code/src/components/Pattern.tsx @@ -0,0 +1,9 @@ +export default function Pattern() { + return ( + pattern + ); +} diff --git a/corp-comment/final-code/src/components/Spinner.tsx b/corp-comment/final-code/src/components/Spinner.tsx new file mode 100644 index 0000000..b8f01cc --- /dev/null +++ b/corp-comment/final-code/src/components/Spinner.tsx @@ -0,0 +1,3 @@ +export default function Spinner() { + return
    ; +} diff --git a/corp-comment/final-code/src/components/feedback/FeedbackForm.tsx b/corp-comment/final-code/src/components/feedback/FeedbackForm.tsx new file mode 100644 index 0000000..af7e983 --- /dev/null +++ b/corp-comment/final-code/src/components/feedback/FeedbackForm.tsx @@ -0,0 +1,66 @@ +import { useState } from "react"; +import { MAX_CHARACTERS } from "../../lib/constants"; + +type FeedbackFormProps = { + onAddToList: (text: string) => void; +}; + +export default function FeedbackForm({ onAddToList }: FeedbackFormProps) { + const [text, setText] = useState(""); + const [showValidIndicator, setShowValidIndicator] = useState(false); + const [showInvalidIndicator, setShowInvalidIndicator] = useState(false); + const charCount = MAX_CHARACTERS - text.length; + + const handleChange = (event: React.ChangeEvent) => { + const newText = event.target.value; + if (newText.length > MAX_CHARACTERS) { + return; + } + setText(newText); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + + // basic validation + if (text.includes("#") && text.length >= 5) { + setShowValidIndicator(true); + setTimeout(() => setShowValidIndicator(false), 2000); + } else { + setShowInvalidIndicator(true); + setTimeout(() => setShowInvalidIndicator(false), 2000); + return; + } + + onAddToList(text); + setText(""); + }; + + return ( +
    + + + + + ); +} diff --git a/word-analytics/final-code/src/components/Warning.jsx b/word-analytics/final-code/src/components/Warning.jsx new file mode 100644 index 0000000..4da9618 --- /dev/null +++ b/word-analytics/final-code/src/components/Warning.jsx @@ -0,0 +1,3 @@ +export default function Warning({ warningText }) { + return

    {warningText}

    ; +} diff --git a/word-analytics/final-code/src/index.css b/word-analytics/final-code/src/index.css new file mode 100644 index 0000000..a977dfb --- /dev/null +++ b/word-analytics/final-code/src/index.css @@ -0,0 +1,168 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +textarea { + all: unset; +} + +body { + font-family: "Inter", sans-serif; + background-color: #e1e8eb; + display: flex; + flex-direction: column; + align-items: center; +} + +.bg { + position: absolute; + top: 0; + left: 0; + z-index: -1; + height: 254px; + width: 100%; + background-image: linear-gradient( + to right, + rgba(101, 0, 163, 0.9), + rgba(24, 120, 175, 0.9) + ), + url("https://images.unsplash.com/photo-1546453667-8a8d2d07bc20?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1974&q=100"); +} + +.first-heading { + font-size: 65px; + letter-spacing: 5px; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.92); + margin-top: 75px; + text-align: center; +} + +.first-heading--thin { + font-weight: 300; +} + +.container { + width: 1050px; + height: 520px; + background-color: #fff; + border-radius: 10px; + box-shadow: 0 4px 4px rgba(0, 0, 0, 0.1); + margin-top: 55px; + display: flex; + overflow: hidden; + position: relative; +} + +.textarea { + resize: none; + flex: 2; + font: inherit; + padding: 30px 40px; + font-size: 22px; +} + +.textarea::placeholder { + font-weight: 500; + color: #959c9f; +} + +.textarea > textarea { + height: 100%; + width: 100%; +} + +.warning { + position: absolute; + bottom: 15px; + left: 35px; + color: red; + font-size: 14px; + margin-top: 5px; + margin-left: 5px; +} + +.stats { + flex: 1.3; + background-color: #f1f6f8; + display: flex; + flex-wrap: wrap; +} + +.stat { + flex: 1 150px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.stat:nth-child(1), +.stat:nth-child(2) { + border-bottom: 1px solid rgba(0, 0, 0, 0.08); +} + +.stat:nth-child(2), +.stat:nth-child(4) { + border-left: 1px solid rgba(0, 0, 0, 0.08); +} + +.stat__number { + color: #4d5457; + font-weight: 600; + font-size: 35px; + margin-bottom: 10px; +} + +.stat__number--limit { + color: red; +} + +.second-heading { + font-size: 14px; + text-transform: uppercase; + letter-spacing: 2px; + font-weight: 500; + color: #747a7c; +} + +.footer { + width: 1050px; + margin: 15px 0; + display: flex; + justify-content: space-between; + opacity: 0.4; +} + +@media (max-width: 1150px) { + .container { + width: 85vw; + flex-direction: column; + } + + .first-heading { + font-size: 7vw; + } + + .stat { + padding: 15px 10px; + } + + .stat:nth-child(1), + .stat:nth-child(2), + .stat:nth-child(4) { + border: none; + } + + .stat__number { + font-size: 20px; + } + + .footer { + width: initial; + flex-direction: column; + align-items: center; + } +} diff --git a/word-analytics/final-code/src/lib/constants.js b/word-analytics/final-code/src/lib/constants.js new file mode 100644 index 0000000..56c62f0 --- /dev/null +++ b/word-analytics/final-code/src/lib/constants.js @@ -0,0 +1,2 @@ +export const INSTAGRAM_MAX_CHARACTERS = 280; +export const FACEBOOK_MAX_CHARACTERS = 2200; diff --git a/word-analytics/final-code/src/main.jsx b/word-analytics/final-code/src/main.jsx new file mode 100644 index 0000000..3511724 --- /dev/null +++ b/word-analytics/final-code/src/main.jsx @@ -0,0 +1,10 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./components/App.jsx"; +import "./index.css"; + +ReactDOM.createRoot(document.getElementById("root")).render( + + + +); diff --git a/word-analytics/final-code/vite.config.js b/word-analytics/final-code/vite.config.js new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/word-analytics/final-code/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/word-analytics/resources/index.css b/word-analytics/resources/index.css new file mode 100644 index 0000000..a977dfb --- /dev/null +++ b/word-analytics/resources/index.css @@ -0,0 +1,168 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +textarea { + all: unset; +} + +body { + font-family: "Inter", sans-serif; + background-color: #e1e8eb; + display: flex; + flex-direction: column; + align-items: center; +} + +.bg { + position: absolute; + top: 0; + left: 0; + z-index: -1; + height: 254px; + width: 100%; + background-image: linear-gradient( + to right, + rgba(101, 0, 163, 0.9), + rgba(24, 120, 175, 0.9) + ), + url("https://images.unsplash.com/photo-1546453667-8a8d2d07bc20?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1974&q=100"); +} + +.first-heading { + font-size: 65px; + letter-spacing: 5px; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.92); + margin-top: 75px; + text-align: center; +} + +.first-heading--thin { + font-weight: 300; +} + +.container { + width: 1050px; + height: 520px; + background-color: #fff; + border-radius: 10px; + box-shadow: 0 4px 4px rgba(0, 0, 0, 0.1); + margin-top: 55px; + display: flex; + overflow: hidden; + position: relative; +} + +.textarea { + resize: none; + flex: 2; + font: inherit; + padding: 30px 40px; + font-size: 22px; +} + +.textarea::placeholder { + font-weight: 500; + color: #959c9f; +} + +.textarea > textarea { + height: 100%; + width: 100%; +} + +.warning { + position: absolute; + bottom: 15px; + left: 35px; + color: red; + font-size: 14px; + margin-top: 5px; + margin-left: 5px; +} + +.stats { + flex: 1.3; + background-color: #f1f6f8; + display: flex; + flex-wrap: wrap; +} + +.stat { + flex: 1 150px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.stat:nth-child(1), +.stat:nth-child(2) { + border-bottom: 1px solid rgba(0, 0, 0, 0.08); +} + +.stat:nth-child(2), +.stat:nth-child(4) { + border-left: 1px solid rgba(0, 0, 0, 0.08); +} + +.stat__number { + color: #4d5457; + font-weight: 600; + font-size: 35px; + margin-bottom: 10px; +} + +.stat__number--limit { + color: red; +} + +.second-heading { + font-size: 14px; + text-transform: uppercase; + letter-spacing: 2px; + font-weight: 500; + color: #747a7c; +} + +.footer { + width: 1050px; + margin: 15px 0; + display: flex; + justify-content: space-between; + opacity: 0.4; +} + +@media (max-width: 1150px) { + .container { + width: 85vw; + flex-direction: column; + } + + .first-heading { + font-size: 7vw; + } + + .stat { + padding: 15px 10px; + } + + .stat:nth-child(1), + .stat:nth-child(2), + .stat:nth-child(4) { + border: none; + } + + .stat__number { + font-size: 20px; + } + + .footer { + width: initial; + flex-direction: column; + align-items: center; + } +} From 5486fad3ddde77c1a346c6bf4cedc236645e9e48 Mon Sep 17 00:00:00 2001 From: ByteGrad Date: Mon, 25 Dec 2023 17:57:05 +0100 Subject: [PATCH 31/40] add rmtdev --- rmtdev/final-code/.eslintrc.cjs | 18 + rmtdev/final-code/.gitignore | 24 + rmtdev/final-code/README.md | 27 + rmtdev/final-code/index.html | 27 + rmtdev/final-code/package-lock.json | 3018 +++++++++++++++++ rmtdev/final-code/package.json | 31 + rmtdev/final-code/public/vite.svg | 1 + .../src/components/App-Before-Context.tsx | 102 + rmtdev/final-code/src/components/App.tsx | 52 + .../final-code/src/components/Background.tsx | 10 + .../src/components/BookmarkIcon.tsx | 28 + .../src/components/BookmarksButton.tsx | 66 + .../src/components/BookmarksPopover.tsx | 18 + .../final-code/src/components/Container.tsx | 7 + rmtdev/final-code/src/components/Footer.tsx | 19 + rmtdev/final-code/src/components/Header.tsx | 10 + .../src/components/JobItemContent.tsx | 138 + .../JobList/JobList-Before-Context.tsx | 37 + .../src/components/JobList/JobList.tsx | 30 + .../components/JobList/JobListBookmarks.tsx | 8 + .../src/components/JobList/JobListItem.tsx | 63 + .../src/components/JobList/JobListSearch.tsx | 8 + rmtdev/final-code/src/components/Logo.tsx | 11 + .../components/Pagination-Before-Context.tsx | 68 + .../src/components/PaginationControls.tsx | 64 + .../ResultsCount-Before-Context.tsx | 7 + .../src/components/ResultsCount.tsx | 11 + .../src/components/Search-Before-Context.tsx | 31 + .../final-code/src/components/SearchForm.tsx | 28 + rmtdev/final-code/src/components/Sidebar.tsx | 18 + .../src/components/Sorting-Before-Context.tsx | 39 + .../src/components/SortingControls.tsx | 35 + rmtdev/final-code/src/components/Spinner.tsx | 3 + .../src/contexts/ActiveIdContextProvider.tsx | 41 + .../src/contexts/BookmarksContextProvider.tsx | 78 + .../src/contexts/JobItemsContextProvider.tsx | 117 + .../contexts/SearchTextContextProvider.tsx | 63 + rmtdev/final-code/src/index.css | 1196 +++++++ rmtdev/final-code/src/lib/constants.ts | 4 + rmtdev/final-code/src/lib/hooks.ts | 167 + rmtdev/final-code/src/lib/types.ts | 29 + rmtdev/final-code/src/lib/utils.ts | 18 + rmtdev/final-code/src/main.tsx | 27 + rmtdev/final-code/src/vite-env.d.ts | 1 + rmtdev/final-code/tsconfig.json | 25 + rmtdev/final-code/tsconfig.node.json | 10 + rmtdev/final-code/vite.config.ts | 7 + rmtdev/resources/base-url.txt | 1 + rmtdev/resources/starter.txt | 1 + 49 files changed, 5842 insertions(+) create mode 100644 rmtdev/final-code/.eslintrc.cjs create mode 100644 rmtdev/final-code/.gitignore create mode 100644 rmtdev/final-code/README.md create mode 100644 rmtdev/final-code/index.html create mode 100644 rmtdev/final-code/package-lock.json create mode 100644 rmtdev/final-code/package.json create mode 100644 rmtdev/final-code/public/vite.svg create mode 100644 rmtdev/final-code/src/components/App-Before-Context.tsx create mode 100644 rmtdev/final-code/src/components/App.tsx create mode 100644 rmtdev/final-code/src/components/Background.tsx create mode 100644 rmtdev/final-code/src/components/BookmarkIcon.tsx create mode 100644 rmtdev/final-code/src/components/BookmarksButton.tsx create mode 100644 rmtdev/final-code/src/components/BookmarksPopover.tsx create mode 100644 rmtdev/final-code/src/components/Container.tsx create mode 100644 rmtdev/final-code/src/components/Footer.tsx create mode 100644 rmtdev/final-code/src/components/Header.tsx create mode 100644 rmtdev/final-code/src/components/JobItemContent.tsx create mode 100644 rmtdev/final-code/src/components/JobList/JobList-Before-Context.tsx create mode 100644 rmtdev/final-code/src/components/JobList/JobList.tsx create mode 100644 rmtdev/final-code/src/components/JobList/JobListBookmarks.tsx create mode 100644 rmtdev/final-code/src/components/JobList/JobListItem.tsx create mode 100644 rmtdev/final-code/src/components/JobList/JobListSearch.tsx create mode 100644 rmtdev/final-code/src/components/Logo.tsx create mode 100644 rmtdev/final-code/src/components/Pagination-Before-Context.tsx create mode 100644 rmtdev/final-code/src/components/PaginationControls.tsx create mode 100644 rmtdev/final-code/src/components/ResultsCount-Before-Context.tsx create mode 100644 rmtdev/final-code/src/components/ResultsCount.tsx create mode 100644 rmtdev/final-code/src/components/Search-Before-Context.tsx create mode 100644 rmtdev/final-code/src/components/SearchForm.tsx create mode 100644 rmtdev/final-code/src/components/Sidebar.tsx create mode 100644 rmtdev/final-code/src/components/Sorting-Before-Context.tsx create mode 100644 rmtdev/final-code/src/components/SortingControls.tsx create mode 100644 rmtdev/final-code/src/components/Spinner.tsx create mode 100644 rmtdev/final-code/src/contexts/ActiveIdContextProvider.tsx create mode 100644 rmtdev/final-code/src/contexts/BookmarksContextProvider.tsx create mode 100644 rmtdev/final-code/src/contexts/JobItemsContextProvider.tsx create mode 100644 rmtdev/final-code/src/contexts/SearchTextContextProvider.tsx create mode 100644 rmtdev/final-code/src/index.css create mode 100644 rmtdev/final-code/src/lib/constants.ts create mode 100644 rmtdev/final-code/src/lib/hooks.ts create mode 100644 rmtdev/final-code/src/lib/types.ts create mode 100644 rmtdev/final-code/src/lib/utils.ts create mode 100644 rmtdev/final-code/src/main.tsx create mode 100644 rmtdev/final-code/src/vite-env.d.ts create mode 100644 rmtdev/final-code/tsconfig.json create mode 100644 rmtdev/final-code/tsconfig.node.json create mode 100644 rmtdev/final-code/vite.config.ts create mode 100644 rmtdev/resources/base-url.txt create mode 100644 rmtdev/resources/starter.txt diff --git a/rmtdev/final-code/.eslintrc.cjs b/rmtdev/final-code/.eslintrc.cjs new file mode 100644 index 0000000..d6c9537 --- /dev/null +++ b/rmtdev/final-code/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/rmtdev/final-code/.gitignore b/rmtdev/final-code/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/rmtdev/final-code/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/rmtdev/final-code/README.md b/rmtdev/final-code/README.md new file mode 100644 index 0000000..1ebe379 --- /dev/null +++ b/rmtdev/final-code/README.md @@ -0,0 +1,27 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname, + }, +``` + +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/rmtdev/final-code/index.html b/rmtdev/final-code/index.html new file mode 100644 index 0000000..6d68a09 --- /dev/null +++ b/rmtdev/final-code/index.html @@ -0,0 +1,27 @@ + + + + + + + + + + + rmtDev -- Find your remote developer job + + +
    + + + diff --git a/rmtdev/final-code/package-lock.json b/rmtdev/final-code/package-lock.json new file mode 100644 index 0000000..e17cc4d --- /dev/null +++ b/rmtdev/final-code/package-lock.json @@ -0,0 +1,3018 @@ +{ + "name": "rmtdev", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "rmtdev", + "version": "0.0.0", + "dependencies": { + "@radix-ui/react-icons": "^1.3.0", + "@tanstack/react-query": "^4.36.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "typescript": "^5.0.2", + "vite": "^4.4.5" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", + "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", + "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", + "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", + "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", + "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", + "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x" + } + }, + "node_modules/@tanstack/query-core": { + "version": "4.36.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz", + "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "4.36.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz", + "integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==", + "dependencies": { + "@tanstack/query-core": "4.36.1", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", + "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", + "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", + "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", + "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.8", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.8.tgz", + "integrity": "sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.25", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.25.tgz", + "integrity": "sha512-24xqse6+VByVLIr+xWaQ9muX1B4bXJKXBbjszbld/UEDslGLY53+ZucF44HCmLbMPejTzGG9XgR+3m2/Wqu1kw==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.11", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.11.tgz", + "integrity": "sha512-zq6Dy0EiCuF9pWFW6I6k6W2LdpUixLE4P6XjXU1QHLfak3GPACQfLwEuHzY5pOYa4hzj1d0GxX/P141aFjZsyg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.4.tgz", + "integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", + "integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.7.4", + "@typescript-eslint/type-utils": "6.7.4", + "@typescript-eslint/utils": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", + "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.7.4", + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/typescript-estree": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", + "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", + "integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.7.4", + "@typescript-eslint/utils": "6.7.4", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", + "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", + "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", + "integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.7.4", + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/typescript-estree": "6.7.4", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", + "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.4", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.1.0.tgz", + "integrity": "sha512-rM0SqazU9iqPUraQ2JlIvReeaxOoRj6n+PzB1C0cBzIbd8qP336nC39/R9yPi3wVcah7E7j/kdU1uCUqMEU4OQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.22.20", + "@babel/plugin-transform-react-jsx-self": "^7.22.5", + "@babel/plugin-transform-react-jsx-source": "^7.22.5", + "@types/babel__core": "^7.20.2", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001546", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz", + "integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.544", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.544.tgz", + "integrity": "sha512-54z7squS1FyFRSUqq/knOFSptjjogLZXbKcYk3B0qkE1KZzvqASwRZnY2KzZQJqIYLVD38XZeoiMRflYSwyO4w==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.51.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.3.tgz", + "integrity": "sha512-Hh0wv8bUNY877+sI0BlCUlsS0TYYQqvzEwJsJJPM2WF4RnTStSnSR3zdJYa2nPOJgg3UghXi54lVyMSmpCalzA==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/goober": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz", + "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-hot-toast": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", + "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", + "dependencies": { + "goober": "^2.1.10" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/vite": { + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz", + "integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/rmtdev/final-code/package.json b/rmtdev/final-code/package.json new file mode 100644 index 0000000..ed4b4cd --- /dev/null +++ b/rmtdev/final-code/package.json @@ -0,0 +1,31 @@ +{ + "name": "rmtdev", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@radix-ui/react-icons": "^1.3.0", + "@tanstack/react-query": "^4.36.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "typescript": "^5.0.2", + "vite": "^4.4.5" + } +} diff --git a/rmtdev/final-code/public/vite.svg b/rmtdev/final-code/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/rmtdev/final-code/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rmtdev/final-code/src/components/App-Before-Context.tsx b/rmtdev/final-code/src/components/App-Before-Context.tsx new file mode 100644 index 0000000..a577806 --- /dev/null +++ b/rmtdev/final-code/src/components/App-Before-Context.tsx @@ -0,0 +1,102 @@ +import { useState, useMemo } from "react"; +import Background from "./Background"; +import Container from "./Container"; +import Footer from "./Footer"; +import Header, { HeaderTop } from "./Header"; +import { useDebounce, useJobItems } from "../lib/hooks"; +import Sidebar, { SidebarTop } from "./Sidebar"; +import JobList from "./JobList/JobList"; +import Search from "./SearchForm"; +import { Toaster } from "react-hot-toast"; +import ResultsCount from "./ResultsCount"; +import Sorting from "./SortingControls"; +import Pagination from "./PaginationControls"; +import JobItemContent from "./JobItemContent"; +import Logo from "./Logo"; +import Bookmarks from "./BookmarksButton"; +import { RESULTS_PER_PAGE } from "../lib/constants"; +import type { SortBy } from "../lib/types"; + +function App() { + const [searchText, setSearchText] = useState(""); + const debouncedSearchText = useDebounce(searchText, 250); + const [allJobItems, isLoading] = useJobItems(debouncedSearchText); + const [sortBy, setSortBy] = useState("relevant"); + const [currentPage, setCurrentPage] = useState(1); + + const totalNumberOfResults = allJobItems.length || 0; + const totalNumberOfPages = Math.ceil(totalNumberOfResults / RESULTS_PER_PAGE); + const sortedAllJobItems = useMemo(() => { + if (sortBy === "relevant") { + return [...allJobItems].sort((a, b) => { + return b.relevanceScore - a.relevanceScore; + }); + } else { + return [...allJobItems].sort((a, b) => { + return a.daysAgo - b.daysAgo; + }); + } + }, [allJobItems, sortBy]); + const jobItems = useMemo( + () => + sortedAllJobItems?.slice( + currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, + currentPage * RESULTS_PER_PAGE + ), + [sortedAllJobItems, currentPage] + ); + + const handleSearchChange = (e: React.ChangeEvent) => { + setSearchText(e.target.value); + }; + const handleSortChange = (newSortBy: SortBy) => { + setCurrentPage(1); + setSortBy(newSortBy); + }; + const handlePageChange = (newPage: number) => { + setCurrentPage(newPage); + }; + + return ( + <> + + +
    + + + + + + +
    + + + + + + + + + 0} + /> + + + + + + + +
    + + + + ); +} + +export default App; diff --git a/rmtdev/final-code/src/components/App.tsx b/rmtdev/final-code/src/components/App.tsx new file mode 100644 index 0000000..4204d26 --- /dev/null +++ b/rmtdev/final-code/src/components/App.tsx @@ -0,0 +1,52 @@ +import Background from "./Background"; +import Container from "./Container"; +import Footer from "./Footer"; +import Header, { HeaderTop } from "./Header"; +import Sidebar, { SidebarTop } from "./Sidebar"; +import { Toaster } from "react-hot-toast"; +import ResultsCount from "./ResultsCount"; +import Sorting from "./SortingControls"; +import Pagination from "./PaginationControls"; +import JobItemContent from "./JobItemContent"; +import Logo from "./Logo"; +import Bookmarks from "./BookmarksButton"; +import JobListSearch from "./JobList/JobListSearch"; +import SearchForm from "./SearchForm"; + +function App() { + return ( + <> + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + +
    + + + + ); +} + +export default App; diff --git a/rmtdev/final-code/src/components/Background.tsx b/rmtdev/final-code/src/components/Background.tsx new file mode 100644 index 0000000..4f6df15 --- /dev/null +++ b/rmtdev/final-code/src/components/Background.tsx @@ -0,0 +1,10 @@ +export default function Background() { + return ( +
    + Background pattern +
    + ); +} diff --git a/rmtdev/final-code/src/components/BookmarkIcon.tsx b/rmtdev/final-code/src/components/BookmarkIcon.tsx new file mode 100644 index 0000000..8515db0 --- /dev/null +++ b/rmtdev/final-code/src/components/BookmarkIcon.tsx @@ -0,0 +1,28 @@ +import { BookmarkFilledIcon } from "@radix-ui/react-icons"; +import { JobItemId } from "../lib/types"; +import { useBookmarksContext } from "../contexts/BookmarksContextProvider"; + +type BookmarkIconProps = { + jobItemId: JobItemId; +}; + +export default function BookmarkIcon({ jobItemId }: BookmarkIconProps) { + const { bookmarkedJobItemIds, handleToggleBookmark } = useBookmarksContext(); + + return ( + + ); +} diff --git a/rmtdev/final-code/src/components/BookmarksButton.tsx b/rmtdev/final-code/src/components/BookmarksButton.tsx new file mode 100644 index 0000000..f5076bb --- /dev/null +++ b/rmtdev/final-code/src/components/BookmarksButton.tsx @@ -0,0 +1,66 @@ +import { useState, useEffect, useRef } from "react"; +import { TriangleDownIcon } from "@radix-ui/react-icons"; +import BookmarksPopover from "./BookmarksPopover"; +import { useOnClickOutside } from "../lib/hooks"; + +export default function Bookmarks() { + // also called local state + const [isOpen, setIsOpen] = useState(false); + const buttonRef = useRef(null); + const popoverRef = useRef(null); + + // hooks for common use cases are available (show google results) + + // detect click outside of the bookmarks list with own solution, without ref + // useEffect(() => { + // function handleClick(event: MouseEvent) { + // const target = event.target as HTMLElement; + // if ( + // !target.closest(".bookmarks-popover") && + // !target.closest(".bookmarks-btn") + // ) { + // setIsOpen(false); + // } + // } + + // document.addEventListener("click", handleClick); + + // return () => document.removeEventListener("click", handleClick); + // }, []); + + // detect click outside of the bookmarks list with own solution, with ref + // useEffect(() => { + // function handleClick(event: MouseEvent) { + // const target = event.target as HTMLElement; + // if ( + // // use ref to check if the click is inside the popover + // !popoverRef.current?.contains(target) && + // // use ref to check if the click is inside the button + // !buttonRef.current?.contains(target) + // ) { + // setIsOpen(false); + // } + // } + + // document.addEventListener("click", handleClick); + + // return () => document.removeEventListener("click", handleClick); + // }, []); + + // use hook + useOnClickOutside([buttonRef, popoverRef], () => setIsOpen(false)); + + return ( +
    + + + {isOpen && } +
    + ); +} diff --git a/rmtdev/final-code/src/components/BookmarksPopover.tsx b/rmtdev/final-code/src/components/BookmarksPopover.tsx new file mode 100644 index 0000000..837933a --- /dev/null +++ b/rmtdev/final-code/src/components/BookmarksPopover.tsx @@ -0,0 +1,18 @@ +// use portal so that the bookmarks list is not affected by the z-index of the header +import { createPortal } from "react-dom"; +import JobListBookmarks from "./JobList/JobListBookmarks"; +import { forwardRef } from "react"; + +const BookmarksPopover = forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(function (_, ref) { + return createPortal( +
    + +
    , + document.body + ); +}); + +export default BookmarksPopover; diff --git a/rmtdev/final-code/src/components/Container.tsx b/rmtdev/final-code/src/components/Container.tsx new file mode 100644 index 0000000..884e10c --- /dev/null +++ b/rmtdev/final-code/src/components/Container.tsx @@ -0,0 +1,7 @@ +type ContainerProps = { + children: React.ReactNode; +}; + +export default function Container({ children }: ContainerProps) { + return
    {children}
    ; +} diff --git a/rmtdev/final-code/src/components/Footer.tsx b/rmtdev/final-code/src/components/Footer.tsx new file mode 100644 index 0000000..4e1d443 --- /dev/null +++ b/rmtdev/final-code/src/components/Footer.tsx @@ -0,0 +1,19 @@ +export default function Footer() { + return ( +
    + +

    + © Copyright by{" "} + + ByteGrad.com + + . Intended for learning or your portfolio. +

    +
    + +

    + 109573 total jobs available +

    +
    + ); +} diff --git a/rmtdev/final-code/src/components/Header.tsx b/rmtdev/final-code/src/components/Header.tsx new file mode 100644 index 0000000..3fb0c51 --- /dev/null +++ b/rmtdev/final-code/src/components/Header.tsx @@ -0,0 +1,10 @@ +import Bookmarks from "./BookmarksButton"; +import Logo from "./Logo"; + +export default function Header({ children }) { + return
    {children}
    ; +} + +export function HeaderTop({ children }) { + return
    {children}
    ; +} diff --git a/rmtdev/final-code/src/components/JobItemContent.tsx b/rmtdev/final-code/src/components/JobItemContent.tsx new file mode 100644 index 0000000..35665c2 --- /dev/null +++ b/rmtdev/final-code/src/components/JobItemContent.tsx @@ -0,0 +1,138 @@ +import { useActiveIdContext } from "../contexts/ActiveIdContextProvider"; +import { useJobItem } from "../lib/hooks"; +import { urlContainsHashId } from "../lib/utils"; +import BookmarkIcon from "./BookmarkIcon"; +import Spinner from "./Spinner"; + +export default function JobItemContent() { + const { activeJobItemId } = useActiveIdContext(); + const [jobItem, isLoading] = useJobItem(activeJobItemId); + + if (isLoading && urlContainsHashId()) { + return ; + } + + if ((!isLoading && !jobItem) || (!isLoading && !urlContainsHashId())) { + return ; + } + + // to ensure to typescript that jobItem is not null below this + if (!jobItem) { + return ; + } + + return ( +
    +
    + # + + + Apply + + +
    +
    +
    {jobItem.badgeLetters}
    +
    + + + +
    +
    + +
    +

    {jobItem.title}

    +

    {jobItem.company}

    +

    {jobItem.description}

    +
    +

    + + {jobItem.duration} +

    +

    + + {jobItem.salary} +

    +

    + {" "} + {jobItem.location} +

    +
    +
    +
    + +
    +
    +
    +

    Qualifications

    +

    + Other qualifications may apply +

    +
    +
      + {/* {jobItem.qualifications.map((qualificationText, index) => ( + // careful with key=index, it's not recommended +
    • + {qualificationText} +
    • + ))} */} +
    • Test
    • +
    • Test
    • +
    • Test
    • +
    +
    + +
    +
    +

    Company reviews

    +

    + Recent things people are saying +

    +
    +
      + {jobItem.reviews.map((reviewText, index) => ( + // careful with key=index, it's not recommended +
    • + {reviewText} +
    • + ))} +
    +
    +
    + +
    +

    + If possible, please reference that you found the job on{" "} + rmtDev, we would really appreciate + it! +

    +
    +
    +
    + ); +} + +function LoadingJobContent() { + return ( +
    +
    + +
    +
    + ); +} + +function EmptyJobContent() { + return ( +
    +
    +
    +

    What are you looking for?

    +

    + Start by searching for any technology your ideal job is working with +

    +
    +
    +
    + ); +} diff --git a/rmtdev/final-code/src/components/JobList/JobList-Before-Context.tsx b/rmtdev/final-code/src/components/JobList/JobList-Before-Context.tsx new file mode 100644 index 0000000..d4bbb3c --- /dev/null +++ b/rmtdev/final-code/src/components/JobList/JobList-Before-Context.tsx @@ -0,0 +1,37 @@ +import { memo } from "react"; +import { useActiveIdContext, useBookmarksContext } from "../../lib/hooks"; +import { JobItem } from "../../lib/types"; +import JobListItem from "./JobListItem"; +import Spinner from "../Spinner"; + +type JobListProps = { + jobItems: JobItem[]; + isLoading: boolean; +}; + +// show that before memo it renders every key stroke +// pick a bigger debounce to show it properly +const JobList = memo(({ jobItems, isLoading }: JobListProps) => { + console.log("JobList rendering..."); + const { bookmarkedJobItems } = useBookmarksContext(); + const { activeJobItemId } = useActiveIdContext(); + + return ( +
      + {isLoading && } + + {jobItems.map((jobItem) => ( + bookmarkedJobItem.id === jobItem.id + )} + /> + ))} +
    + ); +}); + +export default JobList; diff --git a/rmtdev/final-code/src/components/JobList/JobList.tsx b/rmtdev/final-code/src/components/JobList/JobList.tsx new file mode 100644 index 0000000..d02dee3 --- /dev/null +++ b/rmtdev/final-code/src/components/JobList/JobList.tsx @@ -0,0 +1,30 @@ +import { useActiveIdContext } from "../../contexts/ActiveIdContextProvider"; +import { JobItem } from "../../lib/types"; +import JobListItem from "./JobListItem"; +import Spinner from "../Spinner"; + +type JobListProps = { + jobItems: JobItem[]; + isLoading?: boolean; +}; + +const JobList = ({ jobItems, isLoading = false }: JobListProps) => { + const { activeJobItemId } = useActiveIdContext(); + + return ( +
      + {isLoading && } + + {!isLoading && + jobItems.map((jobItem) => ( + + ))} +
    + ); +}; + +export default JobList; diff --git a/rmtdev/final-code/src/components/JobList/JobListBookmarks.tsx b/rmtdev/final-code/src/components/JobList/JobListBookmarks.tsx new file mode 100644 index 0000000..893a6e0 --- /dev/null +++ b/rmtdev/final-code/src/components/JobList/JobListBookmarks.tsx @@ -0,0 +1,8 @@ +import { useBookmarksContext } from "../../contexts/BookmarksContextProvider"; +import JobList from "./JobList"; + +export default function JobListBookmarks() { + const { bookmarkedJobItems, isLoading } = useBookmarksContext(); + + return ; +} diff --git a/rmtdev/final-code/src/components/JobList/JobListItem.tsx b/rmtdev/final-code/src/components/JobList/JobListItem.tsx new file mode 100644 index 0000000..89d4754 --- /dev/null +++ b/rmtdev/final-code/src/components/JobList/JobListItem.tsx @@ -0,0 +1,63 @@ +import { JobItem } from "../../lib/types"; +import BookmarkIcon from "../BookmarkIcon"; + +// type JobListItemProps = { +// jobItem: JobItem; +// }; + +// type JobListItemProps = { +// id: number; +// badgeLetters: string; +// title: string; +// company: string; +// duration: string; +// salary: string; +// location: string; +// daysAgo: number; +// }; + +type JobListItemProps = JobItem & { + isActive?: boolean; +}; + +// export default function JobListItem({ jobItem }: JobListItemProps) { +export default function JobListItem({ + id, + badgeLetters, + title, + company, + daysAgo, + isActive, +}: JobListItemProps) { + return ( +
  • + +
    {badgeLetters}
    + +
    +

    {title}

    +

    {company}

    + {/*
    +

    + + {duration} +

    +

    + + {salary} +

    +

    + {" "} + {location} +

    +
    */} +
    + +
    + + +
    +
    +
  • + ); +} diff --git a/rmtdev/final-code/src/components/JobList/JobListSearch.tsx b/rmtdev/final-code/src/components/JobList/JobListSearch.tsx new file mode 100644 index 0000000..f03aa80 --- /dev/null +++ b/rmtdev/final-code/src/components/JobList/JobListSearch.tsx @@ -0,0 +1,8 @@ +import { useJobItemsContext } from "../../contexts/JobItemsContextProvider"; +import JobList from "./JobList"; + +export default function JobListSearch() { + const { jobItemsSortedAndSliced, isLoading } = useJobItemsContext(); + + return ; +} diff --git a/rmtdev/final-code/src/components/Logo.tsx b/rmtdev/final-code/src/components/Logo.tsx new file mode 100644 index 0000000..94f76da --- /dev/null +++ b/rmtdev/final-code/src/components/Logo.tsx @@ -0,0 +1,11 @@ +export default function Logo() { + return ( + + Logo + + ); +} diff --git a/rmtdev/final-code/src/components/Pagination-Before-Context.tsx b/rmtdev/final-code/src/components/Pagination-Before-Context.tsx new file mode 100644 index 0000000..e3c0fd0 --- /dev/null +++ b/rmtdev/final-code/src/components/Pagination-Before-Context.tsx @@ -0,0 +1,68 @@ +import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons"; + +type PaginationProps = { + currentPage: number; + totalNumberOfPages: number; + onPageChange: (newPage: number) => void; +}; + +export default function Pagination({ + currentPage, + totalNumberOfPages, + onPageChange, +}: PaginationProps) { + return ( +
    + {currentPage > 1 && ( + + )} + + {currentPage < totalNumberOfPages && ( + + )} +
    + ); +} + +type PaginationButtonProps = Omit & { + direction: "previous" | "next"; +}; + +function PaginationButton({ + direction, + currentPage, + onPageChange, +}: PaginationButtonProps) { + return ( + + ); +} diff --git a/rmtdev/final-code/src/components/PaginationControls.tsx b/rmtdev/final-code/src/components/PaginationControls.tsx new file mode 100644 index 0000000..86cdda0 --- /dev/null +++ b/rmtdev/final-code/src/components/PaginationControls.tsx @@ -0,0 +1,64 @@ +import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons"; +import { useJobItemsContext } from "../contexts/JobItemsContextProvider"; + +export default function Pagination() { + const { currentPage, totalNumberOfPages, handleChangePage } = + useJobItemsContext(); + + return ( +
    + {currentPage > 1 && ( + + )} + + {currentPage < totalNumberOfPages && ( + + )} +
    + ); +} + +type PaginationButtonProps = { + direction: "previous" | "next"; + currentPage: number; + onChangePage: (newPage: number) => void; +}; + +function PaginationButton({ + direction, + currentPage, + onChangePage, +}: PaginationButtonProps) { + return ( + + ); +} diff --git a/rmtdev/final-code/src/components/ResultsCount-Before-Context.tsx b/rmtdev/final-code/src/components/ResultsCount-Before-Context.tsx new file mode 100644 index 0000000..b49aa33 --- /dev/null +++ b/rmtdev/final-code/src/components/ResultsCount-Before-Context.tsx @@ -0,0 +1,7 @@ +export default function ResultsCount({ count }: { count: number }) { + return ( +

    + {count} results +

    + ); +} diff --git a/rmtdev/final-code/src/components/ResultsCount.tsx b/rmtdev/final-code/src/components/ResultsCount.tsx new file mode 100644 index 0000000..0b4594c --- /dev/null +++ b/rmtdev/final-code/src/components/ResultsCount.tsx @@ -0,0 +1,11 @@ +import { useJobItemsContext } from "../contexts/JobItemsContextProvider"; + +export default function ResultsCount() { + const { totalNumberOfResults } = useJobItemsContext(); + + return ( +

    + {totalNumberOfResults} results +

    + ); +} diff --git a/rmtdev/final-code/src/components/Search-Before-Context.tsx b/rmtdev/final-code/src/components/Search-Before-Context.tsx new file mode 100644 index 0000000..d21ee68 --- /dev/null +++ b/rmtdev/final-code/src/components/Search-Before-Context.tsx @@ -0,0 +1,31 @@ +import { MagnifyingGlassIcon } from "@radix-ui/react-icons"; + +type SearchProps = { + searchText: string; + onSearchChange: (newText: string) => void; +}; + +export default function Search({ searchText, onSearchChange }: SearchProps) { + return ( + { + e.preventDefault(); + }} + > + + + onSearchChange(e.target.value)} + spellCheck="false" + type="text" + required + placeholder="Find remote developer jobs..." + /> + + ); +} diff --git a/rmtdev/final-code/src/components/SearchForm.tsx b/rmtdev/final-code/src/components/SearchForm.tsx new file mode 100644 index 0000000..ba5eff6 --- /dev/null +++ b/rmtdev/final-code/src/components/SearchForm.tsx @@ -0,0 +1,28 @@ +import { useSearchTextContext } from "../contexts/SearchTextContextProvider"; + +export default function Search() { + const { searchText, handleChangeSearch } = useSearchTextContext(); + + return ( +
    { + e.preventDefault(); + }} + > + + + handleChangeSearch(e.target.value)} + spellCheck="false" + type="text" + required + placeholder="Find remote developer jobs..." + /> +
    + ); +} diff --git a/rmtdev/final-code/src/components/Sidebar.tsx b/rmtdev/final-code/src/components/Sidebar.tsx new file mode 100644 index 0000000..7122d09 --- /dev/null +++ b/rmtdev/final-code/src/components/Sidebar.tsx @@ -0,0 +1,18 @@ +type SidebarProps = { + children: React.ReactNode; + // top?: React.ReactNode; + top?: React.ReactElement; +}; + +export default function Sidebar({ children, top }: SidebarProps) { + return ( +
    + {/* {top &&
    {top}
    } */} + {children} +
    + ); +} + +export function SidebarTop({ children }: { children: React.ReactNode }) { + return
    {children}
    ; +} diff --git a/rmtdev/final-code/src/components/Sorting-Before-Context.tsx b/rmtdev/final-code/src/components/Sorting-Before-Context.tsx new file mode 100644 index 0000000..78ec199 --- /dev/null +++ b/rmtdev/final-code/src/components/Sorting-Before-Context.tsx @@ -0,0 +1,39 @@ +import { SortBy } from "../lib/types"; + +export default function Sorting({ + currentSortBy, + onSortChange, +}: { + currentSortBy: SortBy; + onSortChange: (sortBy: SortBy) => void; +}) { + return ( +
    + + + + + +
    + ); +} diff --git a/rmtdev/final-code/src/components/SortingControls.tsx b/rmtdev/final-code/src/components/SortingControls.tsx new file mode 100644 index 0000000..8766bc7 --- /dev/null +++ b/rmtdev/final-code/src/components/SortingControls.tsx @@ -0,0 +1,35 @@ +import { useJobItemsContext } from "../contexts/JobItemsContextProvider"; + +export default function Sorting() { + const { sortBy, handleChangeSort } = useJobItemsContext(); + + return ( +
    + + + + + +
    + ); +} diff --git a/rmtdev/final-code/src/components/Spinner.tsx b/rmtdev/final-code/src/components/Spinner.tsx new file mode 100644 index 0000000..a3daa6b --- /dev/null +++ b/rmtdev/final-code/src/components/Spinner.tsx @@ -0,0 +1,3 @@ +export default function Spinner() { + return
    ; +} diff --git a/rmtdev/final-code/src/contexts/ActiveIdContextProvider.tsx b/rmtdev/final-code/src/contexts/ActiveIdContextProvider.tsx new file mode 100644 index 0000000..067f02f --- /dev/null +++ b/rmtdev/final-code/src/contexts/ActiveIdContextProvider.tsx @@ -0,0 +1,41 @@ +import React, { createContext, useContext, useMemo } from "react"; +import { JobItemId } from "../lib/types"; +import { useActiveId } from "../lib/hooks"; + +type ActiveIdProviderProps = { + children: React.ReactNode; +}; + +type ActiveIdContext = { + activeJobItemId: JobItemId | null; +}; + +export const ActiveIdContext = createContext(null); + +export default function ActiveIdContextProvider({ + children, +}: ActiveIdProviderProps) { + const activeJobItemId = useActiveId(); + + const contextValue = useMemo( + () => ({ + activeJobItemId, + }), + [activeJobItemId] + ); + + return ( + + {children} + + ); +} + +export function useActiveIdContext() { + const context = useContext(ActiveIdContext); + + if (!context) { + throw new Error("ActiveIdContext must be used within an ActiveIdProvider"); + } + return context; +} diff --git a/rmtdev/final-code/src/contexts/BookmarksContextProvider.tsx b/rmtdev/final-code/src/contexts/BookmarksContextProvider.tsx new file mode 100644 index 0000000..6ba98b5 --- /dev/null +++ b/rmtdev/final-code/src/contexts/BookmarksContextProvider.tsx @@ -0,0 +1,78 @@ +import React, { + createContext, + useState, + useEffect, + useMemo, + useContext, + useCallback, +} from "react"; +import { JobItem, JobItemId } from "../lib/types"; +import { useJobItems } from "../lib/hooks"; + +type BookmarksProviderProps = { + children: React.ReactNode; +}; + +type BookmarksContext = { + bookmarkedJobItems: JobItem[]; + bookmarkedJobItemIds: JobItemId[]; + isLoading: boolean; + handleToggleBookmark: (jobItem: JobItemId) => void; +}; + +export const BookmarksContext = createContext(null); + +export default function BookmarksContextProvider({ + children, +}: BookmarksProviderProps) { + // state + const [bookmarkedJobItemIds, setBookmarkedJobItemIds] = useState( + () => JSON.parse(localStorage.getItem("bookmarkedJobItemIds") || "[]") + ); + const { jobItems: bookmarkedJobItems, isLoading } = + useJobItems(bookmarkedJobItemIds); + + // event handlers / actions + const handleToggleBookmark = useCallback((jobItemId: JobItemId) => { + setBookmarkedJobItemIds((prevBookmarkedJobItemIds) => { + if (prevBookmarkedJobItemIds.includes(jobItemId)) { + return prevBookmarkedJobItemIds.filter((id) => id !== jobItemId); + } else { + return [...prevBookmarkedJobItemIds, jobItemId]; + } + }); + }, []); + + // side effects + useEffect(() => { + localStorage.setItem( + "bookmarkedJobItemIds", + JSON.stringify(bookmarkedJobItemIds) + ); + }, [bookmarkedJobItemIds]); + + // context value + const contextValue = useMemo( + () => ({ + bookmarkedJobItems, + bookmarkedJobItemIds, + isLoading, + handleToggleBookmark, + }), + [bookmarkedJobItems, bookmarkedJobItemIds, isLoading, handleToggleBookmark] + ); + + return ( + + {children} + + ); +} + +export function useBookmarksContext() { + const context = useContext(BookmarksContext); + if (!context) { + throw new Error("BookmarksContext must be used within a BookmarksProvider"); + } + return context; +} diff --git a/rmtdev/final-code/src/contexts/JobItemsContextProvider.tsx b/rmtdev/final-code/src/contexts/JobItemsContextProvider.tsx new file mode 100644 index 0000000..e02f4cc --- /dev/null +++ b/rmtdev/final-code/src/contexts/JobItemsContextProvider.tsx @@ -0,0 +1,117 @@ +import React, { + useState, + createContext, + useMemo, + useCallback, + useContext, +} from "react"; +import { useJobItems, useSearchQuery } from "../lib/hooks"; +import { JobItem, SortBy } from "../lib/types"; +import { RESULTS_PER_PAGE } from "../lib/constants"; +import { useSearchTextContext } from "./SearchTextContextProvider"; + +type JobItemsProviderProps = { + children: React.ReactNode; +}; + +type JobItemsContext = { + jobItems: JobItem[]; + jobItemsSortedAndSliced: JobItem[]; + isLoading: boolean; + totalNumberOfResults: number; + totalNumberOfPages: number; + currentPage: number; + handleChangePage: (newPage: number) => void; + sortBy: SortBy; + handleChangeSort: (newSortBy: SortBy) => void; +}; + +export const JobItemsContext = createContext(null); + +export default function JobItemsContextProvider({ + children, +}: JobItemsProviderProps) { + // dependency on other context + const { debouncedSearchText } = useSearchTextContext(); + + // state + const [jobItems, isLoading] = useSearchQuery(debouncedSearchText); + const [sortBy, setSortBy] = useState("relevant"); + const [currentPage, setCurrentPage] = useState(1); + + // derived / computed state + const totalNumberOfResults = jobItems.length || 0; + const totalNumberOfPages = Math.ceil(totalNumberOfResults / RESULTS_PER_PAGE); + const jobItemsSorted = useMemo(() => { + if (sortBy === "relevant") { + return [...jobItems].sort((a, b) => { + return b.relevanceScore - a.relevanceScore; + }); + } else { + return [...jobItems].sort((a, b) => { + return a.daysAgo - b.daysAgo; + }); + } + }, [jobItems, sortBy]); + const jobItemsSortedAndSliced = useMemo( + () => + jobItemsSorted?.slice( + currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, + currentPage * RESULTS_PER_PAGE + ), + [jobItemsSorted, currentPage] + ); + + // event handlers / actions + const handleChangeSort = useCallback((newSortBy: SortBy) => { + setCurrentPage(1); + setSortBy(newSortBy); + }, []); + const handleChangePage = useCallback((newPage: number) => { + setCurrentPage(newPage); + }, []); + + // context value + const contextValue = useMemo( + () => ({ + jobItems, + jobItemsSortedAndSliced, + isLoading: isLoading && debouncedSearchText.length > 0, + totalNumberOfResults, + totalNumberOfPages, + currentPage, + handleChangePage, + sortBy, + handleChangeSort, + }), + [ + jobItems, + jobItemsSortedAndSliced, + isLoading, + debouncedSearchText, + totalNumberOfResults, + totalNumberOfPages, + currentPage, + handleChangePage, + sortBy, + handleChangeSort, + ] + ); + + return ( + + {children} + + ); +} + +export function useJobItemsContext() { + const context = useContext(JobItemsContext); + + if (!context) { + throw new Error( + "JobItemsContext must be used within a JobItemsContextContextProvider" + ); + } + return context; +} diff --git a/rmtdev/final-code/src/contexts/SearchTextContextProvider.tsx b/rmtdev/final-code/src/contexts/SearchTextContextProvider.tsx new file mode 100644 index 0000000..c8808b8 --- /dev/null +++ b/rmtdev/final-code/src/contexts/SearchTextContextProvider.tsx @@ -0,0 +1,63 @@ +import React, { + useState, + createContext, + useMemo, + useCallback, + useContext, +} from "react"; +import { useDebounce } from "../lib/hooks"; + +type SearchTextProviderProps = { + children: React.ReactNode; +}; + +type SearchTextContext = { + searchText: string; + debouncedSearchText: string; + handleChangeSearch: (newSearchText: string) => void; +}; + +export const SearchTextContext = createContext(null); + +export default function SearchTextContextProvider({ + children, +}: SearchTextProviderProps) { + // state + const [searchText, setSearchText] = useState(""); + const debouncedSearchText = useDebounce(searchText, 250); + + // event handlers / actions + const handleChangeSearch = useCallback( + (newSearchText: string) => { + setSearchText(newSearchText); + }, + [setSearchText] + ); + + // context value + const contextValue = useMemo( + () => ({ + searchText, + debouncedSearchText, + handleChangeSearch, + }), + [searchText, debouncedSearchText, handleChangeSearch] + ); + + return ( + + {children} + + ); +} + +export function useSearchTextContext() { + const context = useContext(SearchTextContext); + + if (!context) { + throw new Error( + "SearchTextContext must be used within a SearchTextProvider" + ); + } + return context; +} diff --git a/rmtdev/final-code/src/index.css b/rmtdev/final-code/src/index.css new file mode 100644 index 0000000..2202175 --- /dev/null +++ b/rmtdev/final-code/src/index.css @@ -0,0 +1,1196 @@ +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap"); + +/* RESET */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +ul, +ol { + list-style: none; +} + +a { + color: inherit; + text-decoration: initial; +} + +button { + font: inherit; + border: initial; + outline: initial; /* create alternative for focus state */ + background-color: initial; +} + +input { + border: initial; + outline: initial; /* create alternative for focus state */ + font: inherit; +} + +/* UTILITIES */ +.u-bold { + font-weight: 700; +} + +/* KEYFRAMES */ +@keyframes spinner { + 0% { + transform: translateX(-50%) rotate(0deg); + } + + 100% { + transform: translateX(-50%) rotate(360deg); + } +} + +@keyframes intro { + 0% { + opacity: 0; + transform: translateY(-10px) scale(0.95); + } + + 100% { + opacity: 1; + transform: translateY(0px) scale(1); + } +} + +/* BASE */ +body { + background-color: #dee3e9; + color: rgb(22, 24, 28); + min-height: 100vh; + font-family: "Inter", sans-serif; + position: relative; + + scrollbar-width: none; /* Firefox */ +} + +body::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + display: none; +} + +/* HEADINGS */ +.first-heading { + font-size: 31px; + font-weight: 400; + color: #fff; + + font-size: 27px; + display: none; +} + +.second-heading { + font-size: 23px; + color: #fff; + font-weight: 500; +} + +.third-heading { + font-size: 15px; + font-weight: 500; + margin-top: 4px; +} + +.fourth-heading { + font-size: 16px; + font-weight: 600; + text-transform: capitalize; +} + +/* BACKGROUND */ +.background { + position: absolute; + height: 210px; + width: 100%; + top: 0; + left: 0; + z-index: -2; + overflow: hidden; + background-image: linear-gradient(125deg, #1f74f1 -10%, #0850b9 100%); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); +} + +.background::before { + content: ""; + background-image: linear-gradient( + -180deg, + rgba(0, 0, 0, 0.025) 0, + rgba(0, 0, 0, 0.075) 99% + ); + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + +.background > img { + position: absolute; + top: -25px; + left: 0; + z-index: -1; + user-select: none; + + transform: scale(1.1); +} + +/* HEADER */ +.header { + margin-bottom: 13px; + position: relative; + + margin-bottom: 0; +} + +.header__top { + display: flex; + align-items: center; + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + padding-top: 12px; + position: relative; + justify-content: center; + padding-top: 40px; + + animation: intro 0.3s; +} + +.header__submit-job { + margin-right: auto; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + text-transform: lowercase; + padding-left: 10px; + transform: translateY(-2px); + cursor: pointer; + + margin-right: 0; +} + +.header__submit-job::before { + content: ""; + display: inline-block; + height: 13px; + width: 2px; + margin-right: 8px; + background-color: rgba(255, 255, 255, 0.25); + transform: translateY(3px); +} + +/* LOGO */ +.logo { + margin-left: -8px; + user-select: none; +} + +.logo__img { + margin-bottom: -5px; +} + +/* BOOKMARKS BTN */ +.bookmarks-btn { + text-transform: lowercase; + font-size: 13px; + color: rgba(255, 255, 255, 0.75); + margin-left: 13px; + padding-left: 13px; + position: relative; + cursor: pointer; + transition: all 0.2s; + height: 32px; + display: flex; + align-items: center; +} + +.bookmarks-btn::before { + content: ""; + height: 15px; + width: 2px; + display: block; + position: absolute; + background-color: rgba(255, 255, 255, 0.3); + left: 0px; + top: 50%; + transform: translateY(-50%); +} + +.bookmarks-btn--active, +.bookmarks-btn:hover, +.bookmarks-btn:focus { + color: rgba(255, 255, 255, 1); +} + +.bookmarks-btn--active .bookmarks-btn__icon, +.bookmarks-btn:hover .bookmarks-btn__icon, +.bookmarks-btn:focus .bookmarks-btn__icon { + color: rgba(255, 255, 255, 0.8); +} + +.bookmarks-btn__icon { + font-size: 10px; + margin-left: 2px; + transform: translateY(-1px); + color: rgba(255, 255, 255, 0.6); + transition: all 0.2s; +} + +/* JOB LIST */ +.job-list { + background-color: #fff; + position: relative; + min-height: 140px; + /* height: 100%; */ + flex: 1; + display: flex; + flex-direction: column; + + scrollbar-color: #cacdd0 #ffffff; /* Firefox */ + scrollbar-width: thin; /* Firefox */ +} + +.job-list::-webkit-scrollbar { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + width: 4px; +} + +.job-list::-webkit-scrollbar-track { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #ffffff; +} + +.job-list::-webkit-scrollbar-thumb { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #cacdd0; + transition: all 0.2s; +} + +.job-list::-webkit-scrollbar-thumb:hover { + /* Chrome, Safari, Edge, Opera, All browsers on iOS */ + background-color: #b1b4b8; +} + +.job-list--search { + flex: 1; +} + +.bookmarks-popover { + /* visibility: hidden; */ + /* opacity: 0; */ + background-color: #fff; + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + transform: translateX(-50%); + transform-origin: left; + transition: all 0.2s; + /* pointer-events: none; */ + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks { + /* visibility: hidden; */ + /* opacity: 0; */ + min-height: 76px; + min-width: 340px; + width: 340px; + border-radius: 4px; + overflow: hidden; + z-index: 10; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + transform: translateX(-50%); + transform-origin: left; + transition: all 0.2s; + /* pointer-events: none; */ + + position: fixed; + left: 50%; + top: 82px; +} + +.job-list--bookmarks > p { + font-size: 14px; + position: absolute; + top: 46%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-list--bookmarks:hover { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +.job-list--visible { + pointer-events: initial; + visibility: initial; + transform: scale(1) translateX(-50%); + opacity: 1; +} + +/* JOB ITEM */ +.job-item { + cursor: pointer; + transition: background-color 0.2s; + background-color: #fff; + /* flex: 1; */ + height: 76px; +} + +.job-item:not(:nth-child(7)) { + border-bottom: 1px solid #ebeff1; +} + +.job-item:hover { + background-color: #f4f5f7; +} + +.job-item--active { + background-color: #f4f5f7; +} + +.job-item__link { + height: 100%; + width: 100%; + padding: 14px 20px; + display: flex; +} + +.job-item__badge { + font-size: 13px; + height: 46px; + width: 38px; + /* background-color: #8dd335; */ + background-color: #dee3e9; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 600; + margin-right: 13px; +} + +/* .job-item:nth-child(4n + 2) .job-item__badge { + background-color: #3d87f1; +} + +.job-item:nth-child(4n + 3) .job-item__badge { + background-color: #d2d631; +} + +.job-item:nth-child(4n + 4) .job-item__badge { + background-color: #d96a46; +} */ + +.job-item__middle { +} + +.job-item__company { + font-size: 13px; + /* margin-bottom: 2px; */ + font-style: italic; +} + +.job-item__extras { + display: grid; + grid-template-columns: 65px 72px 65px; + column-gap: 10px; +} + +.job-item__extra { + color: #4d5054; + font-size: 11px; + display: flex; + align-items: center; + gap: 3px; +} + +.job-item__extra-icon { + color: #bec5ce; + font-size: 10px; + margin-right: 1px; +} + +.job-item__right { + margin-left: auto; + display: flex; + flex-direction: column; + align-items: center; + transform: translateX(2px) translateY(0px); +} + +.job-item__right svg { + font-size: 14px; + + cursor: pointer; + color: #d7dbe0; + transition: all 0.2s; +} + +.job-item__right svg.filled { + color: #2671dd; +} + +.job-item__bookmark-icon--bookmarked { + color: #2671dd; +} + +.job-item__time { + font-size: 11px; + margin-top: 5px; + color: #515459; +} + +/* MAIN */ +.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +/* INTRO */ +.intro { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 33px; + + margin-top: 20px; + row-gap: 20px; +} + +/* SEARCH */ +.search { + position: relative; + animation: intro 0.3s 0.1s backwards; + margin: 0 auto; + width: 610px; + margin-top: 20px; +} + +.search > button { + cursor: pointer; + position: absolute; + top: 17px; + left: 25px; +} + +.search > button > i { + transition: all 0.2s; + color: rgba(0, 0, 0, 0.73); +} + +.search__icon:hover, +.search__icon:focus { + color: rgba(0, 0, 0, 0.93); +} + +.search input { + height: 56px; + width: 100%; + border-radius: 4px; + padding-left: 55px; + padding-right: 15px; + padding-bottom: 2px; + color: rgba(0, 0, 0, 0.9); + caret-color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0.9); + transition: all 0.2s, box-shadow 0.1s; +} + +.search input::selection { + background-color: rgba(0, 0, 0, 0.25); +} + +.search input:hover, +.search input:focus { + background-color: rgba(255, 255, 255, 1); +} + +.search input:focus { + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4); +} + +.search input::placeholder { + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + font-size: 15px; +} + +.search__input--invalid { + box-shadow: 0 0 0 4px rgba(47, 19, 19, 0.729); +} + +/* CONTAINER */ +.container { + margin: 0 12px; + margin-top: 27px; + margin-left: auto; + margin-right: auto; + height: 616px; + width: 976px; + background-color: #fff; + border-radius: 8px; + display: flex; + border-top-right-radius: 9px; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.07); + margin-top: 40px; + animation: intro 0.3s 0.2s backwards; +} + +/* SIDEBAR */ +.sidebar { + width: 340px; + display: flex; + flex-direction: column; +} + +/* SEARCH RESULTS */ +.search-results { + width: 340px; + display: flex; + flex-direction: column; + position: relative; + cursor: default; +} + +.sidebar__top { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + border-bottom: 1px solid #e8edf0; +} + +/* COUNT */ +.count { + font-size: 12px; +} + +/* SORTING */ +.sorting { + display: flex; + align-items: center; + gap: 4px; +} + +.sorting > i { + font-size: 11px; + color: #4c4f50; + margin-right: 5px; +} + +.sorting__button { + font-size: 10px; + text-transform: uppercase; + background-color: #e8edf0; + padding: 6px 8px; + border-radius: 3px; + margin-left: 2px; + cursor: pointer; + transition: all 0.2s; +} + +.sorting__button:hover, +.sorting__button:focus { + background-color: #d0d5d8; +} + +.sorting__button--active, +.sorting__button--active:hover, +.sorting__button--active:focus { + color: #fff; + background-color: #3c4041; +} + +/* PAGINATION */ +.pagination { + height: 40px; + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px 1px; + border-top: 1px solid #e8edf0; +} + +.pagination__button { + font-size: 10px; + padding: 4px 10px; + border-radius: 500px; + color: #747c82; + background-color: #eceff2; + cursor: pointer; + transition: all 0.2s, visibility 0s; + gap: 2px; + display: flex; + align-items: center; +} + +.pagination__button svg { + width: 10px; + transform: translateY(1px); +} + +.pagination__button:hover, +.pagination__button:focus { + background-color: #dde2e6; +} + +.pagination__button--hidden { + visibility: hidden; +} + +.pagination__button--next { + margin-left: auto; +} + +.pagination__button--back .pagination__icon { + margin-right: 3px; +} + +.pagination__button--next .pagination__icon { + margin-left: 3px; +} + +.pagination__button > span { + font-weight: 500; +} +.pagination__icon { + font-size: 8px; + color: #9fa6b0; +} + +/* JOB DETAILS */ +.job-details { + flex: 1; + background-color: #eff2f5; + position: relative; + border-top-right-radius: 12px; + border-bottom-right-radius: 8px; +} + +.job-details__start-view { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.job-details__start-view > p:first-child { + opacity: 0.55; + width: 275px; + text-align: center; + color: #0d1114; + font-size: 18px; + font-weight: 600; + margin-bottom: 10px; +} + +.job-details__start-view > p:nth-child(2) { + opacity: 0.55; + color: #24292d; + font-size: 14px; + width: 275px; + text-align: center; +} + +.job-details > div { + height: 100%; +} + +.job-details > div > img { + width: 100%; + height: 174px; + position: absolute; + z-index: 0; + top: 0; + object-fit: cover; + border-top-right-radius: 8px; + user-select: none; +} + +.job-details__other { +} + +.job-details__footer { + margin-left: 42px; + margin-right: 42px; + margin-top: 33px; + padding-top: 13px; + border-top: 1px solid #dce2e8; +} + +.job-details__footer-text { + color: #858b8f; + font-size: 10px; +} + +/* APPLY BTN */ +.apply-btn { + position: absolute; + background-color: #2671dd; + z-index: 2; + color: rgba(255, 255, 255, 0.92); + font-size: 11px; + padding: 5px 10px 6px; + border-radius: 3px; + top: 12px; + right: 12px; + cursor: pointer; + text-transform: uppercase; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.apply-btn:hover, +.apply-btn:focus { + background-color: #1d60bd; + color: rgba(255, 255, 255, 1); +} + +.apply-btn__icon { + color: rgba(255, 255, 255, 0.65); + font-size: 8px; + margin-left: 4px; + margin-top: -1px; +} + +/* JOB INFO */ +.job-info { + position: relative; + z-index: 1; + margin-bottom: 40px; + display: flex; + column-gap: 16px; + padding-top: 120px; +} + +.job-info::before { + content: ""; + position: absolute; + width: 100%; + height: 174px; + top: 0; + left: 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 0.7), + rgba(0, 0, 0, 0.15) + ); + z-index: -1; + border-top-right-radius: 8px; +} + +.job-info__left { + padding-left: 42px; +} + +.job-info__right { + padding-right: 42px; +} + +.job-info__badge { + width: 55px; + height: 70px; + background-color: #d0d335; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 600; + margin-bottom: 13px; +} + +.job-info__below-badge { + display: flex; + justify-content: space-between; +} + +.job-info__time { + font-size: 12px; + transform: translateY(1px); + color: #494d4f; +} + +.bookmark-btn { + cursor: pointer; +} + +.bookmark-btn:hover svg { + color: #2671dd; +} + +.bookmark-btn svg { + color: #d7dbe0; + transition: all 0.2s; + font-size: 18px; +} + +.bookmark-btn .filled { + color: #2671dd; +} + +.job-info__company { + font-size: 14px; + font-style: italic; + color: rgba(255, 255, 255, 0.8); +} + +.job-info__description { + font-size: 14px; + margin-top: 18px; + margin-bottom: 12px; + line-height: 1.4; +} + +.job-info__extras { + display: flex; + column-gap: 35px; +} + +.job-info__extra { + font-size: 12px; + display: flex; + align-items: center; + gap: 5px; +} + +.job-info__extra-icon { + height: 23px; + width: 23px; + border-radius: 50%; + background-color: #e4e9ed; + display: flex; + justify-content: center; + align-items: center; + font-size: 9px; + margin-right: 8px; + color: #a1a8b0; +} + +/* QUALIFICATIONS */ +.qualifications { + display: flex; + padding-left: 42px; + padding-right: 42px; + margin-bottom: 30px; +} + +.qualifications__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.qualifications__left { + margin-right: 35px; +} + +.qualifications__list { + display: flex; + align-items: start; + flex-wrap: wrap; + gap: 6px; +} + +.qualifications__item { + font-size: 13px; + background-color: #e6ebee; + border-radius: 2px; + padding: 6px 10px; + color: #494d4f; +} + +/* REVIEWS */ +.reviews { + display: flex; + padding-left: 42px; + padding-right: 42px; +} + +.reviews__sub-text { + font-size: 13px; + width: 157px; + margin-top: 3px; +} + +.reviews__left { + margin-right: 35px; +} + +.reviews__list { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + column-gap: 20px; + row-gap: 20px; +} + +.reviews__item { + font-size: 13px; + font-style: italic; + color: #494d4f; + position: relative; + transform-style: preserve-3d; +} + +.reviews__item::before { + content: "“"; + position: absolute; + font-size: 50px; + top: -15px; + left: -10px; + color: #d2d7db; + transform: translateZ(-1px); +} + +/* FOOTER */ +.footer { + max-width: 1000px; + padding: 0 12px; + margin: 0 auto; + margin-top: 15px; + display: flex; + align-items: center; + justify-content: space-between; + color: #a4a9ac; +} + +/* COPYRIGHT */ +.footer > small { + font-size: 11px; + max-width: 200px; +} + +.footer > small *::selection { + background-color: rgba(255, 255, 255, 0.1); +} + +.footer > small > p:first-child { + line-height: 1.2; +} + +.footer > small > p:first-child > a { + text-decoration: underline; +} + +.footer > small > p:first-child > svg { + font-size: 10px; + margin-right: 4px; + margin-left: 2px; + color: #aeb3b6; +} + +/* JOBS AVAILABLE */ +.footer > p { + font-size: 11px; + align-self: flex-start; +} + +/* SPINNER */ +.spinner { + position: absolute; + border-radius: 50%; + animation: spinner 1s infinite linear; + left: 50%; + top: 18%; + width: 85px; + height: 85px; + border-top: 5px solid #e2e7e9; + border-right: 5px solid #e2e7e9; + border-bottom: 5px solid #e2e7e9; + border-left: 5px solid #ccd1d3; +} + +.spinner--search { +} + +.spinner--job-details { + left: 50%; + top: 40%; + width: 105px; + height: 105px; + border-top: 6px solid #d5d9db; + border-right: 6px solid #d5d9db; + border-bottom: 6px solid #d5d9db; + border-left: 6px solid #bbc0c2; +} + +.spinner--visible { + visibility: visible; +} + +/* ERROR */ +.error { + background: #fff; + padding: 14px 20px; + width: 280px; + min-height: 46px; + border-radius: 3px; + box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 15px; + right: 15px; + display: flex; + opacity: 0; + visibility: hidden; + transform: translateY(-120px); + transition: all 0.3s; +} + +.error--visible { + opacity: 1; + transform: initial; + visibility: initial; +} + +.error__icon { + font-size: 16px; + color: rgb(123, 64, 64); + margin-top: 2px; +} + +.error__right { + margin-left: 10px; +} + +.error__title { + text-transform: uppercase; + font-size: 12px; + margin-bottom: 1px; + font-weight: 500; +} + +.error__text { + font-size: 13px; + color: rgb(97, 98, 104); +} + +/* MEDIA QUERIES */ +@media (max-height: 925px) and (min-width: 1010px) { + .body { + padding-bottom: 50px; + } +} + +@media (max-width: 1179px) { + .job-list--bookmarks { + right: 0; + } +} + +@media (max-width: 1009px) { + .body { + padding: 0 12px; + padding-bottom: 50px; + } + + .header__top { + padding-left: 0; + padding-right: 0; + max-width: 800px; + } + + .container { + flex-direction: column; + height: initial; + width: 100%; + max-width: 800px; + border-radius: 8px; + overflow: hidden; + } + + .search-results { + width: 100%; + } + + .job-details { + display: none; + } + + .footer { + max-width: 800px; + padding-left: 0; + padding-right: 0; + } +} + +@media (max-width: 660px) { + .intro { + width: 100%; + } + + .search { + width: 100%; + } + + .search__input { + width: 100%; + } + + .footer { + justify-content: center; + } + + .footer > small { + text-align: center; + } + + .footer > p { + margin-left: 15px; + text-align: right; + display: none; + } + + .intro { + row-gap: 25px; + } + + .first-heading { + text-align: center; + max-width: 400px; + } +} + +@media (max-width: 370px) { + .job-list--bookmarks { + width: 93vw; + min-width: initial; + } + + .job-item { + width: 100%; + } + + .job-item__badge { + display: none; + } + + .error { + width: 93vw; + right: initial; + left: 50%; + transform: translateX(-50%); + } +} diff --git a/rmtdev/final-code/src/lib/constants.ts b/rmtdev/final-code/src/lib/constants.ts new file mode 100644 index 0000000..8da5f7c --- /dev/null +++ b/rmtdev/final-code/src/lib/constants.ts @@ -0,0 +1,4 @@ +export const BASE_API_URL = + "https://bytegrad.com/course-assets/projects/rmtdev/api/data"; + +export const RESULTS_PER_PAGE = 7; diff --git a/rmtdev/final-code/src/lib/hooks.ts b/rmtdev/final-code/src/lib/hooks.ts new file mode 100644 index 0000000..837b046 --- /dev/null +++ b/rmtdev/final-code/src/lib/hooks.ts @@ -0,0 +1,167 @@ +import { RefObject, useEffect, useState } from "react"; +import { useQueries, useQuery } from "@tanstack/react-query"; +import { BASE_API_URL } from "./constants"; +import { handleError } from "./utils"; +import { JobItem, JobItemExpanded, JobItemId } from "./types"; + +// ------------------------------ + +export function useOnClickOutside( + // refs can be a single ref or an array of refs + refs: RefObject | RefObject[], + callback: () => void +) { + useEffect(() => { + const handleClick = (e: MouseEvent) => { + if ( + // if clicked outside of the ref elements + Array.isArray(refs) + ? refs.every( + (refItem) => !refItem.current?.contains(e.target as Node) + ) + : !refs.current?.contains(e.target as Node) + ) { + callback(); + } + }; + + document.addEventListener("click", handleClick); + + return () => { + document.removeEventListener("click", handleClick); + }; + }, [refs, callback]); +} + +// ------------------------------ + +export function useActiveId() { + const [activeJobItemId, setActiveJobItemId] = useState(null); + + useEffect(() => { + const handleHashChange = () => { + const id = +window.location.hash.slice(1); + setActiveJobItemId(id); + }; + handleHashChange(); // for initial page load + + window.addEventListener("hashchange", handleHashChange); + + return () => { + window.removeEventListener("hashchange", handleHashChange); + }; + }, []); + + return activeJobItemId; +} + +// ------------------------------ + +export function useDebounce(value: T, delay = 500): T { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + const timerId = setTimeout(() => setDebouncedValue(value), delay); + + return () => clearTimeout(timerId); + }, [value, delay]); + + return debouncedValue; +} + +// ------------------------------ + +type JobItemsApiResponse = { + public: boolean; + sorted: boolean; + jobItems: JobItem[]; +}; + +const fetchJobItems = async ( + searchText: string +): Promise => { + const response = await fetch(`${BASE_API_URL}?search=${searchText}`); + if (!response.ok) { + // 4xx or 5xx response + const errorData = await response.json(); + throw new Error(errorData.description); + } + return await response.json(); +}; + +export function useSearchQuery(searchText: string) { + const { data, isLoading } = useQuery( + ["job-items", searchText], + () => fetchJobItems(searchText), + { + enabled: Boolean(searchText), + staleTime: 1000 * 20, + refetchOnWindowFocus: false, + retry: false, + onError: handleError, + } + ); + + return [data?.jobItems || [], isLoading] as const; +} + +// ------------------------------ + +type JobItemApiResponse = { + public: boolean; + jobItem: JobItemExpanded; +}; + +const fetchJobItem = async (id: number): Promise => { + const response = await fetch(`${BASE_API_URL}/${id}`); + if (!response.ok) { + // 4xx or 5xx response + const errorData = await response.json(); + throw new Error(errorData.description); + } + return await response.json(); +}; + +export function useJobItems(ids: JobItemId[]) { + const results = useQueries({ + queries: ids.map((id) => ({ + queryKey: ["job-item", id], + queryFn: () => fetchJobItem(id), + staleTime: 1000 * 60 * 60 * 24, + refetchOnWindowFocus: false, + retry: false, + enabled: Boolean(id), + onError: handleError, + })), + }); + + const atLeastOneLoading = results.some((result) => result.isLoading); + const jobItems = results + .map((result) => result.data?.jobItem) + // .filter(Boolean); + // .filter((jobItem) => jobItem !== undefined); + // .filter((jobItem) => !!jobItem); + .filter((jobItem) => jobItem !== undefined) as JobItemExpanded[]; + return { jobItems, isLoading: atLeastOneLoading } as const; +} + +export function useJobItem(id: JobItemId | null) { + const { data, isLoading } = useQuery( + ["job-item", id], + () => (id ? fetchJobItem(id) : undefined), + // () => { + // if (id) { + // return fetchJobItem(id); + // } + // }, + { + staleTime: 1000 * 60 * 60 * 24, + refetchOnWindowFocus: false, + retry: false, + enabled: Boolean(id), + onError: handleError, + } + ); + + return [data?.jobItem, isLoading] as const; +} diff --git a/rmtdev/final-code/src/lib/types.ts b/rmtdev/final-code/src/lib/types.ts new file mode 100644 index 0000000..d8630dd --- /dev/null +++ b/rmtdev/final-code/src/lib/types.ts @@ -0,0 +1,29 @@ +export type SortBy = "relevant" | "recent"; + +export type JobItem = { + id: number; + title: string; + badgeLetters: string; + company: string; + relevanceScore: number; + daysAgo: number; + coverImgURL: string; + companyURL: string; + duration: string; + salary: string; + location: string; + description: string; + qualifications: string[]; + reviews: string[]; +}; + +export type JobItemExpanded = JobItem & { + description: string; + qualifications: string[]; + reviews: string[]; + location: string; + salary: string; + duration: string; +}; + +export type JobItemId = JobItem["id"]; diff --git a/rmtdev/final-code/src/lib/utils.ts b/rmtdev/final-code/src/lib/utils.ts new file mode 100644 index 0000000..382c4f6 --- /dev/null +++ b/rmtdev/final-code/src/lib/utils.ts @@ -0,0 +1,18 @@ +import toast from "react-hot-toast"; + +export const urlContainsHashId = () => { + const id = +window.location.hash.slice(1); + return Boolean(id); +}; + +export const handleError = (error: unknown) => { + let message = "An error occurred."; + + if (error instanceof Error) { + message = error.message; + } else if (typeof error === "string") { + message = error; + } + + toast.error(message); +}; diff --git a/rmtdev/final-code/src/main.tsx b/rmtdev/final-code/src/main.tsx new file mode 100644 index 0000000..e5fb71e --- /dev/null +++ b/rmtdev/final-code/src/main.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./components/App.tsx"; +import "./index.css"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import ActiveIdContextProvider from "./contexts/ActiveIdContextProvider.tsx"; +import SearchTextContextProvider from "./contexts/SearchTextContextProvider.tsx"; +import BookmarksContextProvider from "./contexts/BookmarksContextProvider.tsx"; +import JobItemsContextProvider from "./contexts/JobItemsContextProvider.tsx"; + +const queryClient = new QueryClient(); + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + + + + + + + + + + + +); diff --git a/rmtdev/final-code/src/vite-env.d.ts b/rmtdev/final-code/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/rmtdev/final-code/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/rmtdev/final-code/tsconfig.json b/rmtdev/final-code/tsconfig.json new file mode 100644 index 0000000..a7fc6fb --- /dev/null +++ b/rmtdev/final-code/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/rmtdev/final-code/tsconfig.node.json b/rmtdev/final-code/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/rmtdev/final-code/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/rmtdev/final-code/vite.config.ts b/rmtdev/final-code/vite.config.ts new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/rmtdev/final-code/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/rmtdev/resources/base-url.txt b/rmtdev/resources/base-url.txt new file mode 100644 index 0000000..54da27f --- /dev/null +++ b/rmtdev/resources/base-url.txt @@ -0,0 +1 @@ +https://bytegrad.com/course-assets/projects/rmtdev/api/data \ No newline at end of file diff --git a/rmtdev/resources/starter.txt b/rmtdev/resources/starter.txt new file mode 100644 index 0000000..e54b075 --- /dev/null +++ b/rmtdev/resources/starter.txt @@ -0,0 +1 @@ +https://github.com/ByteGrad/starter-rmtdev \ No newline at end of file From 850e96f761b3d0108b7bfe78e1bfdf3b86ca1e25 Mon Sep 17 00:00:00 2001 From: ByteGrad Date: Mon, 25 Dec 2023 17:58:50 +0100 Subject: [PATCH 32/40] clarify starter --- rmtdev/resources/starter.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rmtdev/resources/starter.txt b/rmtdev/resources/starter.txt index e54b075..f504ceb 100644 --- a/rmtdev/resources/starter.txt +++ b/rmtdev/resources/starter.txt @@ -1 +1 @@ -https://github.com/ByteGrad/starter-rmtdev \ No newline at end of file +Go to this url to see the complete starter: https://github.com/ByteGrad/starter-rmtdev \ No newline at end of file From 9f052a559f02d2ce9d4b2bc2a59488efee545ca4 Mon Sep 17 00:00:00 2001 From: admin Date: Sat, 6 Jan 2024 02:03:22 +0200 Subject: [PATCH 33/40] fix useLocalStorage --- rmtdev/final-code/package-lock.json | 289 +++++++++--------- rmtdev/final-code/package.json | 30 +- .../src/components/App-Before-Context.tsx | 102 ------- rmtdev/final-code/src/components/App.tsx | 22 +- .../src/components/BookmarkIcon.tsx | 17 +- .../src/components/BookmarksButton.tsx | 47 +-- .../src/components/BookmarksPopover.tsx | 15 +- .../final-code/src/components/Container.tsx | 6 +- rmtdev/final-code/src/components/Header.tsx | 7 +- .../src/components/JobItemContent.tsx | 36 +-- rmtdev/final-code/src/components/JobList.tsx | 30 ++ .../JobList/JobList-Before-Context.tsx | 37 --- .../src/components/JobList/JobList.tsx | 30 -- .../components/JobList/JobListBookmarks.tsx | 8 - .../src/components/JobList/JobListItem.tsx | 63 ---- .../final-code/src/components/JobListItem.tsx | 27 ++ .../{JobList => }/JobListSearch.tsx | 2 +- .../components/Pagination-Before-Context.tsx | 68 ----- .../src/components/PaginationControls.tsx | 33 +- .../ResultsCount-Before-Context.tsx | 7 - .../src/components/ResultsCount.tsx | 2 +- .../src/components/Search-Before-Context.tsx | 31 -- .../final-code/src/components/SearchForm.tsx | 14 +- rmtdev/final-code/src/components/Sidebar.tsx | 15 +- .../src/components/Sorting-Before-Context.tsx | 39 --- .../src/components/SortingControls.tsx | 52 ++-- .../src/contexts/ActiveIdContextProvider.tsx | 37 +-- .../src/contexts/BookmarksContextProvider.tsx | 88 ++---- .../src/contexts/JobItemsContextProvider.tsx | 96 +++--- .../contexts/SearchTextContextProvider.tsx | 58 +--- rmtdev/final-code/src/lib/hooks.ts | 286 ++++++++++------- rmtdev/final-code/src/lib/types.ts | 21 +- rmtdev/final-code/src/lib/utils.ts | 9 +- rmtdev/final-code/src/main.tsx | 18 +- 34 files changed, 598 insertions(+), 1044 deletions(-) delete mode 100644 rmtdev/final-code/src/components/App-Before-Context.tsx create mode 100644 rmtdev/final-code/src/components/JobList.tsx delete mode 100644 rmtdev/final-code/src/components/JobList/JobList-Before-Context.tsx delete mode 100644 rmtdev/final-code/src/components/JobList/JobList.tsx delete mode 100644 rmtdev/final-code/src/components/JobList/JobListBookmarks.tsx delete mode 100644 rmtdev/final-code/src/components/JobList/JobListItem.tsx create mode 100644 rmtdev/final-code/src/components/JobListItem.tsx rename rmtdev/final-code/src/components/{JobList => }/JobListSearch.tsx (74%) delete mode 100644 rmtdev/final-code/src/components/Pagination-Before-Context.tsx delete mode 100644 rmtdev/final-code/src/components/ResultsCount-Before-Context.tsx delete mode 100644 rmtdev/final-code/src/components/Search-Before-Context.tsx delete mode 100644 rmtdev/final-code/src/components/Sorting-Before-Context.tsx diff --git a/rmtdev/final-code/package-lock.json b/rmtdev/final-code/package-lock.json index e17cc4d..008c936 100644 --- a/rmtdev/final-code/package-lock.json +++ b/rmtdev/final-code/package-lock.json @@ -8,23 +8,23 @@ "name": "rmtdev", "version": "0.0.0", "dependencies": { - "@radix-ui/react-icons": "^1.3.0", - "@tanstack/react-query": "^4.36.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "@radix-ui/react-icons": "1.3.0", + "@tanstack/react-query": "4.36.1", + "react": "18.2.0", + "react-dom": "18.2.0", "react-hot-toast": "^2.4.1" }, "devDependencies": { - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "@vitejs/plugin-react": "^4.0.3", - "eslint": "^8.45.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "typescript": "^5.0.2", - "vite": "^4.4.5" + "@types/react": "18.2.15", + "@types/react-dom": "18.2.7", + "@typescript-eslint/eslint-plugin": "6.0.0", + "@typescript-eslint/parser": "6.0.0", + "@vitejs/plugin-react": "4.0.3", + "eslint": "8.45.0", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-refresh": "0.4.3", + "typescript": "5.0.2", + "vite": "4.4.5" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -809,9 +809,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", - "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -976,51 +976,10 @@ } } }, - "node_modules/@types/babel__core": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", - "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", - "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", - "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", - "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/prop-types": { @@ -1030,9 +989,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.25", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.25.tgz", - "integrity": "sha512-24xqse6+VByVLIr+xWaQ9muX1B4bXJKXBbjszbld/UEDslGLY53+ZucF44HCmLbMPejTzGG9XgR+3m2/Wqu1kw==", + "version": "18.2.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.15.tgz", + "integrity": "sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -1041,9 +1000,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.11.tgz", - "integrity": "sha512-zq6Dy0EiCuF9pWFW6I6k6W2LdpUixLE4P6XjXU1QHLfak3GPACQfLwEuHzY5pOYa4hzj1d0GxX/P141aFjZsyg==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", + "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", "dev": true, "dependencies": { "@types/react": "*" @@ -1056,27 +1015,29 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", - "integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.0.0.tgz", + "integrity": "sha512-xuv6ghKGoiq856Bww/yVYnXGsKa588kY3M0XK7uUW/3fJNNULKRfZfSBkMTSpqGG/8ZCXCadfh8G/z/B4aqS/A==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/type-utils": "6.7.4", - "@typescript-eslint/utils": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@eslint-community/regexpp": "^4.5.0", + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/type-utils": "6.0.0", + "@typescript-eslint/utils": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", - "semver": "^7.5.4", + "natural-compare-lite": "^1.4.0", + "semver": "^7.5.0", "ts-api-utils": "^1.0.1" }, "engines": { @@ -1097,15 +1058,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", - "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.0.0.tgz", + "integrity": "sha512-TNaufYSPrr1U8n+3xN+Yp9g31vQDJqhXzzPSHfQDLcaO4tU+mCfODPxCwf4H530zo7aUBE3QIdxCXamEnG04Tg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/typescript-estree": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", "debug": "^4.3.4" }, "engines": { @@ -1125,13 +1086,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", - "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.0.0.tgz", + "integrity": "sha512-o4q0KHlgCZTqjuaZ25nw5W57NeykZT9LiMEG4do/ovwvOcPnDO1BI5BQdCsUkjxFyrCL0cSzLjvIMfR9uo7cWg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4" + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1142,13 +1103,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", - "integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.0.0.tgz", + "integrity": "sha512-ah6LJvLgkoZ/pyJ9GAdFkzeuMZ8goV6BH7eC9FPmojrnX9yNCIsfjB+zYcnex28YO3RFvBkV6rMV6WpIqkPvoQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/utils": "6.7.4", + "@typescript-eslint/typescript-estree": "6.0.0", + "@typescript-eslint/utils": "6.0.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1169,9 +1130,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", - "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.0.0.tgz", + "integrity": "sha512-Zk9KDggyZM6tj0AJWYYKgF0yQyrcnievdhG0g5FqyU3Y2DRxJn4yWY21sJC0QKBckbsdKKjYDV2yVrrEvuTgxg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1182,17 +1143,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", - "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.0.0.tgz", + "integrity": "sha512-2zq4O7P6YCQADfmJ5OTDQTP3ktajnXIRrYAtHM9ofto/CJZV3QfJ89GEaM2BNGeSr1KgmBuLhEkz5FBkS2RQhQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/visitor-keys": "6.0.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.5.4", + "semver": "^7.5.0", "ts-api-utils": "^1.0.1" }, "engines": { @@ -1209,18 +1170,19 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", - "integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.0.0.tgz", + "integrity": "sha512-SOr6l4NB6HE4H/ktz0JVVWNXqCJTOo/mHnvIte1ZhBQ0Cvd04x5uKZa3zT6tiodL06zf5xxdK8COiDvPnQ27JQ==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "semver": "^7.5.4" + "@eslint-community/eslint-utils": "^4.3.0", + "@types/json-schema": "^7.0.11", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "6.0.0", + "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/typescript-estree": "6.0.0", + "eslint-scope": "^5.1.1", + "semver": "^7.5.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1233,13 +1195,35 @@ "eslint": "^7.0.0 || ^8.0.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", - "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.0.0.tgz", + "integrity": "sha512-cvJ63l8c0yXdeT5POHpL0Q1cZoRcmRKFCtSjNGJxPkcP571EfZMcNbzWAc7oK3D1dRzm/V5EwtkANTZxqvuuUA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/types": "6.0.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1251,15 +1235,14 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.1.0.tgz", - "integrity": "sha512-rM0SqazU9iqPUraQ2JlIvReeaxOoRj6n+PzB1C0cBzIbd8qP336nC39/R9yPi3wVcah7E7j/kdU1uCUqMEU4OQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.3.tgz", + "integrity": "sha512-pwXDog5nwwvSIzwrvYYmA2Ljcd/ZNlcsSG2Q9CNDBwnsd55UGAyr2doXtB5j+2uymRCnCfExlznzzSFbBRcoCg==", "dev": true, "dependencies": { - "@babel/core": "^7.22.20", + "@babel/core": "^7.22.5", "@babel/plugin-transform-react-jsx-self": "^7.22.5", "@babel/plugin-transform-react-jsx-source": "^7.22.5", - "@types/babel__core": "^7.20.2", "react-refresh": "^0.14.0" }, "engines": { @@ -1600,27 +1583,27 @@ } }, "node_modules/eslint": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", - "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", + "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.51.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.12.4", + "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.6.0", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -1865,9 +1848,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -2071,6 +2054,12 @@ "csstype": "^3.0.10" } }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2367,6 +2356,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "node_modules/node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", @@ -2861,16 +2856,16 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", + "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=12.20" } }, "node_modules/update-browserslist-db": { @@ -2921,14 +2916,14 @@ } }, "node_modules/vite": { - "version": "4.4.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz", - "integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.5.tgz", + "integrity": "sha512-4m5kEtAWHYr0O1Fu7rZp64CfO1PsRGZlD3TAB32UmQlpd7qg15VF7ROqGN5CyqN7HFuwr7ICNM2+fDWRqFEKaA==", "dev": true, "dependencies": { "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" + "postcss": "^8.4.26", + "rollup": "^3.25.2" }, "bin": { "vite": "bin/vite.js" diff --git a/rmtdev/final-code/package.json b/rmtdev/final-code/package.json index ed4b4cd..c96f99d 100644 --- a/rmtdev/final-code/package.json +++ b/rmtdev/final-code/package.json @@ -10,22 +10,22 @@ "preview": "vite preview" }, "dependencies": { - "@radix-ui/react-icons": "^1.3.0", - "@tanstack/react-query": "^4.36.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-hot-toast": "^2.4.1" + "@radix-ui/react-icons": "1.3.0", + "@tanstack/react-query": "4.36.1", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-hot-toast": "2.4.1" }, "devDependencies": { - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "@vitejs/plugin-react": "^4.0.3", - "eslint": "^8.45.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "typescript": "^5.0.2", - "vite": "^4.4.5" + "@types/react": "18.2.15", + "@types/react-dom": "18.2.7", + "@typescript-eslint/eslint-plugin": "6.0.0", + "@typescript-eslint/parser": "6.0.0", + "@vitejs/plugin-react": "4.0.3", + "eslint": "8.45.0", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-refresh": "0.4.3", + "typescript": "5.0.2", + "vite": "4.4.5" } } diff --git a/rmtdev/final-code/src/components/App-Before-Context.tsx b/rmtdev/final-code/src/components/App-Before-Context.tsx deleted file mode 100644 index a577806..0000000 --- a/rmtdev/final-code/src/components/App-Before-Context.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { useState, useMemo } from "react"; -import Background from "./Background"; -import Container from "./Container"; -import Footer from "./Footer"; -import Header, { HeaderTop } from "./Header"; -import { useDebounce, useJobItems } from "../lib/hooks"; -import Sidebar, { SidebarTop } from "./Sidebar"; -import JobList from "./JobList/JobList"; -import Search from "./SearchForm"; -import { Toaster } from "react-hot-toast"; -import ResultsCount from "./ResultsCount"; -import Sorting from "./SortingControls"; -import Pagination from "./PaginationControls"; -import JobItemContent from "./JobItemContent"; -import Logo from "./Logo"; -import Bookmarks from "./BookmarksButton"; -import { RESULTS_PER_PAGE } from "../lib/constants"; -import type { SortBy } from "../lib/types"; - -function App() { - const [searchText, setSearchText] = useState(""); - const debouncedSearchText = useDebounce(searchText, 250); - const [allJobItems, isLoading] = useJobItems(debouncedSearchText); - const [sortBy, setSortBy] = useState("relevant"); - const [currentPage, setCurrentPage] = useState(1); - - const totalNumberOfResults = allJobItems.length || 0; - const totalNumberOfPages = Math.ceil(totalNumberOfResults / RESULTS_PER_PAGE); - const sortedAllJobItems = useMemo(() => { - if (sortBy === "relevant") { - return [...allJobItems].sort((a, b) => { - return b.relevanceScore - a.relevanceScore; - }); - } else { - return [...allJobItems].sort((a, b) => { - return a.daysAgo - b.daysAgo; - }); - } - }, [allJobItems, sortBy]); - const jobItems = useMemo( - () => - sortedAllJobItems?.slice( - currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, - currentPage * RESULTS_PER_PAGE - ), - [sortedAllJobItems, currentPage] - ); - - const handleSearchChange = (e: React.ChangeEvent) => { - setSearchText(e.target.value); - }; - const handleSortChange = (newSortBy: SortBy) => { - setCurrentPage(1); - setSortBy(newSortBy); - }; - const handlePageChange = (newPage: number) => { - setCurrentPage(newPage); - }; - - return ( - <> - - -
    - - - - - - -
    - - - - - - - - - 0} - /> - - - - - - - -
    - - - - ); -} - -export default App; diff --git a/rmtdev/final-code/src/components/App.tsx b/rmtdev/final-code/src/components/App.tsx index 4204d26..2cc906e 100644 --- a/rmtdev/final-code/src/components/App.tsx +++ b/rmtdev/final-code/src/components/App.tsx @@ -2,16 +2,16 @@ import Background from "./Background"; import Container from "./Container"; import Footer from "./Footer"; import Header, { HeaderTop } from "./Header"; -import Sidebar, { SidebarTop } from "./Sidebar"; -import { Toaster } from "react-hot-toast"; -import ResultsCount from "./ResultsCount"; -import Sorting from "./SortingControls"; -import Pagination from "./PaginationControls"; -import JobItemContent from "./JobItemContent"; +import BookmarksButton from "./BookmarksButton"; import Logo from "./Logo"; -import Bookmarks from "./BookmarksButton"; -import JobListSearch from "./JobList/JobListSearch"; import SearchForm from "./SearchForm"; +import JobItemContent from "./JobItemContent"; +import Sidebar, { SidebarTop } from "./Sidebar"; +import PaginationControls from "./PaginationControls"; +import ResultsCount from "./ResultsCount"; +import SortingControls from "./SortingControls"; +import { Toaster } from "react-hot-toast"; +import JobListSearch from "./JobListSearch"; function App() { return ( @@ -21,7 +21,7 @@ function App() {
    - + @@ -31,12 +31,12 @@ function App() { - + - + diff --git a/rmtdev/final-code/src/components/BookmarkIcon.tsx b/rmtdev/final-code/src/components/BookmarkIcon.tsx index 8515db0..747b508 100644 --- a/rmtdev/final-code/src/components/BookmarkIcon.tsx +++ b/rmtdev/final-code/src/components/BookmarkIcon.tsx @@ -1,27 +1,26 @@ import { BookmarkFilledIcon } from "@radix-ui/react-icons"; -import { JobItemId } from "../lib/types"; -import { useBookmarksContext } from "../contexts/BookmarksContextProvider"; +import { useBookmarksContext } from "../lib/hooks"; type BookmarkIconProps = { - jobItemId: JobItemId; + id: number; }; -export default function BookmarkIcon({ jobItemId }: BookmarkIconProps) { - const { bookmarkedJobItemIds, handleToggleBookmark } = useBookmarksContext(); +export default function BookmarkIcon({ id }: BookmarkIconProps) { + const { bookmarkedIds, handleToggleBookmark } = useBookmarksContext(); return ( ); diff --git a/rmtdev/final-code/src/components/BookmarksButton.tsx b/rmtdev/final-code/src/components/BookmarksButton.tsx index f5076bb..5d22b28 100644 --- a/rmtdev/final-code/src/components/BookmarksButton.tsx +++ b/rmtdev/final-code/src/components/BookmarksButton.tsx @@ -1,60 +1,19 @@ -import { useState, useEffect, useRef } from "react"; import { TriangleDownIcon } from "@radix-ui/react-icons"; import BookmarksPopover from "./BookmarksPopover"; +import { useRef, useState } from "react"; import { useOnClickOutside } from "../lib/hooks"; -export default function Bookmarks() { - // also called local state +export default function BookmarksButton() { const [isOpen, setIsOpen] = useState(false); const buttonRef = useRef(null); const popoverRef = useRef(null); - - // hooks for common use cases are available (show google results) - - // detect click outside of the bookmarks list with own solution, without ref - // useEffect(() => { - // function handleClick(event: MouseEvent) { - // const target = event.target as HTMLElement; - // if ( - // !target.closest(".bookmarks-popover") && - // !target.closest(".bookmarks-btn") - // ) { - // setIsOpen(false); - // } - // } - - // document.addEventListener("click", handleClick); - - // return () => document.removeEventListener("click", handleClick); - // }, []); - - // detect click outside of the bookmarks list with own solution, with ref - // useEffect(() => { - // function handleClick(event: MouseEvent) { - // const target = event.target as HTMLElement; - // if ( - // // use ref to check if the click is inside the popover - // !popoverRef.current?.contains(target) && - // // use ref to check if the click is inside the button - // !buttonRef.current?.contains(target) - // ) { - // setIsOpen(false); - // } - // } - - // document.addEventListener("click", handleClick); - - // return () => document.removeEventListener("click", handleClick); - // }, []); - - // use hook useOnClickOutside([buttonRef, popoverRef], () => setIsOpen(false)); return (
    @@ -70,15 +63,11 @@ export default function JobItemContent() {

      - {/* {jobItem.qualifications.map((qualificationText, index) => ( - // careful with key=index, it's not recommended -
    • - {qualificationText} + {jobItem.qualifications.map((qualification) => ( +
    • + {qualification}
    • - ))} */} -
    • Test
    • -
    • Test
    • -
    • Test
    • + ))}
    @@ -90,10 +79,9 @@ export default function JobItemContent() {

      - {jobItem.reviews.map((reviewText, index) => ( - // careful with key=index, it's not recommended -
    • - {reviewText} + {jobItem.reviews.map((review) => ( +
    • + {review}
    • ))}
    diff --git a/rmtdev/final-code/src/components/JobList.tsx b/rmtdev/final-code/src/components/JobList.tsx new file mode 100644 index 0000000..aac79cf --- /dev/null +++ b/rmtdev/final-code/src/components/JobList.tsx @@ -0,0 +1,30 @@ +import { useActiveIdContext, useJobItemsContext } from "../lib/hooks"; +import { JobItem } from "../lib/types"; +import JobListItem from "./JobListItem"; +import Spinner from "./Spinner"; + +type JobListProps = { + jobItems: JobItem[]; + isLoading: boolean; +}; + +export function JobList({ jobItems, isLoading }: JobListProps) { + const { activeId } = useActiveIdContext(); + + return ( +
      + {isLoading && } + + {!isLoading && + jobItems.map((jobItem) => ( + + ))} +
    + ); +} + +export default JobList; diff --git a/rmtdev/final-code/src/components/JobList/JobList-Before-Context.tsx b/rmtdev/final-code/src/components/JobList/JobList-Before-Context.tsx deleted file mode 100644 index d4bbb3c..0000000 --- a/rmtdev/final-code/src/components/JobList/JobList-Before-Context.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { memo } from "react"; -import { useActiveIdContext, useBookmarksContext } from "../../lib/hooks"; -import { JobItem } from "../../lib/types"; -import JobListItem from "./JobListItem"; -import Spinner from "../Spinner"; - -type JobListProps = { - jobItems: JobItem[]; - isLoading: boolean; -}; - -// show that before memo it renders every key stroke -// pick a bigger debounce to show it properly -const JobList = memo(({ jobItems, isLoading }: JobListProps) => { - console.log("JobList rendering..."); - const { bookmarkedJobItems } = useBookmarksContext(); - const { activeJobItemId } = useActiveIdContext(); - - return ( -
      - {isLoading && } - - {jobItems.map((jobItem) => ( - bookmarkedJobItem.id === jobItem.id - )} - /> - ))} -
    - ); -}); - -export default JobList; diff --git a/rmtdev/final-code/src/components/JobList/JobList.tsx b/rmtdev/final-code/src/components/JobList/JobList.tsx deleted file mode 100644 index d02dee3..0000000 --- a/rmtdev/final-code/src/components/JobList/JobList.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useActiveIdContext } from "../../contexts/ActiveIdContextProvider"; -import { JobItem } from "../../lib/types"; -import JobListItem from "./JobListItem"; -import Spinner from "../Spinner"; - -type JobListProps = { - jobItems: JobItem[]; - isLoading?: boolean; -}; - -const JobList = ({ jobItems, isLoading = false }: JobListProps) => { - const { activeJobItemId } = useActiveIdContext(); - - return ( -
      - {isLoading && } - - {!isLoading && - jobItems.map((jobItem) => ( - - ))} -
    - ); -}; - -export default JobList; diff --git a/rmtdev/final-code/src/components/JobList/JobListBookmarks.tsx b/rmtdev/final-code/src/components/JobList/JobListBookmarks.tsx deleted file mode 100644 index 893a6e0..0000000 --- a/rmtdev/final-code/src/components/JobList/JobListBookmarks.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { useBookmarksContext } from "../../contexts/BookmarksContextProvider"; -import JobList from "./JobList"; - -export default function JobListBookmarks() { - const { bookmarkedJobItems, isLoading } = useBookmarksContext(); - - return ; -} diff --git a/rmtdev/final-code/src/components/JobList/JobListItem.tsx b/rmtdev/final-code/src/components/JobList/JobListItem.tsx deleted file mode 100644 index 89d4754..0000000 --- a/rmtdev/final-code/src/components/JobList/JobListItem.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { JobItem } from "../../lib/types"; -import BookmarkIcon from "../BookmarkIcon"; - -// type JobListItemProps = { -// jobItem: JobItem; -// }; - -// type JobListItemProps = { -// id: number; -// badgeLetters: string; -// title: string; -// company: string; -// duration: string; -// salary: string; -// location: string; -// daysAgo: number; -// }; - -type JobListItemProps = JobItem & { - isActive?: boolean; -}; - -// export default function JobListItem({ jobItem }: JobListItemProps) { -export default function JobListItem({ - id, - badgeLetters, - title, - company, - daysAgo, - isActive, -}: JobListItemProps) { - return ( -
  • - -
    {badgeLetters}
    - -
    -

    {title}

    -

    {company}

    - {/*
    -

    - - {duration} -

    -

    - - {salary} -

    -

    - {" "} - {location} -

    -
    */} -
    - -
    - - -
    -
    -
  • - ); -} diff --git a/rmtdev/final-code/src/components/JobListItem.tsx b/rmtdev/final-code/src/components/JobListItem.tsx new file mode 100644 index 0000000..d9ccd5d --- /dev/null +++ b/rmtdev/final-code/src/components/JobListItem.tsx @@ -0,0 +1,27 @@ +import { JobItem } from "../lib/types"; +import BookmarkIcon from "./BookmarkIcon"; + +type JobListItemProps = { + jobItem: JobItem; + isActive: boolean; +}; + +export default function JobListItem({ jobItem, isActive }: JobListItemProps) { + return ( +
  • + +
    {jobItem.badgeLetters}
    + +
    +

    {jobItem.title}

    +

    {jobItem.company}

    +
    + +
    + + +
    +
    +
  • + ); +} diff --git a/rmtdev/final-code/src/components/JobList/JobListSearch.tsx b/rmtdev/final-code/src/components/JobListSearch.tsx similarity index 74% rename from rmtdev/final-code/src/components/JobList/JobListSearch.tsx rename to rmtdev/final-code/src/components/JobListSearch.tsx index f03aa80..c208bb7 100644 --- a/rmtdev/final-code/src/components/JobList/JobListSearch.tsx +++ b/rmtdev/final-code/src/components/JobListSearch.tsx @@ -1,4 +1,4 @@ -import { useJobItemsContext } from "../../contexts/JobItemsContextProvider"; +import { useJobItemsContext } from "../lib/hooks"; import JobList from "./JobList"; export default function JobListSearch() { diff --git a/rmtdev/final-code/src/components/Pagination-Before-Context.tsx b/rmtdev/final-code/src/components/Pagination-Before-Context.tsx deleted file mode 100644 index e3c0fd0..0000000 --- a/rmtdev/final-code/src/components/Pagination-Before-Context.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons"; - -type PaginationProps = { - currentPage: number; - totalNumberOfPages: number; - onPageChange: (newPage: number) => void; -}; - -export default function Pagination({ - currentPage, - totalNumberOfPages, - onPageChange, -}: PaginationProps) { - return ( -
    - {currentPage > 1 && ( - - )} - - {currentPage < totalNumberOfPages && ( - - )} -
    - ); -} - -type PaginationButtonProps = Omit & { - direction: "previous" | "next"; -}; - -function PaginationButton({ - direction, - currentPage, - onPageChange, -}: PaginationButtonProps) { - return ( - - ); -} diff --git a/rmtdev/final-code/src/components/PaginationControls.tsx b/rmtdev/final-code/src/components/PaginationControls.tsx index 86cdda0..65a838b 100644 --- a/rmtdev/final-code/src/components/PaginationControls.tsx +++ b/rmtdev/final-code/src/components/PaginationControls.tsx @@ -1,9 +1,13 @@ import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons"; -import { useJobItemsContext } from "../contexts/JobItemsContextProvider"; +import { PageDirection } from "../lib/types"; +import { useJobItemsContext } from "../lib/hooks"; -export default function Pagination() { - const { currentPage, totalNumberOfPages, handleChangePage } = - useJobItemsContext(); +export default function PaginationControls() { + const { + currentPage, + totalNumberOfPages, + handleChangePage: onClick, + } = useJobItemsContext(); return (
    @@ -11,15 +15,14 @@ export default function Pagination() { onClick("previous")} /> )} - {currentPage < totalNumberOfPages && ( onClick("next")} /> )}
    @@ -27,35 +30,33 @@ export default function Pagination() { } type PaginationButtonProps = { - direction: "previous" | "next"; + direction: PageDirection; currentPage: number; - onChangePage: (newPage: number) => void; + onClick: () => void; }; function PaginationButton({ direction, currentPage, - onChangePage, + onClick, }: PaginationButtonProps) { return ( - - onSearchChange(e.target.value)} - spellCheck="false" - type="text" - required - placeholder="Find remote developer jobs..." - /> - - ); -} diff --git a/rmtdev/final-code/src/components/SearchForm.tsx b/rmtdev/final-code/src/components/SearchForm.tsx index ba5eff6..896127e 100644 --- a/rmtdev/final-code/src/components/SearchForm.tsx +++ b/rmtdev/final-code/src/components/SearchForm.tsx @@ -1,15 +1,11 @@ -import { useSearchTextContext } from "../contexts/SearchTextContextProvider"; - -export default function Search() { - const { searchText, handleChangeSearch } = useSearchTextContext(); - +export default function SearchForm({ searchText, setSearchText }) { return (
    { e.preventDefault(); }} + action="#" + className="search" > - - - - ); -} diff --git a/rmtdev/final-code/src/components/SortingControls.tsx b/rmtdev/final-code/src/components/SortingControls.tsx index 8766bc7..e254834 100644 --- a/rmtdev/final-code/src/components/SortingControls.tsx +++ b/rmtdev/final-code/src/components/SortingControls.tsx @@ -1,35 +1,43 @@ -import { useJobItemsContext } from "../contexts/JobItemsContextProvider"; +import { useJobItemsContext } from "../lib/hooks"; -export default function Sorting() { - const { sortBy, handleChangeSort } = useJobItemsContext(); +export default function SortingControls() { + const { sortBy, handleChangeSortBy } = useJobItemsContext(); return (
    - - - +
    ); } + +type SortingButtonProps = { + children: React.ReactNode; + onClick: () => void; + isActive: boolean; +}; + +function SortingButton({ children, onClick, isActive }: SortingButtonProps) { + return ( + + ); +} diff --git a/rmtdev/final-code/src/contexts/ActiveIdContextProvider.tsx b/rmtdev/final-code/src/contexts/ActiveIdContextProvider.tsx index 067f02f..8cb78ce 100644 --- a/rmtdev/final-code/src/contexts/ActiveIdContextProvider.tsx +++ b/rmtdev/final-code/src/contexts/ActiveIdContextProvider.tsx @@ -1,41 +1,26 @@ -import React, { createContext, useContext, useMemo } from "react"; -import { JobItemId } from "../lib/types"; +import { createContext } from "react"; import { useActiveId } from "../lib/hooks"; -type ActiveIdProviderProps = { - children: React.ReactNode; -}; - type ActiveIdContext = { - activeJobItemId: JobItemId | null; + activeId: number | null; }; export const ActiveIdContext = createContext(null); export default function ActiveIdContextProvider({ children, -}: ActiveIdProviderProps) { - const activeJobItemId = useActiveId(); - - const contextValue = useMemo( - () => ({ - activeJobItemId, - }), - [activeJobItemId] - ); +}: { + children: React.ReactNode; +}) { + const activeId = useActiveId(); return ( - + {children} ); } - -export function useActiveIdContext() { - const context = useContext(ActiveIdContext); - - if (!context) { - throw new Error("ActiveIdContext must be used within an ActiveIdProvider"); - } - return context; -} diff --git a/rmtdev/final-code/src/contexts/BookmarksContextProvider.tsx b/rmtdev/final-code/src/contexts/BookmarksContextProvider.tsx index 6ba98b5..5ded3ba 100644 --- a/rmtdev/final-code/src/contexts/BookmarksContextProvider.tsx +++ b/rmtdev/final-code/src/contexts/BookmarksContextProvider.tsx @@ -1,78 +1,46 @@ -import React, { - createContext, - useState, - useEffect, - useMemo, - useContext, - useCallback, -} from "react"; -import { JobItem, JobItemId } from "../lib/types"; -import { useJobItems } from "../lib/hooks"; - -type BookmarksProviderProps = { - children: React.ReactNode; -}; +import { createContext } from "react"; +import { useJobItems, useLocalStorage } from "../lib/hooks"; +import { JobItemExpanded } from "../lib/types"; type BookmarksContext = { - bookmarkedJobItems: JobItem[]; - bookmarkedJobItemIds: JobItemId[]; + bookmarkedIds: number[]; + handleToggleBookmark: (id: number) => void; + bookmarkedJobItems: JobItemExpanded[]; isLoading: boolean; - handleToggleBookmark: (jobItem: JobItemId) => void; }; export const BookmarksContext = createContext(null); export default function BookmarksContextProvider({ children, -}: BookmarksProviderProps) { - // state - const [bookmarkedJobItemIds, setBookmarkedJobItemIds] = useState( - () => JSON.parse(localStorage.getItem("bookmarkedJobItemIds") || "[]") +}: { + children: React.ReactNode; +}) { + const [bookmarkedIds, setBookmarkedIds] = useLocalStorage( + "bookmarkedIds", + [] ); const { jobItems: bookmarkedJobItems, isLoading } = - useJobItems(bookmarkedJobItemIds); - - // event handlers / actions - const handleToggleBookmark = useCallback((jobItemId: JobItemId) => { - setBookmarkedJobItemIds((prevBookmarkedJobItemIds) => { - if (prevBookmarkedJobItemIds.includes(jobItemId)) { - return prevBookmarkedJobItemIds.filter((id) => id !== jobItemId); - } else { - return [...prevBookmarkedJobItemIds, jobItemId]; - } - }); - }, []); + useJobItems(bookmarkedIds); - // side effects - useEffect(() => { - localStorage.setItem( - "bookmarkedJobItemIds", - JSON.stringify(bookmarkedJobItemIds) - ); - }, [bookmarkedJobItemIds]); - - // context value - const contextValue = useMemo( - () => ({ - bookmarkedJobItems, - bookmarkedJobItemIds, - isLoading, - handleToggleBookmark, - }), - [bookmarkedJobItems, bookmarkedJobItemIds, isLoading, handleToggleBookmark] - ); + const handleToggleBookmark = (id: number) => { + if (bookmarkedIds.includes(id)) { + setBookmarkedIds((prev) => prev.filter((item) => item !== id)); + } else { + setBookmarkedIds((prev) => [...prev, id]); + } + }; return ( - + {children} ); } - -export function useBookmarksContext() { - const context = useContext(BookmarksContext); - if (!context) { - throw new Error("BookmarksContext must be used within a BookmarksProvider"); - } - return context; -} diff --git a/rmtdev/final-code/src/contexts/JobItemsContextProvider.tsx b/rmtdev/final-code/src/contexts/JobItemsContextProvider.tsx index e02f4cc..1e9837a 100644 --- a/rmtdev/final-code/src/contexts/JobItemsContextProvider.tsx +++ b/rmtdev/final-code/src/contexts/JobItemsContextProvider.tsx @@ -1,100 +1,93 @@ -import React, { - useState, - createContext, - useMemo, - useCallback, - useContext, -} from "react"; -import { useJobItems, useSearchQuery } from "../lib/hooks"; -import { JobItem, SortBy } from "../lib/types"; +import { createContext, useCallback, useMemo, useState } from "react"; +import { useSearchQuery, useSearchTextContext } from "../lib/hooks"; import { RESULTS_PER_PAGE } from "../lib/constants"; -import { useSearchTextContext } from "./SearchTextContextProvider"; - -type JobItemsProviderProps = { - children: React.ReactNode; -}; +import { SortBy, PageDirection, JobItem } from "../lib/types"; type JobItemsContext = { - jobItems: JobItem[]; + jobItems: JobItem[] | undefined; jobItemsSortedAndSliced: JobItem[]; isLoading: boolean; totalNumberOfResults: number; totalNumberOfPages: number; currentPage: number; - handleChangePage: (newPage: number) => void; sortBy: SortBy; - handleChangeSort: (newSortBy: SortBy) => void; + handleChangePage: (direction: PageDirection) => void; + handleChangeSortBy: (newSortBy: SortBy) => void; }; export const JobItemsContext = createContext(null); export default function JobItemsContextProvider({ children, -}: JobItemsProviderProps) { +}: { + children: React.ReactNode; +}) { // dependency on other context const { debouncedSearchText } = useSearchTextContext(); // state - const [jobItems, isLoading] = useSearchQuery(debouncedSearchText); - const [sortBy, setSortBy] = useState("relevant"); + const { jobItems, isLoading } = useSearchQuery(debouncedSearchText); const [currentPage, setCurrentPage] = useState(1); + const [sortBy, setSortBy] = useState("relevant"); // derived / computed state - const totalNumberOfResults = jobItems.length || 0; - const totalNumberOfPages = Math.ceil(totalNumberOfResults / RESULTS_PER_PAGE); - const jobItemsSorted = useMemo(() => { - if (sortBy === "relevant") { - return [...jobItems].sort((a, b) => { - return b.relevanceScore - a.relevanceScore; - }); - } else { - return [...jobItems].sort((a, b) => { - return a.daysAgo - b.daysAgo; - }); - } - }, [jobItems, sortBy]); + const totalNumberOfResults = jobItems?.length || 0; + const totalNumberOfPages = totalNumberOfResults / RESULTS_PER_PAGE; + const jobItemsSorted = useMemo( + () => + [...(jobItems || [])].sort((a, b) => { + if (sortBy === "relevant") { + return b.relevanceScore - a.relevanceScore; + } else { + return a.daysAgo - b.daysAgo; + } + }), + [sortBy, jobItems] + ); const jobItemsSortedAndSliced = useMemo( () => - jobItemsSorted?.slice( + jobItemsSorted.slice( currentPage * RESULTS_PER_PAGE - RESULTS_PER_PAGE, currentPage * RESULTS_PER_PAGE ), - [jobItemsSorted, currentPage] + [currentPage, jobItemsSorted] ); // event handlers / actions - const handleChangeSort = useCallback((newSortBy: SortBy) => { + const handleChangePage = useCallback((direction: PageDirection) => { + if (direction === "next") { + setCurrentPage((prev) => prev + 1); + } else if (direction === "previous") { + setCurrentPage((prev) => prev - 1); + } + }, []); + const handleChangeSortBy = useCallback((newSortBy: SortBy) => { setCurrentPage(1); setSortBy(newSortBy); }, []); - const handleChangePage = useCallback((newPage: number) => { - setCurrentPage(newPage); - }, []); - // context value const contextValue = useMemo( () => ({ jobItems, jobItemsSortedAndSliced, - isLoading: isLoading && debouncedSearchText.length > 0, + isLoading, totalNumberOfResults, totalNumberOfPages, currentPage, - handleChangePage, sortBy, - handleChangeSort, + handleChangePage, + handleChangeSortBy, }), [ jobItems, jobItemsSortedAndSliced, isLoading, - debouncedSearchText, totalNumberOfResults, totalNumberOfPages, currentPage, - handleChangePage, sortBy, - handleChangeSort, + handleChangePage, + handleChangeSortBy, ] ); @@ -104,14 +97,3 @@ export default function JobItemsContextProvider({ ); } - -export function useJobItemsContext() { - const context = useContext(JobItemsContext); - - if (!context) { - throw new Error( - "JobItemsContext must be used within a JobItemsContextContextProvider" - ); - } - return context; -} diff --git a/rmtdev/final-code/src/contexts/SearchTextContextProvider.tsx b/rmtdev/final-code/src/contexts/SearchTextContextProvider.tsx index c8808b8..63d05d1 100644 --- a/rmtdev/final-code/src/contexts/SearchTextContextProvider.tsx +++ b/rmtdev/final-code/src/contexts/SearchTextContextProvider.tsx @@ -1,63 +1,35 @@ -import React, { - useState, - createContext, - useMemo, - useCallback, - useContext, -} from "react"; +import { createContext, useState } from "react"; import { useDebounce } from "../lib/hooks"; -type SearchTextProviderProps = { - children: React.ReactNode; -}; - type SearchTextContext = { searchText: string; debouncedSearchText: string; - handleChangeSearch: (newSearchText: string) => void; + handleChangeSearchText: (newSearchText: string) => void; }; export const SearchTextContext = createContext(null); export default function SearchTextContextProvider({ children, -}: SearchTextProviderProps) { - // state +}: { + children: React.ReactNode; +}) { const [searchText, setSearchText] = useState(""); const debouncedSearchText = useDebounce(searchText, 250); - // event handlers / actions - const handleChangeSearch = useCallback( - (newSearchText: string) => { - setSearchText(newSearchText); - }, - [setSearchText] - ); - - // context value - const contextValue = useMemo( - () => ({ - searchText, - debouncedSearchText, - handleChangeSearch, - }), - [searchText, debouncedSearchText, handleChangeSearch] - ); + const handleChangeSearchText = (newSearchText: string) => { + setSearchText(newSearchText); + }; return ( - + {children} ); } - -export function useSearchTextContext() { - const context = useContext(SearchTextContext); - - if (!context) { - throw new Error( - "SearchTextContext must be used within a SearchTextProvider" - ); - } - return context; -} diff --git a/rmtdev/final-code/src/lib/hooks.ts b/rmtdev/final-code/src/lib/hooks.ts index 837b046..a753785 100644 --- a/rmtdev/final-code/src/lib/hooks.ts +++ b/rmtdev/final-code/src/lib/hooks.ts @@ -1,75 +1,76 @@ -import { RefObject, useEffect, useState } from "react"; -import { useQueries, useQuery } from "@tanstack/react-query"; +import { useContext, useEffect, useState } from "react"; +import { JobItem, JobItemExpanded } from "./types"; import { BASE_API_URL } from "./constants"; +import { useQueries, useQuery } from "@tanstack/react-query"; import { handleError } from "./utils"; -import { JobItem, JobItemExpanded, JobItemId } from "./types"; - -// ------------------------------ - -export function useOnClickOutside( - // refs can be a single ref or an array of refs - refs: RefObject | RefObject[], - callback: () => void -) { - useEffect(() => { - const handleClick = (e: MouseEvent) => { - if ( - // if clicked outside of the ref elements - Array.isArray(refs) - ? refs.every( - (refItem) => !refItem.current?.contains(e.target as Node) - ) - : !refs.current?.contains(e.target as Node) - ) { - callback(); - } - }; +import { BookmarksContext } from "../contexts/BookmarksContextProvider"; +import { ActiveIdContext } from "../contexts/ActiveIdContextProvider"; +import { SearchTextContext } from "../contexts/SearchTextContextProvider"; +import { JobItemsContext } from "../contexts/JobItemsContextProvider"; - document.addEventListener("click", handleClick); - - return () => { - document.removeEventListener("click", handleClick); - }; - }, [refs, callback]); -} - -// ------------------------------ - -export function useActiveId() { - const [activeJobItemId, setActiveJobItemId] = useState(null); +type JobItemApiResponse = { + public: boolean; + jobItem: JobItemExpanded; +}; - useEffect(() => { - const handleHashChange = () => { - const id = +window.location.hash.slice(1); - setActiveJobItemId(id); - }; - handleHashChange(); // for initial page load +const fetchJobItem = async (id: number): Promise => { + const response = await fetch(`${BASE_API_URL}/${id}`); + // 4xx or 5xx + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.description); + } - window.addEventListener("hashchange", handleHashChange); + const data = await response.json(); + return data; +}; - return () => { - window.removeEventListener("hashchange", handleHashChange); - }; - }, []); +export function useJobItem(id: number | null) { + const { data, isInitialLoading } = useQuery( + ["job-item", id], + () => (id ? fetchJobItem(id) : null), + { + staleTime: 1000 * 60 * 60, + refetchOnWindowFocus: false, + retry: false, + enabled: Boolean(id), + onError: handleError, + } + ); - return activeJobItemId; + return { + jobItem: data?.jobItem, + isLoading: isInitialLoading, + } as const; } -// ------------------------------ - -export function useDebounce(value: T, delay = 500): T { - const [debouncedValue, setDebouncedValue] = useState(value); - - useEffect(() => { - const timerId = setTimeout(() => setDebouncedValue(value), delay); +export function useJobItems(ids: number[]) { + const results = useQueries({ + queries: ids.map((id) => ({ + queryKey: ["job-item", id], + queryFn: () => fetchJobItem(id), + staleTime: 1000 * 60 * 60, + refetchOnWindowFocus: false, + retry: false, + enabled: Boolean(id), + onError: handleError, + })), + }); - return () => clearTimeout(timerId); - }, [value, delay]); + const jobItems = results + .map((result) => result.data?.jobItem) + // .filter((jobItem) => jobItem !== undefined); + // .filter((jobItem) => !!jobItem); + .filter((jobItem) => Boolean(jobItem)) as JobItemExpanded[]; + const isLoading = results.some((result) => result.isLoading); - return debouncedValue; + return { + jobItems, + isLoading, + }; } -// ------------------------------ +// -------------------------------------------------- type JobItemsApiResponse = { public: boolean; @@ -81,87 +82,140 @@ const fetchJobItems = async ( searchText: string ): Promise => { const response = await fetch(`${BASE_API_URL}?search=${searchText}`); + // 4xx or 5xx if (!response.ok) { - // 4xx or 5xx response const errorData = await response.json(); throw new Error(errorData.description); } - return await response.json(); + const data = await response.json(); + return data; }; export function useSearchQuery(searchText: string) { - const { data, isLoading } = useQuery( + const { data, isInitialLoading } = useQuery( ["job-items", searchText], () => fetchJobItems(searchText), { - enabled: Boolean(searchText), - staleTime: 1000 * 20, + staleTime: 1000 * 60 * 60, refetchOnWindowFocus: false, retry: false, + enabled: Boolean(searchText), onError: handleError, } ); - return [data?.jobItems || [], isLoading] as const; + return { + jobItems: data?.jobItems, + isLoading: isInitialLoading, + } as const; } -// ------------------------------ +// -------------------------------------------------- -type JobItemApiResponse = { - public: boolean; - jobItem: JobItemExpanded; -}; +export function useDebounce(value: T, delay = 500): T { + const [debouncedValue, setDebouncedValue] = useState(value); -const fetchJobItem = async (id: number): Promise => { - const response = await fetch(`${BASE_API_URL}/${id}`); - if (!response.ok) { - // 4xx or 5xx response - const errorData = await response.json(); - throw new Error(errorData.description); - } - return await response.json(); -}; + useEffect(() => { + const timerId = setTimeout(() => setDebouncedValue(value), delay); -export function useJobItems(ids: JobItemId[]) { - const results = useQueries({ - queries: ids.map((id) => ({ - queryKey: ["job-item", id], - queryFn: () => fetchJobItem(id), - staleTime: 1000 * 60 * 60 * 24, - refetchOnWindowFocus: false, - retry: false, - enabled: Boolean(id), - onError: handleError, - })), - }); + return () => clearTimeout(timerId); + }, [value, delay]); - const atLeastOneLoading = results.some((result) => result.isLoading); - const jobItems = results - .map((result) => result.data?.jobItem) - // .filter(Boolean); - // .filter((jobItem) => jobItem !== undefined); - // .filter((jobItem) => !!jobItem); - .filter((jobItem) => jobItem !== undefined) as JobItemExpanded[]; - return { jobItems, isLoading: atLeastOneLoading } as const; + return debouncedValue; } -export function useJobItem(id: JobItemId | null) { - const { data, isLoading } = useQuery( - ["job-item", id], - () => (id ? fetchJobItem(id) : undefined), - // () => { - // if (id) { - // return fetchJobItem(id); - // } - // }, - { - staleTime: 1000 * 60 * 60 * 24, - refetchOnWindowFocus: false, - retry: false, - enabled: Boolean(id), - onError: handleError, - } +export function useActiveId() { + const [activeId, setActiveId] = useState(null); + + useEffect(() => { + const handleHashChange = () => { + const id = +window.location.hash.slice(1); + setActiveId(id); + }; + handleHashChange(); + + window.addEventListener("hashchange", handleHashChange); + + return () => { + window.removeEventListener("hashchange", handleHashChange); + }; + }, []); + + return activeId; +} + +export function useLocalStorage( + key: string, + initialValue: T +): [T, React.Dispatch>] { + const [value, setValue] = useState(() => + JSON.parse(localStorage.getItem(key) || JSON.stringify(initialValue)) ); - return [data?.jobItem, isLoading] as const; + useEffect(() => { + localStorage.setItem(key, JSON.stringify(value)); + }, [value, key]); + + return [value, setValue] as const; +} + +export function useOnClickOutside( + refs: React.RefObject[], + handler: () => void +) { + useEffect(() => { + const handleClick = (e: MouseEvent) => { + if (refs.every((ref) => !ref.current?.contains(e.target as Node))) { + handler(); + } + }; + + document.addEventListener("click", handleClick); + + return () => { + document.removeEventListener("click", handleClick); + }; + }, [refs, handler]); +} + +// -------------------------------------------------- + +export function useBookmarksContext() { + const context = useContext(BookmarksContext); + if (!context) { + throw new Error( + "useBookmarksContext must be used within a BookmarksContextProvider" + ); + } + return context; +} + +export function useActiveIdContext() { + const context = useContext(ActiveIdContext); + if (!context) { + throw new Error( + "useActiveIdContext must be used within a ActiveIdContextProvider" + ); + } + return context; +} + +export function useSearchTextContext() { + const context = useContext(SearchTextContext); + if (!context) { + throw new Error( + "useSearchTextContext must be used within a SearchTextContextProvider" + ); + } + return context; +} + +export function useJobItemsContext() { + const context = useContext(JobItemsContext); + if (!context) { + throw new Error( + "useJobItemsContext must be used within a JobItemsContextProvider" + ); + } + return context; } diff --git a/rmtdev/final-code/src/lib/types.ts b/rmtdev/final-code/src/lib/types.ts index d8630dd..2042055 100644 --- a/rmtdev/final-code/src/lib/types.ts +++ b/rmtdev/final-code/src/lib/types.ts @@ -1,29 +1,24 @@ -export type SortBy = "relevant" | "recent"; - export type JobItem = { id: number; - title: string; badgeLetters: string; + title: string; company: string; + date: string; relevanceScore: number; daysAgo: number; - coverImgURL: string; - companyURL: string; - duration: string; - salary: string; - location: string; - description: string; - qualifications: string[]; - reviews: string[]; }; export type JobItemExpanded = JobItem & { description: string; qualifications: string[]; reviews: string[]; + duration: string; location: string; salary: string; - duration: string; + coverImgURL: string; + companyURL: string; }; -export type JobItemId = JobItem["id"]; +export type PageDirection = "next" | "previous"; + +export type SortBy = "relevant" | "recent"; diff --git a/rmtdev/final-code/src/lib/utils.ts b/rmtdev/final-code/src/lib/utils.ts index 382c4f6..0c5766b 100644 --- a/rmtdev/final-code/src/lib/utils.ts +++ b/rmtdev/final-code/src/lib/utils.ts @@ -1,17 +1,14 @@ import toast from "react-hot-toast"; -export const urlContainsHashId = () => { - const id = +window.location.hash.slice(1); - return Boolean(id); -}; - export const handleError = (error: unknown) => { - let message = "An error occurred."; + let message; if (error instanceof Error) { message = error.message; } else if (typeof error === "string") { message = error; + } else { + message = "An error occurred."; } toast.error(message); diff --git a/rmtdev/final-code/src/main.tsx b/rmtdev/final-code/src/main.tsx index e5fb71e..51f977a 100644 --- a/rmtdev/final-code/src/main.tsx +++ b/rmtdev/final-code/src/main.tsx @@ -3,9 +3,9 @@ import ReactDOM from "react-dom/client"; import App from "./components/App.tsx"; import "./index.css"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import BookmarksContextProvider from "./contexts/BookmarksContextProvider.tsx"; import ActiveIdContextProvider from "./contexts/ActiveIdContextProvider.tsx"; import SearchTextContextProvider from "./contexts/SearchTextContextProvider.tsx"; -import BookmarksContextProvider from "./contexts/BookmarksContextProvider.tsx"; import JobItemsContextProvider from "./contexts/JobItemsContextProvider.tsx"; const queryClient = new QueryClient(); @@ -13,15 +13,15 @@ const queryClient = new QueryClient(); ReactDOM.createRoot(document.getElementById("root")!).render( - - - - + + + + - - - - + + + + ); From 594046c782049e1d1b4704327bb31451516eac69 Mon Sep 17 00:00:00 2001 From: bytegrad Date: Tue, 20 Feb 2024 13:03:02 +0100 Subject: [PATCH 34/40] petsoft final code --- petsoft/final-code/.eslintrc.json | 3 + petsoft/final-code/.gitignore | 37 + petsoft/final-code/README.md | 36 + petsoft/final-code/components.json | 17 + petsoft/final-code/next.config.mjs | 17 + petsoft/final-code/package-lock.json | 5980 +++++++++++++++++ petsoft/final-code/package.json | 54 + petsoft/final-code/postcss.config.js | 6 + petsoft/final-code/prisma/schema.prisma | 50 + petsoft/final-code/prisma/seed.ts | 61 + petsoft/final-code/public/logo.svg | 8 + petsoft/final-code/src/actions/actions.ts | 238 + .../src/app/(app)/app/account/page.tsx | 20 + .../src/app/(app)/app/dashboard/page.tsx | 41 + .../final-code/src/app/(app)/app/layout.tsx | 34 + petsoft/final-code/src/app/(auth)/layout.tsx | 10 + .../final-code/src/app/(auth)/login/page.tsx | 20 + .../src/app/(auth)/payment/page.tsx | 60 + .../final-code/src/app/(auth)/signup/page.tsx | 20 + .../final-code/src/app/(marketing)/page.tsx | 37 + .../src/app/api/auth/[...nextauth]/route.ts | 1 + .../final-code/src/app/api/stripe/route.ts | 40 + petsoft/final-code/src/app/icon.svg | 8 + petsoft/final-code/src/app/layout.tsx | 27 + .../final-code/src/components/app-footer.tsx | 9 + .../final-code/src/components/app-header.tsx | 47 + .../src/components/auth-form-btn.tsx | 19 + .../final-code/src/components/auth-form.tsx | 45 + .../src/components/background-pattern.tsx | 3 + .../final-code/src/components/branding.tsx | 12 + .../src/components/content-block.tsx | 22 + petsoft/final-code/src/components/h1.tsx | 14 + petsoft/final-code/src/components/logo.tsx | 11 + .../final-code/src/components/pet-button.tsx | 69 + .../final-code/src/components/pet-details.tsx | 92 + .../src/components/pet-form-btn.tsx | 13 + .../final-code/src/components/pet-form.tsx | 103 + .../final-code/src/components/pet-list.tsx | 43 + .../final-code/src/components/search-form.tsx | 19 + .../src/components/sign-out-btn.tsx | 22 + petsoft/final-code/src/components/stats.tsx | 14 + .../final-code/src/components/ui/button.tsx | 54 + .../final-code/src/components/ui/dialog.tsx | 122 + .../final-code/src/components/ui/input.tsx | 25 + .../final-code/src/components/ui/label.tsx | 26 + .../final-code/src/components/ui/sonner.tsx | 31 + .../final-code/src/components/ui/textarea.tsx | 24 + .../src/contexts/pet-context-provider.tsx | 104 + .../src/contexts/search-context-provider.tsx | 39 + petsoft/final-code/src/lib/auth-edge.ts | 82 + petsoft/final-code/src/lib/auth-no-edge.ts | 50 + petsoft/final-code/src/lib/constants.ts | 2 + petsoft/final-code/src/lib/db.ts | 15 + petsoft/final-code/src/lib/hooks.ts | 25 + petsoft/final-code/src/lib/next-auth.d.ts | 22 + petsoft/final-code/src/lib/server-utils.ts | 42 + petsoft/final-code/src/lib/types.ts | 6 + petsoft/final-code/src/lib/utils.ts | 10 + petsoft/final-code/src/lib/validations.ts | 33 + petsoft/final-code/src/middleware.ts | 8 + petsoft/final-code/src/styles/globals.css | 3 + petsoft/final-code/tailwind.config.ts | 29 + petsoft/final-code/tsconfig.json | 26 + 63 files changed, 8160 insertions(+) create mode 100644 petsoft/final-code/.eslintrc.json create mode 100644 petsoft/final-code/.gitignore create mode 100644 petsoft/final-code/README.md create mode 100644 petsoft/final-code/components.json create mode 100644 petsoft/final-code/next.config.mjs create mode 100644 petsoft/final-code/package-lock.json create mode 100644 petsoft/final-code/package.json create mode 100644 petsoft/final-code/postcss.config.js create mode 100644 petsoft/final-code/prisma/schema.prisma create mode 100644 petsoft/final-code/prisma/seed.ts create mode 100644 petsoft/final-code/public/logo.svg create mode 100644 petsoft/final-code/src/actions/actions.ts create mode 100644 petsoft/final-code/src/app/(app)/app/account/page.tsx create mode 100644 petsoft/final-code/src/app/(app)/app/dashboard/page.tsx create mode 100644 petsoft/final-code/src/app/(app)/app/layout.tsx create mode 100644 petsoft/final-code/src/app/(auth)/layout.tsx create mode 100644 petsoft/final-code/src/app/(auth)/login/page.tsx create mode 100644 petsoft/final-code/src/app/(auth)/payment/page.tsx create mode 100644 petsoft/final-code/src/app/(auth)/signup/page.tsx create mode 100644 petsoft/final-code/src/app/(marketing)/page.tsx create mode 100644 petsoft/final-code/src/app/api/auth/[...nextauth]/route.ts create mode 100644 petsoft/final-code/src/app/api/stripe/route.ts create mode 100644 petsoft/final-code/src/app/icon.svg create mode 100644 petsoft/final-code/src/app/layout.tsx create mode 100644 petsoft/final-code/src/components/app-footer.tsx create mode 100644 petsoft/final-code/src/components/app-header.tsx create mode 100644 petsoft/final-code/src/components/auth-form-btn.tsx create mode 100644 petsoft/final-code/src/components/auth-form.tsx create mode 100644 petsoft/final-code/src/components/background-pattern.tsx create mode 100644 petsoft/final-code/src/components/branding.tsx create mode 100644 petsoft/final-code/src/components/content-block.tsx create mode 100644 petsoft/final-code/src/components/h1.tsx create mode 100644 petsoft/final-code/src/components/logo.tsx create mode 100644 petsoft/final-code/src/components/pet-button.tsx create mode 100644 petsoft/final-code/src/components/pet-details.tsx create mode 100644 petsoft/final-code/src/components/pet-form-btn.tsx create mode 100644 petsoft/final-code/src/components/pet-form.tsx create mode 100644 petsoft/final-code/src/components/pet-list.tsx create mode 100644 petsoft/final-code/src/components/search-form.tsx create mode 100644 petsoft/final-code/src/components/sign-out-btn.tsx create mode 100644 petsoft/final-code/src/components/stats.tsx create mode 100644 petsoft/final-code/src/components/ui/button.tsx create mode 100644 petsoft/final-code/src/components/ui/dialog.tsx create mode 100644 petsoft/final-code/src/components/ui/input.tsx create mode 100644 petsoft/final-code/src/components/ui/label.tsx create mode 100644 petsoft/final-code/src/components/ui/sonner.tsx create mode 100644 petsoft/final-code/src/components/ui/textarea.tsx create mode 100644 petsoft/final-code/src/contexts/pet-context-provider.tsx create mode 100644 petsoft/final-code/src/contexts/search-context-provider.tsx create mode 100644 petsoft/final-code/src/lib/auth-edge.ts create mode 100644 petsoft/final-code/src/lib/auth-no-edge.ts create mode 100644 petsoft/final-code/src/lib/constants.ts create mode 100644 petsoft/final-code/src/lib/db.ts create mode 100644 petsoft/final-code/src/lib/hooks.ts create mode 100644 petsoft/final-code/src/lib/next-auth.d.ts create mode 100644 petsoft/final-code/src/lib/server-utils.ts create mode 100644 petsoft/final-code/src/lib/types.ts create mode 100644 petsoft/final-code/src/lib/utils.ts create mode 100644 petsoft/final-code/src/lib/validations.ts create mode 100644 petsoft/final-code/src/middleware.ts create mode 100644 petsoft/final-code/src/styles/globals.css create mode 100644 petsoft/final-code/tailwind.config.ts create mode 100644 petsoft/final-code/tsconfig.json diff --git a/petsoft/final-code/.eslintrc.json b/petsoft/final-code/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/petsoft/final-code/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/petsoft/final-code/.gitignore b/petsoft/final-code/.gitignore new file mode 100644 index 0000000..00bba9b --- /dev/null +++ b/petsoft/final-code/.gitignore @@ -0,0 +1,37 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local +.env + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/petsoft/final-code/README.md b/petsoft/final-code/README.md new file mode 100644 index 0000000..c403366 --- /dev/null +++ b/petsoft/final-code/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/petsoft/final-code/components.json b/petsoft/final-code/components.json new file mode 100644 index 0000000..e3766c2 --- /dev/null +++ b/petsoft/final-code/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/styles/globals.css", + "baseColor": "zinc", + "cssVariables": false, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} \ No newline at end of file diff --git a/petsoft/final-code/next.config.mjs b/petsoft/final-code/next.config.mjs new file mode 100644 index 0000000..f76d05c --- /dev/null +++ b/petsoft/final-code/next.config.mjs @@ -0,0 +1,17 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "bytegrad.com", + }, + { + protocol: "https", + hostname: "images.unsplash.com", + }, + ], + }, +}; + +export default nextConfig; diff --git a/petsoft/final-code/package-lock.json b/petsoft/final-code/package-lock.json new file mode 100644 index 0000000..52135cf --- /dev/null +++ b/petsoft/final-code/package-lock.json @@ -0,0 +1,5980 @@ +{ + "name": "petsoft-project", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "petsoft-project", + "version": "0.1.0", + "dependencies": { + "@hookform/resolvers": "^3.3.2", + "@prisma/client": "^5.8.1", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-slot": "^1.0.2", + "bcrypt": "^5.1.1", + "bcryptjs": "^2.4.3", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "next": "14.1.0", + "next-auth": "^5.0.0-beta.4", + "next-themes": "^0.2.1", + "react": "^18", + "react-dom": "^18", + "react-hook-form": "^7.47.0", + "server-only": "^0.0.1", + "sonner": "^1.3.1", + "stripe": "^14.12.0", + "tailwind-merge": "^2.2.1", + "tailwindcss-animate": "^1.0.7", + "zod": "^3.22.4" + }, + "devDependencies": { + "@types/bcrypt": "^5.0.2", + "@types/bcryptjs": "^2.4.6", + "@types/node": "^20.11.7", + "@types/react": "^18", + "@types/react-dom": "^18", + "autoprefixer": "^10.0.1", + "eslint": "^8", + "eslint-config-next": "14.1.0", + "postcss": "^8", + "prisma": "^5.8.1", + "tailwindcss": "^3.3.0", + "ts-node": "^10.9.1", + "typescript": "^5.3.3" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@auth/core": { + "version": "0.18.4", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.18.4.tgz", + "integrity": "sha512-GsNhsP1xE/3FoNS3dVkPjqRljLNJ4iyL2OLv3klQGNvw3bMpROFcK4lqhx7+pPHiamnVaYt2vg1xbB+lsNaevg==", + "dependencies": { + "@panva/hkdf": "^1.1.1", + "cookie": "0.6.0", + "jose": "^5.1.0", + "oauth4webapi": "^2.3.0", + "preact": "10.11.3", + "preact-render-to-string": "5.2.3" + }, + "peerDependencies": { + "nodemailer": "^6.8.0" + }, + "peerDependenciesMeta": { + "nodemailer": { + "optional": true + } + } + }, + "node_modules/@babel/runtime": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "devOptional": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@hookform/resolvers": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.2.tgz", + "integrity": "sha512-Tw+GGPnBp+5DOsSg4ek3LCPgkBOuOgS5DsDV7qsWNH9LZc433kgsWICjlsh2J9p04H2K66hsXPPb9qn9ILdUtA==", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@next/env": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", + "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.1.0.tgz", + "integrity": "sha512-x4FavbNEeXx/baD/zC/SdrvkjSby8nBn8KcCREqk6UuwvwoAPZmaV8TFCAuo/cpovBRTIY67mHhe86MQQm/68Q==", + "dev": true, + "dependencies": { + "glob": "10.3.10" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", + "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", + "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", + "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", + "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", + "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", + "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", + "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", + "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@panva/hkdf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz", + "integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@prisma/client": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.8.1.tgz", + "integrity": "sha512-xQtMPfbIwLlbm0VVIVQY2yqQVOxPwRQhvIp7Z3m2900g1bu/zRHKhYZJQWELqmjl6d8YwBy0K2NvMqh47v1ubw==", + "hasInstallScript": true, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.8.1.tgz", + "integrity": "sha512-tjuw7eA0Us3T42jx9AmAgL58rzwzpFGYc3R7Y4Ip75EBYrKMBA1YihuWMcBC92ILmjlQ/u3p8VxcIE0hr+fZfg==", + "devOptional": true + }, + "node_modules/@prisma/engines": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.8.1.tgz", + "integrity": "sha512-TJgYLRrZr56uhqcXO4GmP5be+zjCIHtLDK20Cnfg+o9d905hsN065QOL+3Z0zQAy6YD31Ol4u2kzSfRmbJv/uA==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/debug": "5.8.1", + "@prisma/engines-version": "5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2", + "@prisma/fetch-engine": "5.8.1", + "@prisma/get-platform": "5.8.1" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2.tgz", + "integrity": "sha512-f5C3JM3l9yhGr3cr4FMqWloFaSCpNpMi58Om22rjD2DOz3owci2mFdFXMgnAGazFPKrCbbEhcxdsRfspEYRoFQ==", + "devOptional": true + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.8.1.tgz", + "integrity": "sha512-+bgjjoSFa6uYEbAPlklfoVSStOEfcpheOjoBoNsNNSQdSzcwE2nM4Q0prun0+P8/0sCHo18JZ9xqa8gObvgOUw==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.8.1", + "@prisma/engines-version": "5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2", + "@prisma/get-platform": "5.8.1" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.8.1.tgz", + "integrity": "sha512-wnA+6HTFcY+tkykMokix9GiAkaauPC5W/gg0O5JB0J8tCTNWrqpnQ7AsaGRfkYUbeOIioh6woDjQrGTTRf1Zag==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.8.1" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", + "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", + "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x" + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz", + "integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.7.1.tgz", + "integrity": "sha512-irBNt5094vHloql4QzY8RdeI8Tns2kGsaiJ/m6jENWx9xCz/m/F4gKQ1dAailFmpL0Id9tgWLqZbTUO4SINM/Q==", + "dev": true + }, + "node_modules/@swc/helpers": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", + "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "devOptional": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "devOptional": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "devOptional": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "devOptional": true + }, + "node_modules/@types/bcrypt": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.7.tgz", + "integrity": "sha512-GPmeN1C3XAyV5uybAf4cMLWT9fDWcmQhZVtMFu7OR32WjrqGG+Wnk2V1d0bmtUyE/Zy1QJ9BxyiTih9z8Oks8A==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", + "devOptional": true + }, + "node_modules/@types/react": { + "version": "18.2.48", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", + "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", + "devOptional": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", + "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "devOptional": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "devOptional": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.19.1.tgz", + "integrity": "sha512-WEfX22ziAh6pRE9jnbkkLGp/4RhTpffr2ZK5bJ18M8mIfA8A+k97U9ZyaXCEJRlmMHh7R9MJZWXp/r73DzINVQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.19.1", + "@typescript-eslint/types": "6.19.1", + "@typescript-eslint/typescript-estree": "6.19.1", + "@typescript-eslint/visitor-keys": "6.19.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.1.tgz", + "integrity": "sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.1", + "@typescript-eslint/visitor-keys": "6.19.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.1.tgz", + "integrity": "sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.1.tgz", + "integrity": "sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.1", + "@typescript-eslint/visitor-keys": "6.19.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.1.tgz", + "integrity": "sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "devOptional": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "devOptional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/aria-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", + "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", + "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "node_modules/asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.17", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", + "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.22.2", + "caniuse-lite": "^1.0.30001578", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", + "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", + "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", + "dependencies": { + "clsx": "2.0.0" + }, + "funding": { + "url": "https://joebell.co.uk" + } + }, + "node_modules/class-variance-authority/node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/clsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", + "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "devOptional": true + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "devOptional": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.643", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.643.tgz", + "integrity": "sha512-QHscvvS7gt155PtoRC0dR2ilhL8E9LHhfTQEq1uD5AL0524rBLAwpAREFH06f87/e45B9XkR6Ki5dbhbCsVEIg==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", + "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", + "dev": true, + "dependencies": { + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.1", + "es-set-tostringtag": "^2.0.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.0.1" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-next": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.1.0.tgz", + "integrity": "sha512-SBX2ed7DoRFXC6CQSLc/SbLY9Ut6HxNB2wPTcoIWjUMd7aF7O/SIE7111L8FdZ9TXsNV4pulUDnfthpyPtbFUg==", + "dev": true, + "dependencies": { + "@next/eslint-plugin-next": "14.1.0", + "@rushstack/eslint-patch": "^1.3.3", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz", + "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.12.0", + "eslint-module-utils": "^2.7.4", + "fast-glob": "^3.3.1", + "get-tsconfig": "^4.5.0", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", + "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.23.2", + "aria-query": "^5.3.0", + "array-includes": "^3.1.7", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "=4.7.0", + "axobject-query": "^3.2.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "es-iterator-helpers": "^1.0.15", + "hasown": "^2.0.0", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.33.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", + "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.12", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.8" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/jose": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.0.tgz", + "integrity": "sha512-oW3PCnvyrcm1HMvGTzqjxxfnEs9EoFOFWi2HsEGhlFVOXxTE3K9GKWVMFoFw06yPUqwpvEWic1BmtUZBI/tIjw==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/next": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", + "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", + "dependencies": { + "@next/env": "14.1.0", + "@swc/helpers": "0.5.2", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.1.0", + "@next/swc-darwin-x64": "14.1.0", + "@next/swc-linux-arm64-gnu": "14.1.0", + "@next/swc-linux-arm64-musl": "14.1.0", + "@next/swc-linux-x64-gnu": "14.1.0", + "@next/swc-linux-x64-musl": "14.1.0", + "@next/swc-win32-arm64-msvc": "14.1.0", + "@next/swc-win32-ia32-msvc": "14.1.0", + "@next/swc-win32-x64-msvc": "14.1.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-auth": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.4.tgz", + "integrity": "sha512-vgocjvwPA8gxd/zrIP/vr9lJ/HeNe+C56lPP1D3sdyenHt8KncQV6ro7q0xCsDp1fcOKx7WAWVZH5o8aMxDzgw==", + "dependencies": { + "@auth/core": "0.18.4" + }, + "peerDependencies": { + "next": "^14", + "nodemailer": "^6.6.5", + "react": "^18.2.0" + }, + "peerDependenciesMeta": { + "nodemailer": { + "optional": true + } + } + }, + "node_modules/next-themes": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz", + "integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==", + "peerDependencies": { + "next": "*", + "react": "*", + "react-dom": "*" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/oauth4webapi": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.8.1.tgz", + "integrity": "sha512-Jm1Z6eUumtevQWxMllSw+4diHOcFyxuc3KAXoyh4fbpHndbXRbviyrLoCn8htEdHYZM/MIOVbeWjDk86BxVF+A==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/object.hasown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "engines": { + "node": ">=14" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/preact": { + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", + "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz", + "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, + "node_modules/prisma": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.8.1.tgz", + "integrity": "sha512-N6CpjzECnUHZ5beeYpDzkt2rYpEdAeqXX2dweu6BoQaeYkNZrC/WJHM+5MO/uidFHTak8QhkPKBWck1o/4MD4A==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "5.8.1" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-hook-form": { + "version": "7.47.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.47.0.tgz", + "integrity": "sha512-F/TroLjTICipmHeFlMrLtNLceO2xr1jU3CyiNla5zdwsGUGu2UOxxR4UyJgLlhMwLW/Wzp4cpJ7CPfgJIeKdSg==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz", + "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", + "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==" + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "dependencies": { + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sonner": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.3.1.tgz", + "integrity": "sha512-+rOAO56b2eI3q5BtgljERSn2umRk63KFIvgb2ohbZ5X+Eb5u+a/7/0ZgswYqgBMg8dyl7n6OXd9KasA8QF9ToA==", + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stripe": { + "version": "14.12.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-14.12.0.tgz", + "integrity": "sha512-3lze4QdO8fM6nh1vaRsnFpaoA0WV7DerYtjEprOwcwyfdF5LdesfQNZiT22LO1dFiYobBifDzEn8V51MXHPrPQ==", + "dependencies": { + "@types/node": ">=8.1.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.1.tgz", + "integrity": "sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==", + "dependencies": { + "@babel/runtime": "^7.23.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "devOptional": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.1.tgz", + "integrity": "sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "devOptional": true + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/petsoft/final-code/package.json b/petsoft/final-code/package.json new file mode 100644 index 0000000..d3b8a47 --- /dev/null +++ b/petsoft/final-code/package.json @@ -0,0 +1,54 @@ +{ + "name": "petsoft-project", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "postinstall": "prisma generate" + }, + "prisma": { + "seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts" + }, + "dependencies": { + "@hookform/resolvers": "^3.3.2", + "@prisma/client": "^5.8.1", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-slot": "^1.0.2", + "bcrypt": "^5.1.1", + "bcryptjs": "^2.4.3", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "next": "14.1.0", + "next-auth": "^5.0.0-beta.4", + "next-themes": "^0.2.1", + "react": "^18", + "react-dom": "^18", + "react-hook-form": "^7.47.0", + "server-only": "^0.0.1", + "sonner": "^1.3.1", + "stripe": "^14.12.0", + "tailwind-merge": "^2.2.1", + "tailwindcss-animate": "^1.0.7", + "zod": "^3.22.4" + }, + "devDependencies": { + "@types/bcrypt": "^5.0.2", + "@types/bcryptjs": "^2.4.6", + "@types/node": "^20.11.7", + "@types/react": "^18", + "@types/react-dom": "^18", + "autoprefixer": "^10.0.1", + "eslint": "^8", + "eslint-config-next": "14.1.0", + "postcss": "^8", + "prisma": "^5.8.1", + "tailwindcss": "^3.3.0", + "ts-node": "^10.9.1", + "typescript": "^5.3.3" + } +} diff --git a/petsoft/final-code/postcss.config.js b/petsoft/final-code/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/petsoft/final-code/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/petsoft/final-code/prisma/schema.prisma b/petsoft/final-code/prisma/schema.prisma new file mode 100644 index 0000000..7d2421b --- /dev/null +++ b/petsoft/final-code/prisma/schema.prisma @@ -0,0 +1,50 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("POSTGRES_PRISMA_URL") // uses connection pooling + directUrl = env("POSTGRES_URL_NON_POOLING") // uses a direct connection +} + +model User { + id String @id @default(cuid()) + email String @unique + hashedPassword String + hasAccess Boolean @default(false) + pets Pet[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +model Pet { + id String @id @default(cuid()) + name String + ownerName String + imageUrl String + age Int + notes String + user User @relation(fields: [userId], references: [id]) + userId String + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +// Using same database for 2 projects +model EventoEvent { + id Int @id @default(autoincrement()) + name String + slug String @unique + city String + location String + date DateTime + organizerName String + imageUrl String + description String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} diff --git a/petsoft/final-code/prisma/seed.ts b/petsoft/final-code/prisma/seed.ts new file mode 100644 index 0000000..9373470 --- /dev/null +++ b/petsoft/final-code/prisma/seed.ts @@ -0,0 +1,61 @@ +import { Prisma, PrismaClient } from "@prisma/client"; +import bcrypt from "bcrypt"; + +const prisma = new PrismaClient(); + +const userData: Prisma.UserCreateInput = { + email: "example@gmail.com", + hashedPassword: "", + pets: { + create: [ + { + name: "Benjamin", + ownerName: "John Doe", + imageUrl: + "https://images.unsplash.com/photo-1517849845537-4d257902454a?auto=format&fit=crop&q=100&w=1935&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + age: 2, + notes: + "Doesn't like to be touched on the belly. Plays well with other dogs.", + }, + { + name: "Richard", + ownerName: "Josephine Dane", + imageUrl: + "https://images.unsplash.com/photo-1583337130417-3346a1be7dee?auto=format&fit=crop&q=100&w=1964&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + age: 5, + notes: "Needs medication twice a day.", + }, + { + name: "Anna", + ownerName: "Frank Doe", + imageUrl: + "https://images.unsplash.com/photo-1537151625747-768eb6cf92b2?auto=format&fit=crop&q=100&w=1970&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + age: 4, + notes: "Allergic to chicken.", + }, + ], + }, +}; + +async function main() { + console.log(`Start seeding ...`); + + const hashedPassword = await bcrypt.hash("example", 10); + userData.hashedPassword = hashedPassword; + + await prisma.user.create({ + data: userData, + }); + + console.log(`Seeding finished.`); +} + +main() + .then(async () => { + await prisma.$disconnect(); + }) + .catch(async (e) => { + console.error(e); + await prisma.$disconnect(); + process.exit(1); + }); diff --git a/petsoft/final-code/public/logo.svg b/petsoft/final-code/public/logo.svg new file mode 100644 index 0000000..75103c8 --- /dev/null +++ b/petsoft/final-code/public/logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/petsoft/final-code/src/actions/actions.ts b/petsoft/final-code/src/actions/actions.ts new file mode 100644 index 0000000..68116f8 --- /dev/null +++ b/petsoft/final-code/src/actions/actions.ts @@ -0,0 +1,238 @@ +"use server"; + +import { auth, signIn, signOut } from "@/lib/auth-no-edge"; +import prisma from "@/lib/db"; +import { sleep } from "@/lib/utils"; +import { authSchema, petFormSchema, petIdSchema } from "@/lib/validations"; +import { revalidatePath } from "next/cache"; +import bcrypt from "bcryptjs"; +import { redirect } from "next/navigation"; +import { checkAuth, getPetById } from "@/lib/server-utils"; +import { Prisma } from "@prisma/client"; +import { AuthError } from "next-auth"; + +const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY); + +// --- user actions --- + +export async function logIn(prevState: unknown, formData: unknown) { + if (!(formData instanceof FormData)) { + return { + message: "Invalid form data.", + }; + } + + try { + await signIn("credentials", formData); + } catch (error) { + if (error instanceof AuthError) { + switch (error.type) { + case "CredentialsSignin": { + return { + message: "Invalid credentials.", + }; + } + default: { + return { + message: "Error. Could not sign in.", + }; + } + } + } + + throw error; // nextjs redirects throws error, so we need to rethrow it + } +} + +export async function signUp(prevState: unknown, formData: unknown) { + // check if formData is a FormData type + if (!(formData instanceof FormData)) { + return { + message: "Invalid form data.", + }; + } + + // convert formData to a plain object + const formDataEntries = Object.fromEntries(formData.entries()); + + // validation + const validatedFormData = authSchema.safeParse(formDataEntries); + if (!validatedFormData.success) { + return { + message: "Invalid form data.", + }; + } + + const { email, password } = validatedFormData.data; + const hashedPassword = await bcrypt.hash(password, 10); + try { + await prisma.user.create({ + data: { + email, + hashedPassword, + }, + }); + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + if (error.code === "P2002") { + return { + message: "Email already exists.", + }; + } + } + + return { + message: "Could not create user.", + }; + } + + await signIn("credentials", formData); +} + +export async function logOut() { + await signOut({ redirectTo: "/" }); +} + +// --- pet actions --- + +export async function addPet(pet: unknown) { + const session = await checkAuth(); + + const validatedPet = petFormSchema.safeParse(pet); + if (!validatedPet.success) { + return { + message: "Invalid pet data.", + }; + } + + try { + await prisma.pet.create({ + data: { + ...validatedPet.data, + user: { + connect: { + id: session.user.id, + }, + }, + }, + }); + } catch (error) { + console.log(error); + return { + message: "Could not add pet.", + }; + } + + revalidatePath("/app", "layout"); +} + +export async function editPet(petId: unknown, newPetData: unknown) { + // authentication check + const session = await checkAuth(); + + // validation + const validatedPetId = petIdSchema.safeParse(petId); + const validatedPet = petFormSchema.safeParse(newPetData); + + if (!validatedPetId.success || !validatedPet.success) { + return { + message: "Invalid pet data.", + }; + } + + // authorization check + const pet = await getPetById(validatedPetId.data); + if (!pet) { + return { + message: "Pet not found.", + }; + } + if (pet.userId !== session.user.id) { + return { + message: "Not authorized.", + }; + } + + // database mutation + try { + await prisma.pet.update({ + where: { + id: validatedPetId.data, + }, + data: validatedPet.data, + }); + } catch (error) { + return { + message: "Could not edit pet.", + }; + } + + revalidatePath("/app", "layout"); +} + +export async function deletePet(petId: unknown) { + // authentication check + const session = await checkAuth(); + + // validation + const validatedPetId = petIdSchema.safeParse(petId); + if (!validatedPetId.success) { + return { + message: "Invalid pet data.", + }; + } + + // authorization check + const pet = await getPetById(validatedPetId.data); + if (!pet) { + return { + message: "Pet not found.", + }; + } + if (pet.userId !== session.user.id) { + return { + message: "Not authorized.", + }; + } + + // database mutation + try { + await prisma.pet.delete({ + where: { + id: validatedPetId.data, + }, + }); + } catch (error) { + return { + message: "Could not delete pet.", + }; + } + + revalidatePath("/app", "layout"); +} + +// --- payment actions --- + +export async function createCheckoutSession() { + // authentication check + const session = await checkAuth(); + + console.log(session.user.email); + + // create checkout session + const checkoutSession = await stripe.checkout.sessions.create({ + customer_email: session.user.email, + line_items: [ + { + price: "price_1OfpJ7FIW685mC8GCahpbCed", + quantity: 1, + }, + ], + mode: "payment", + success_url: `${process.env.CANONICAL_URL}/payment?success=true`, + cancel_url: `${process.env.CANONICAL_URL}/payment?cancelled=true`, + }); + + // redirect user + redirect(checkoutSession.url); +} diff --git a/petsoft/final-code/src/app/(app)/app/account/page.tsx b/petsoft/final-code/src/app/(app)/app/account/page.tsx new file mode 100644 index 0000000..c5f3916 --- /dev/null +++ b/petsoft/final-code/src/app/(app)/app/account/page.tsx @@ -0,0 +1,20 @@ +import ContentBlock from "@/components/content-block"; +import H1 from "@/components/h1"; +import SignOutBtn from "@/components/sign-out-btn"; +import { checkAuth } from "@/lib/server-utils"; + +export default async function Page() { + const session = await checkAuth(); + + return ( +
    +

    Your Account

    + + +

    Logged in as {session.user.email}

    + + +
    +
    + ); +} diff --git a/petsoft/final-code/src/app/(app)/app/dashboard/page.tsx b/petsoft/final-code/src/app/(app)/app/dashboard/page.tsx new file mode 100644 index 0000000..0d6649d --- /dev/null +++ b/petsoft/final-code/src/app/(app)/app/dashboard/page.tsx @@ -0,0 +1,41 @@ +import Branding from "@/components/branding"; +import ContentBlock from "@/components/content-block"; +import PetButton from "@/components/pet-button"; +import PetDetails from "@/components/pet-details"; +import PetList from "@/components/pet-list"; +import SearchForm from "@/components/search-form"; +import Stats from "@/components/stats"; + +export default async function Page() { + return ( +
    +
    + + + +
    + +
    +
    + +
    + +
    + + + +
    + +
    +
    +
    + +
    + + + +
    +
    +
    + ); +} diff --git a/petsoft/final-code/src/app/(app)/app/layout.tsx b/petsoft/final-code/src/app/(app)/app/layout.tsx new file mode 100644 index 0000000..c73e03d --- /dev/null +++ b/petsoft/final-code/src/app/(app)/app/layout.tsx @@ -0,0 +1,34 @@ +import AppFooter from "@/components/app-footer"; +import AppHeader from "@/components/app-header"; +import BackgroundPattern from "@/components/background-pattern"; +import { Toaster } from "@/components/ui/sonner"; +import PetContextProvider from "@/contexts/pet-context-provider"; +import SearchContextProvider from "@/contexts/search-context-provider"; +import { checkAuth, getPetsByUserId } from "@/lib/server-utils"; + +export default async function Layout({ + children, +}: { + children: React.ReactNode; +}) { + const session = await checkAuth(); + const pets = await getPetsByUserId(session.user.id); + + return ( + <> + + +
    + + + + {children} + + + +
    + + + + ); +} diff --git a/petsoft/final-code/src/app/(auth)/layout.tsx b/petsoft/final-code/src/app/(auth)/layout.tsx new file mode 100644 index 0000000..e64a87a --- /dev/null +++ b/petsoft/final-code/src/app/(auth)/layout.tsx @@ -0,0 +1,10 @@ +import Logo from "@/components/logo"; + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( +
    + + {children} +
    + ); +} diff --git a/petsoft/final-code/src/app/(auth)/login/page.tsx b/petsoft/final-code/src/app/(auth)/login/page.tsx new file mode 100644 index 0000000..a609265 --- /dev/null +++ b/petsoft/final-code/src/app/(auth)/login/page.tsx @@ -0,0 +1,20 @@ +import AuthForm from "@/components/auth-form"; +import H1 from "@/components/h1"; +import Link from "next/link"; + +export default function Page() { + return ( +
    +

    Log In

    + + + +

    + No account yet?{" "} + + Sign up + +

    +
    + ); +} diff --git a/petsoft/final-code/src/app/(auth)/payment/page.tsx b/petsoft/final-code/src/app/(auth)/payment/page.tsx new file mode 100644 index 0000000..62beb9a --- /dev/null +++ b/petsoft/final-code/src/app/(auth)/payment/page.tsx @@ -0,0 +1,60 @@ +"use client"; + +import { createCheckoutSession } from "@/actions/actions"; +import H1 from "@/components/h1"; +import { Button } from "@/components/ui/button"; +import { useSession } from "next-auth/react"; +import { useRouter } from "next/navigation"; +import { useTransition } from "react"; + +export default function Page({ + searchParams, +}: { + searchParams: { [key: string]: string | string[] | undefined }; +}) { + const [isPending, startTransition] = useTransition(); + const { data: session, update, status } = useSession(); + const router = useRouter(); + + return ( +
    +

    PetSoft access requires payment

    + + {searchParams.success && ( + + )} + + {!searchParams.success && ( + + )} + + {searchParams.success && ( +

    + Payment successful! You now have lifetime access to PetSoft. +

    + )} + {searchParams.cancelled && ( +

    + Payment cancelled. You can try again. +

    + )} +
    + ); +} diff --git a/petsoft/final-code/src/app/(auth)/signup/page.tsx b/petsoft/final-code/src/app/(auth)/signup/page.tsx new file mode 100644 index 0000000..516046c --- /dev/null +++ b/petsoft/final-code/src/app/(auth)/signup/page.tsx @@ -0,0 +1,20 @@ +import AuthForm from "@/components/auth-form"; +import H1 from "@/components/h1"; +import Link from "next/link"; + +export default function Page() { + return ( +
    +

    Sign Up

    + + + +

    + Already have an account?{" "} + + Log in + +

    +
    + ); +} diff --git a/petsoft/final-code/src/app/(marketing)/page.tsx b/petsoft/final-code/src/app/(marketing)/page.tsx new file mode 100644 index 0000000..b3eb6a8 --- /dev/null +++ b/petsoft/final-code/src/app/(marketing)/page.tsx @@ -0,0 +1,37 @@ +import Logo from "@/components/logo"; +import { Button } from "@/components/ui/button"; +import Image from "next/image"; +import Link from "next/link"; + +export default function Home() { + return ( +
    + Preview of PetSoft + +
    + +

    + Manage your pet daycare with + ease +

    +

    + Use PetSoft to easily keep track of pets under your care. Get lifetime + access for $299. +

    +
    + + +
    +
    +
    + ); +} diff --git a/petsoft/final-code/src/app/api/auth/[...nextauth]/route.ts b/petsoft/final-code/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..6ae9a0e --- /dev/null +++ b/petsoft/final-code/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1 @@ +export { GET, POST } from "@/lib/auth-no-edge"; diff --git a/petsoft/final-code/src/app/api/stripe/route.ts b/petsoft/final-code/src/app/api/stripe/route.ts new file mode 100644 index 0000000..dca3aec --- /dev/null +++ b/petsoft/final-code/src/app/api/stripe/route.ts @@ -0,0 +1,40 @@ +import prisma from "@/lib/db"; + +const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY); + +export async function POST(request: Request) { + const body = await request.text(); + const signature = request.headers.get("stripe-signature"); + + // verify webhook came from Stripe + let event; + try { + event = stripe.webhooks.constructEvent( + body, + signature, + process.env.STRIPE_WEBHOOK_SECRET + ); + } catch (error) { + console.log("Webhook verification failed", error); + return Response.json(null, { status: 400 }); + } + + // fulfill order + switch (event.type) { + case "checkout.session.completed": + await prisma.user.update({ + where: { + email: event.data.object.customer_email, + }, + data: { + hasAccess: true, + }, + }); + break; + default: + console.log(`Unhandled event type ${event.type}`); + } + + // return 200 OK + return Response.json(null, { status: 200 }); +} diff --git a/petsoft/final-code/src/app/icon.svg b/petsoft/final-code/src/app/icon.svg new file mode 100644 index 0000000..75103c8 --- /dev/null +++ b/petsoft/final-code/src/app/icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/petsoft/final-code/src/app/layout.tsx b/petsoft/final-code/src/app/layout.tsx new file mode 100644 index 0000000..d6af00e --- /dev/null +++ b/petsoft/final-code/src/app/layout.tsx @@ -0,0 +1,27 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "../styles/globals.css"; +import { SessionProvider } from "next-auth/react"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "PetSoft - Pet daycare software", + description: "Take care of people's pets responsibly with PetSoft.", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/petsoft/final-code/src/components/app-footer.tsx b/petsoft/final-code/src/components/app-footer.tsx new file mode 100644 index 0000000..2d06faa --- /dev/null +++ b/petsoft/final-code/src/components/app-footer.tsx @@ -0,0 +1,9 @@ +export default function AppFooter() { + return ( +
    + + © 2030 ByteGrad. All rights reserved. + +
    + ); +} diff --git a/petsoft/final-code/src/components/app-header.tsx b/petsoft/final-code/src/components/app-header.tsx new file mode 100644 index 0000000..081d90f --- /dev/null +++ b/petsoft/final-code/src/components/app-header.tsx @@ -0,0 +1,47 @@ +"use client"; + +import Link from "next/link"; +import Logo from "./logo"; +import { usePathname } from "next/navigation"; +import { cn } from "@/lib/utils"; + +const routes = [ + { + label: "Dashboard", + path: "/app/dashboard", + }, + { + label: "Account", + path: "/app/account", + }, +]; + +export default function AppHeader() { + const activePathname = usePathname(); + + return ( +
    + + + +
    + ); +} diff --git a/petsoft/final-code/src/components/auth-form-btn.tsx b/petsoft/final-code/src/components/auth-form-btn.tsx new file mode 100644 index 0000000..771945d --- /dev/null +++ b/petsoft/final-code/src/components/auth-form-btn.tsx @@ -0,0 +1,19 @@ +"use client"; + +import React from "react"; +import { Button } from "./ui/button"; +import { useFormStatus } from "react-dom"; + +type AuthFormBtnProps = { + type: "logIn" | "signUp"; +}; + +export default function AuthFormBtn({ type }: AuthFormBtnProps) { + const { pending } = useFormStatus(); + + return ( + + ); +} diff --git a/petsoft/final-code/src/components/auth-form.tsx b/petsoft/final-code/src/components/auth-form.tsx new file mode 100644 index 0000000..aabadd1 --- /dev/null +++ b/petsoft/final-code/src/components/auth-form.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { logIn, signUp } from "@/actions/actions"; +import { Input } from "./ui/input"; +import { Label } from "./ui/label"; +import AuthFormBtn from "./auth-form-btn"; +import { useFormState } from "react-dom"; + +type AuthFormProps = { + type: "logIn" | "signUp"; +}; + +export default function AuthForm({ type }: AuthFormProps) { + const [signUpError, dispatchSignUp] = useFormState(signUp, undefined); + const [logInError, dispatchLogIn] = useFormState(logIn, undefined); + + return ( + +
    + + +
    + +
    + + +
    + + + + {signUpError && ( +

    {signUpError.message}

    + )} + {logInError && ( +

    {logInError.message}

    + )} + + ); +} diff --git a/petsoft/final-code/src/components/background-pattern.tsx b/petsoft/final-code/src/components/background-pattern.tsx new file mode 100644 index 0000000..e761429 --- /dev/null +++ b/petsoft/final-code/src/components/background-pattern.tsx @@ -0,0 +1,3 @@ +export default function BackgroundPattern() { + return
    ; +} diff --git a/petsoft/final-code/src/components/branding.tsx b/petsoft/final-code/src/components/branding.tsx new file mode 100644 index 0000000..c7e2263 --- /dev/null +++ b/petsoft/final-code/src/components/branding.tsx @@ -0,0 +1,12 @@ +import H1 from "./h1"; + +export default function Branding() { + return ( +
    +

    + PetSoft +

    +

    Manage your pet daycare with ease

    +
    + ); +} diff --git a/petsoft/final-code/src/components/content-block.tsx b/petsoft/final-code/src/components/content-block.tsx new file mode 100644 index 0000000..89f114d --- /dev/null +++ b/petsoft/final-code/src/components/content-block.tsx @@ -0,0 +1,22 @@ +import { cn } from "@/lib/utils"; + +type ContentBlockProps = { + children: React.ReactNode; + className?: string; +}; + +export default function ContentBlock({ + children, + className, +}: ContentBlockProps) { + return ( +
    + {children} +
    + ); +} diff --git a/petsoft/final-code/src/components/h1.tsx b/petsoft/final-code/src/components/h1.tsx new file mode 100644 index 0000000..76e8c02 --- /dev/null +++ b/petsoft/final-code/src/components/h1.tsx @@ -0,0 +1,14 @@ +import { cn } from "@/lib/utils"; + +type H1Props = { + children: React.ReactNode; + className?: string; +}; + +export default function H1({ children, className }: H1Props) { + return ( +

    + {children} +

    + ); +} diff --git a/petsoft/final-code/src/components/logo.tsx b/petsoft/final-code/src/components/logo.tsx new file mode 100644 index 0000000..c981e19 --- /dev/null +++ b/petsoft/final-code/src/components/logo.tsx @@ -0,0 +1,11 @@ +import Image from "next/image"; +import logo from "../../public/logo.svg"; +import Link from "next/link"; + +export default function Logo() { + return ( + + PetSoft logo + + ); +} diff --git a/petsoft/final-code/src/components/pet-button.tsx b/petsoft/final-code/src/components/pet-button.tsx new file mode 100644 index 0000000..35da370 --- /dev/null +++ b/petsoft/final-code/src/components/pet-button.tsx @@ -0,0 +1,69 @@ +"use client"; + +import { PlusIcon } from "@radix-ui/react-icons"; +import { Button } from "./ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "./ui/dialog"; +import PetForm from "./pet-form"; +import { useState } from "react"; +import { flushSync } from "react-dom"; + +type PetButtonProps = { + actionType: "add" | "edit" | "checkout"; + disabled?: boolean; + onClick?: () => void; + children?: React.ReactNode; +}; + +export default function PetButton({ + actionType, + disabled, + onClick, + children, +}: PetButtonProps) { + const [isFormOpen, setIsFormOpen] = useState(false); + + if (actionType === "checkout") { + return ( + + ); + } + + return ( + + + {actionType === "add" ? ( + + ) : ( + + )} + + + + + + {actionType === "add" ? "Add a new pet" : "Edit pet"} + + + + { + flushSync(() => { + setIsFormOpen(false); + }); + }} + /> + + + ); +} diff --git a/petsoft/final-code/src/components/pet-details.tsx b/petsoft/final-code/src/components/pet-details.tsx new file mode 100644 index 0000000..e6071c5 --- /dev/null +++ b/petsoft/final-code/src/components/pet-details.tsx @@ -0,0 +1,92 @@ +"use client"; + +import { usePetContext } from "@/lib/hooks"; +import Image from "next/image"; +import PetButton from "./pet-button"; +import { Pet } from "@prisma/client"; + +export default function PetDetails() { + const { selectedPet } = usePetContext(); + + return ( +
    + {!selectedPet ? ( + + ) : ( + <> + + + + + + + )} +
    + ); +} + +function EmptyView() { + return ( +

    + No pet selected +

    + ); +} + +type Props = { + pet: Pet; +}; + +function TopBar({ pet }: Props) { + const { handleCheckoutPet } = usePetContext(); + + return ( +
    + Selected pet image + +

    {pet.name}

    + +
    + Edit + await handleCheckoutPet(pet.id)} + > + Checkout + +
    +
    + ); +} + +function OtherInfo({ pet }: Props) { + return ( +
    +
    +

    + Owner name +

    +

    {pet.ownerName}

    +
    + +
    +

    Age

    +

    {pet.age}

    +
    +
    + ); +} + +function Notes({ pet }: Props) { + return ( +
    + {pet.notes} +
    + ); +} diff --git a/petsoft/final-code/src/components/pet-form-btn.tsx b/petsoft/final-code/src/components/pet-form-btn.tsx new file mode 100644 index 0000000..ef4439e --- /dev/null +++ b/petsoft/final-code/src/components/pet-form-btn.tsx @@ -0,0 +1,13 @@ +import { Button } from "./ui/button"; + +type PetFormBtnProps = { + actionType: "add" | "edit"; +}; + +export default function PetFormBtn({ actionType }: PetFormBtnProps) { + return ( + + ); +} diff --git a/petsoft/final-code/src/components/pet-form.tsx b/petsoft/final-code/src/components/pet-form.tsx new file mode 100644 index 0000000..56444e7 --- /dev/null +++ b/petsoft/final-code/src/components/pet-form.tsx @@ -0,0 +1,103 @@ +"use client"; + +import { usePetContext } from "@/lib/hooks"; +import { Input } from "./ui/input"; +import { Label } from "./ui/label"; +import { Textarea } from "./ui/textarea"; +import PetFormBtn from "./pet-form-btn"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { DEFAULT_PET_IMAGE } from "@/lib/constants"; +import { TPetForm, petFormSchema } from "@/lib/validations"; + +type PetFormProps = { + actionType: "add" | "edit"; + onFormSubmission: () => void; +}; + +export default function PetForm({ + actionType, + onFormSubmission, +}: PetFormProps) { + const { handleAddPet, handleEditPet, selectedPet } = usePetContext(); + + const { + register, + trigger, + getValues, + formState: { errors }, + } = useForm({ + resolver: zodResolver(petFormSchema), + defaultValues: + actionType === "edit" + ? { + name: selectedPet?.name, + ownerName: selectedPet?.ownerName, + imageUrl: selectedPet?.imageUrl, + age: selectedPet?.age, + notes: selectedPet?.notes, + } + : undefined, + }); + + return ( +
    { + const result = await trigger(); + if (!result) return; + + onFormSubmission(); + + const petData = getValues(); + petData.imageUrl = petData.imageUrl || DEFAULT_PET_IMAGE; + + if (actionType === "add") { + await handleAddPet(petData); + } else if (actionType === "edit") { + await handleEditPet(selectedPet!.id, petData); + } + }} + className="flex flex-col" + > +
    +
    + + + {errors.name &&

    {errors.name.message}

    } +
    + +
    + + + {errors.ownerName && ( +

    {errors.ownerName.message}

    + )} +
    + +
    + + + {errors.imageUrl && ( +

    {errors.imageUrl.message}

    + )} +
    + +
    + + + {errors.age &&

    {errors.age.message}

    } +
    + +
    + +