From ed76b56e02af392da3337257558bdbc9e0951033 Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 02:49:26 +0700 Subject: [PATCH 01/14] Added Delete and Edit Features --- client/css/style.css | 1508 +++++++++++++++++++++++++++++++++++++++++- client/index.html | 72 +- client/index.js | 112 +++- client/js/home.js | 41 +- 4 files changed, 1691 insertions(+), 42 deletions(-) diff --git a/client/css/style.css b/client/css/style.css index 7349a9b..719edea 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -278,4 +278,1510 @@ input.login-template-xd { width: 40px; } /* * =========================================== END OF LOGIN TEMPLATE =========================================== */ -/* ! ====================================== END OF Careful Error Starts Here ====================================== */ \ No newline at end of file +/* ! ====================================== END OF Careful Error Starts Here ====================================== */ + +/* ! ============================ Main (On Progress) ============================ */ + +:root { + --blue: #5e72e4; + --indigo: #5603ad; + --purple: #8965e0; + --pink: #f3a4b5; + --red: #f5365c; + --orange: #fb6340; + --yellow: #ffd600; + --green: #2dce89; + --teal: #11cdef; + --cyan: #2bffc6; + --white: #fff; + --gray: #8898aa; + --gray-dark: #32325d; + --light: #ced4da; + --lighter: #e9ecef; + --primary: #5e72e4; + --secondary: #f7fafc; + --success: #2dce89; + --info: #11cdef; + --warning: #fb6340; + --danger: #f5365c; + --light: #adb5bd; + --dark: #212529; + --default: #172b4d; + --white: #fff; + --neutral: #fff; + --darker: black; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + --font-family-sans-serif: Open Sans, sans-serif; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +@-ms-viewport { + width: device-width; +} + +figcaption, +footer, +main, +nav, +section { + display: block; +} + +body { + font-family: Open Sans, sans-serif; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + margin: 0; + text-align: left; + color: #525f7f; + background-color: #f8f9fe; +} + +[tabindex='-1']:focus { + outline: 0 !important; +} + +h2, +h3 { + margin-top: 0; + margin-bottom: .5rem; +} + +p { + margin-top: 0; + margin-bottom: 1rem; +} + +ul { + margin-top: 0; + margin-bottom: 1rem; +} + +ul ul { + margin-bottom: 0; +} + +dfn { + font-style: italic; +} + +strong { + font-weight: bolder; +} + +a { + text-decoration: none; + color: #5e72e4; + background-color: transparent; + -webkit-text-decoration-skip: objects; +} + +a:hover { + text-decoration: none; + color: #233dd2; +} + +a:not([href]):not([tabindex]) { + text-decoration: none; + color: inherit; +} + +a:not([href]):not([tabindex]):hover, +a:not([href]):not([tabindex]):focus { + text-decoration: none; + color: inherit; +} + +a:not([href]):not([tabindex]):focus { + outline: 0; +} + +img { + vertical-align: middle; + border-style: none; +} + +table { + border-collapse: collapse; +} + +caption { + padding-top: 1rem; + padding-bottom: 1rem; + caption-side: bottom; + text-align: left; + color: #8898aa; +} + +th { + text-align: inherit; +} + +button { + border-radius: 0; +} + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} + +input, +button { + font-family: inherit; + font-size: inherit; + line-height: inherit; + margin: 0; +} + +button, +input { + overflow: visible; +} + +button { + text-transform: none; +} + +button, +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +button::-moz-focus-inner, +[type='button']::-moz-focus-inner, +[type='reset']::-moz-focus-inner, +[type='submit']::-moz-focus-inner { + padding: 0; + border-style: none; +} + +input[type='radio'], +input[type='checkbox'] { + box-sizing: border-box; + padding: 0; +} + +input[type='date'], +input[type='time'], +input[type='datetime-local'], +input[type='month'] { + -webkit-appearance: listbox; +} + +legend { + font-size: 1.5rem; + line-height: inherit; + display: block; + width: 100%; + max-width: 100%; + margin-bottom: .5rem; + padding: 0; + white-space: normal; + color: inherit; +} + +progress { + vertical-align: baseline; +} + +[type='number']::-webkit-inner-spin-button, +[type='number']::-webkit-outer-spin-button { + height: auto; +} + +[type='search'] { + outline-offset: -2px; + -webkit-appearance: none; +} + +[type='search']::-webkit-search-cancel-button, +[type='search']::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button; +} + +[hidden] { + display: none !important; +} + +h2, +h3, +.h2, +.h3 { + font-family: inherit; + font-weight: 600; + line-height: 1.5; + margin-bottom: .5rem; + color: #32325d; +} + +h2, +.h2 { + font-size: 1.25rem; +} + +h3, +.h3 { + font-size: 1.0625rem; +} + +.containerhome { + width: 100%; + margin-right: auto; + margin-left: auto; + padding-right: 15px; + padding-left: 15px; +} + +@media (min-width: 576px) { + .containerhome { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .containerhome { + max-width: 720px; + } +} + +@media (min-width: 992px) { + .containerhome { + max-width: 960px; + } +} + +@media (min-width: 1200px) { + .containerhome { + max-width: 1140px; + } +} + +.row { + display: flex; + margin-right: -15px; + margin-left: -15px; + flex-wrap: wrap; +} + +.col, +.col-xl-6 { + position: relative; + width: 100%; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col { + max-width: 100%; + flex-basis: 0; + flex-grow: 1; +} + +@media (min-width: 1200px) { + + .col-xl-6 { + max-width: 50%; + flex: 0 0 50%; + } +} + +.table { + width: 100%; + margin-bottom: 1rem; + background-color: transparent; +} + +.table th, +.table td { + padding: 1rem; + vertical-align: top; + border-top: 1px solid #e9ecef; +} + +.table thead th { + vertical-align: bottom; + border-bottom: 2px solid #e9ecef; +} + +.table tbody + tbody { + border-top: 2px solid #e9ecef; +} + +.table .table { + background-color: #f8f9fe; +} + +.table-dark, +.table-dark > th, +.table-dark > td { + background-color: #c1c2c3; +} + +.table .thead-dark th { + color: #f8f9fe; + border-color: #1f3a68; + background-color: #172b4d; +} + +.table .thead-light th { + color: #8898aa; + border-color: #e9ecef; + background-color: #f6f9fc; +} + +.table-dark { + color: #f8f9fe; + background-color: #172b4d; +} + +.table-dark th, +.table-dark td, +.table-dark thead th { + border-color: #1f3a68; +} + +.table-responsive { + display: block; + overflow-x: auto; + width: 100%; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; +} + +.btn { + font-size: 1rem; + font-weight: 600; + line-height: 1.5; + display: inline-block; + padding: .625rem 1.25rem; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + text-align: center; + vertical-align: middle; + white-space: nowrap; + border: 1px solid transparent; + border-radius: .375rem; +} + +@media screen and (prefers-reduced-motion: reduce) { + .btn { + transition: none; + } +} + +.btn:hover, +.btn:focus { + text-decoration: none; +} + +.btn:focus { + outline: 0; + box-shadow: 0 7px 14px rgba(50, 50, 93, .1), 0 3px 6px rgba(0, 0, 0, .08); +} + +.btn.disabled, +.btn:disabled { + opacity: .65; + box-shadow: none; +} + +.btn:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.btn:not(:disabled):not(.disabled):active, +.btn:not(:disabled):not(.disabled).active { + box-shadow: none; +} + +.btn:not(:disabled):not(.disabled):active:focus, +.btn:not(:disabled):not(.disabled).active:focus { + box-shadow: 0 7px 14px rgba(50, 50, 93, .1), 0 3px 6px rgba(0, 0, 0, .08), none; +} + +a.btn.disabled { + pointer-events: none; +} + +.btn-sm { + font-size: .875rem; + line-height: 1.5; + padding: .25rem .5rem; + border-radius: .375rem; +} + +.dropdown { + position: relative; +} + +.dropdown-menu { + font-size: 1rem; + position: absolute; + z-index: 1000; + top: 100%; + left: 0; + display: none; + float: left; + min-width: 10rem; + margin: .125rem 0 0; + padding: .5rem 0; + list-style: none; + text-align: left; + color: #525f7f; + border: 0 solid rgba(0, 0, 0, .15); + border-radius: .4375rem; + background-color: #fff; + background-clip: padding-box; + box-shadow: 0 50px 100px rgba(50, 50, 93, .1), 0 15px 35px rgba(50, 50, 93, .15), 0 5px 15px rgba(0, 0, 0, .1); +} + +.dropdown-menu-right { + right: 0; + left: auto; +} + +.dropdown-menu[x-placement^='top'], +.dropdown-menu[x-placement^='right'], +.dropdown-menu[x-placement^='bottom'], +.dropdown-menu[x-placement^='left'] { + right: auto; + bottom: auto; +} + +.dropdown-item { + font-weight: 400; + display: block; + clear: both; + width: 100%; + padding: .25rem 1.5rem; + text-align: inherit; + white-space: nowrap; + color: #212529; + border: 0; + background-color: transparent; +} + +.dropdown-item:hover, +.dropdown-item:focus { + text-decoration: none; + color: #16181b; + background-color: #f6f9fc; +} + +.dropdown-item.active, +.dropdown-item:active { + text-decoration: none; + color: #fff; + background-color: #5e72e4; +} + +.dropdown-item.disabled, +.dropdown-item:disabled { + color: #8898aa; + background-color: transparent; +} + +.nav { + display: flex; + margin-bottom: 0; + padding-left: 0; + list-style: none; + flex-wrap: wrap; +} + +.card { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + border: 1px solid rgba(0, 0, 0, .05); + border-radius: .375rem; + background-color: #fff; + background-clip: border-box; +} + +.card-header { + margin-bottom: 0; + padding: 1.25rem 1.5rem; + border-bottom: 1px solid rgba(0, 0, 0, .05); + background-color: #fff; +} + +.card-header:first-child { + border-radius: calc(.375rem - 1px) calc(.375rem - 1px) 0 0; +} + +.card-footer { + padding: 1.25rem 1.5rem; + border-top: 1px solid rgba(0, 0, 0, .05); + background-color: #fff; +} + +.card-footer:last-child { + border-radius: 0 0 calc(.375rem - 1px) calc(.375rem - 1px); +} + +.pagination { + display: flex; + padding-left: 0; + list-style: none; + border-radius: .375rem; +} + +.page-link { + line-height: 1.25; + position: relative; + display: block; + margin-left: -1px; + padding: .5rem .75rem; + color: #8898aa; + border: 1px solid #dee2e6; + background-color: #fff; +} + +.page-link:hover { + z-index: 2; + text-decoration: none; + color: #8898aa; + border-color: #dee2e6; + background-color: #dee2e6; +} + +.page-link:focus { + z-index: 2; + outline: 0; + box-shadow: none; +} + +.page-link:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.page-item:first-child .page-link { + margin-left: 0; + border-top-left-radius: .375rem; + border-bottom-left-radius: .375rem; +} + +.page-item:last-child .page-link { + border-top-right-radius: .375rem; + border-bottom-right-radius: .375rem; +} + +.page-item.active .page-link { + z-index: 1; + color: #fff; + border-color: #5e72e4; + background-color: #5e72e4; +} + +.page-item.disabled .page-link { + cursor: auto; + pointer-events: none; + color: #8898aa; + border-color: #dee2e6; + background-color: #fff; +} + +.badge { + font-size: 66%; + font-weight: 600; + line-height: 1; + display: inline-block; + padding: .35rem .375rem; + text-align: center; + vertical-align: baseline; + white-space: nowrap; + border-radius: .375rem; +} + +.badge:empty { + display: none; +} + +.btn .badge { + position: relative; + top: -1px; +} + +@keyframes progress-bar-stripes { + from { + background-position: 1rem 0; + } + + to { + background-position: 0 0; + } +} + +.progress { + font-size: .75rem; + display: flex; + overflow: hidden; + height: 1rem; + border-radius: .375rem; + background-color: #e9ecef; + box-shadow: inset 0 .1rem .1rem rgba(0, 0, 0, .1); +} + +.progress-bar { + display: flex; + flex-direction: column; + transition: width .6s ease; + text-align: center; + white-space: nowrap; + color: #fff; + background-color: #5e72e4; + justify-content: center; +} + +@media screen and (prefers-reduced-motion: reduce) { + .progress-bar { + transition: none; + } +} + +.media { + display: flex; + align-items: flex-start; +} + +.media-body { + flex: 1 1; +} + +.tooltip { + font-family: Open Sans, sans-serif; + font-size: .875rem; + font-weight: 400; + font-style: normal; + line-height: 1.5; + position: absolute; + z-index: 1070; + display: block; + margin: 0; + text-align: left; + text-align: start; + white-space: normal; + text-decoration: none; + letter-spacing: normal; + word-spacing: normal; + text-transform: none; + word-wrap: break-word; + word-break: normal; + opacity: 0; + text-shadow: none; + line-break: auto; +} + +.tooltip.show +{ + opacity: .9; +} +.tooltip .arrow +{ + position: absolute; + + display: block; + + width: .8rem; + height: .4rem; +} +.tooltip .arrow::before +{ + position: absolute; + + content: ''; + + border-style: solid; + border-color: transparent; +} + +.bs-tooltip-top, +.bs-tooltip-auto[x-placement^='top'] +{ + padding: .4rem 0; +} +.bs-tooltip-top .arrow, +.bs-tooltip-auto[x-placement^='top'] .arrow +{ + bottom: 0; +} +.bs-tooltip-top .arrow::before, +.bs-tooltip-auto[x-placement^='top'] .arrow::before +{ + top: 0; + + border-width: .4rem .4rem 0; + border-top-color: #000; +} + +.bs-tooltip-right, +.bs-tooltip-auto[x-placement^='right'] +{ + padding: 0 .4rem; +} +.bs-tooltip-right .arrow, +.bs-tooltip-auto[x-placement^='right'] .arrow +{ + left: 0; + + width: .4rem; + height: .8rem; +} +.bs-tooltip-right .arrow::before, +.bs-tooltip-auto[x-placement^='right'] .arrow::before +{ + right: 0; + + border-width: .4rem .4rem .4rem 0; + border-right-color: #000; +} + +.bs-tooltip-bottom, +.bs-tooltip-auto[x-placement^='bottom'] +{ + padding: .4rem 0; +} +.bs-tooltip-bottom .arrow, +.bs-tooltip-auto[x-placement^='bottom'] .arrow +{ + top: 0; +} +.bs-tooltip-bottom .arrow::before, +.bs-tooltip-auto[x-placement^='bottom'] .arrow::before +{ + bottom: 0; + + border-width: 0 .4rem .4rem; + border-bottom-color: #000; +} + +.bs-tooltip-left, +.bs-tooltip-auto[x-placement^='left'] +{ + padding: 0 .4rem; +} +.bs-tooltip-left .arrow, +.bs-tooltip-auto[x-placement^='left'] .arrow +{ + right: 0; + + width: .4rem; + height: .8rem; +} +.bs-tooltip-left .arrow::before, +.bs-tooltip-auto[x-placement^='left'] .arrow::before +{ + left: 0; + + border-width: .4rem 0 .4rem .4rem; + border-left-color: #000; +} + +.tooltip-inner +{ + max-width: 200px; + padding: .25rem .5rem; + + text-align: center; + + color: #fff; + border-radius: .375rem; + background-color: #000; +} + +.bg-success { + background-color: #2dce89 !important; +} + +a.bg-success:hover, +a.bg-success:focus, +button.bg-success:hover, +button.bg-success:focus { + background-color: #24a46d !important; +} + +.bg-info { + background-color: #11cdef !important; +} + +a.bg-info:hover, +a.bg-info:focus, +button.bg-info:hover, +button.bg-info:focus { + background-color: #0da5c0 !important; +} + +.bg-warning { + background-color: #fb6340 !important; +} + +a.bg-warning:hover, +a.bg-warning:focus, +button.bg-warning:hover, +button.bg-warning:focus { + background-color: #fa3a0e !important; +} + +.bg-danger { + background-color: #f5365c !important; +} + +a.bg-danger:hover, +a.bg-danger:focus, +button.bg-danger:hover, +button.bg-danger:focus { + background-color: #ec0c38 !important; +} + +.bg-default { + background-color: #172b4d !important; +} + +a.bg-default:hover, +a.bg-default:focus, +button.bg-default:hover, +button.bg-default:focus { + background-color: #0b1526 !important; +} + +.bg-transparent { + background-color: transparent !important; +} + +.border-0 { + border: 0 !important; +} + +.rounded-circle { + border-radius: 50% !important; +} + +.d-flex { + display: flex !important; +} + +.justify-content-end { + justify-content: flex-end !important; +} + +.align-items-center { + align-items: center !important; +} + +@media (min-width: 1200px) { + + .justify-content-xl-between { + justify-content: space-between !important; + } +} + +.sr-only { + position: absolute; + overflow: hidden; + clip: rect(0, 0, 0, 0); + width: 1px; + height: 1px; + padding: 0; + white-space: nowrap; + border: 0; +} + +.shadow { + box-shadow: 0 0 2rem 0 rgba(136, 152, 170, .15) !important; +} + +.mb-0 { + margin-bottom: 0 !important; +} + +.mr-2 { + margin-right: .5rem !important; +} + +.mr-3 { + margin-right: 1rem !important; +} + +.mr-4 { + margin-right: 1.5rem !important; +} + +.mt-5 { + margin-top: 3rem !important; +} + +.mb-5 { + margin-bottom: 3rem !important; +} + +.mt-7 { + margin-top: 6rem !important; +} + +.py-4 { + padding-top: 1.5rem !important; +} + +.py-4 { + padding-bottom: 1.5rem !important; +} + +.m-auto { + margin: auto !important; +} + +.text-right { + text-align: right !important; +} + +.text-center { + text-align: center !important; +} + +.text-white { + color: #fff !important; +} + +.text-light { + color: #adb5bd !important; +} + +a.text-light:hover, +a.text-light:focus { + color: #919ca6 !important; +} + +.text-white { + color: #fff !important; +} + +a.text-white:hover, +a.text-white:focus { + color: #e6e6e6 !important; +} + +@media print { + *, + *::before, + *::after { + box-shadow: none !important; + text-shadow: none !important; + } + + a:not(.btn) { + text-decoration: underline; + } + + thead { + display: table-header-group; + } + + tr, + img { + page-break-inside: avoid; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } + +@page { + size: a3; + } + + body { + min-width: 992px !important; + } + + .containerhome { + min-width: 992px !important; + } + + .badge { + border: 1px solid #000; + } + + .table { + border-collapse: collapse !important; + } + + .table td, + .table th { + background-color: #fff !important; + } + + .table-dark { + color: inherit; + } + + .table-dark th, + .table-dark td, + .table-dark thead th, + .table-dark tbody + tbody { + border-color: #e9ecef; + } + + .table .thead-dark th { + color: inherit; + border-color: #e9ecef; + } +} + +figcaption, +main { + display: block; +} + +main { + overflow: hidden; +} + +@keyframes floating-lg { + 0% { + transform: translateY(0px); + } + + 50% { + transform: translateY(15px); + } + + 100% { + transform: translateY(0px); + } +} + +@keyframes floating { + 0% { + transform: translateY(0px); + } + + 50% { + transform: translateY(10px); + } + + 100% { + transform: translateY(0px); + } +} + +@keyframes floating-sm { + 0% { + transform: translateY(0px); + } + + 50% { + transform: translateY(5px); + } + + 100% { + transform: translateY(0px); + } +} + +[class*='shadow'] { + transition: all .15s ease; +} + +.text-sm { + font-size: .875rem !important; +} + +.text-white { + color: #fff !important; +} + +a.text-white:hover, +a.text-white:focus { + color: #e6e6e6 !important; +} + +.text-light { + color: #ced4da !important; +} + +a.text-light:hover, +a.text-light:focus { + color: #b1bbc4 !important; +} + +.avatar { + font-size: 1rem; + display: inline-flex; + width: 48px; + height: 48px; + color: #fff; + border-radius: 50%; + background-color: #adb5bd; + align-items: center; + justify-content: center; +} + +.avatar img { + width: 100%; + border-radius: 50%; +} + +.avatar-sm { + font-size: .875rem; + width: 36px; + height: 36px; +} + +.avatar-group .avatar { + position: relative; + z-index: 2; + border: 2px solid #fff; +} + +.avatar-group .avatar:hover { + z-index: 3; +} + +.avatar-group .avatar + .avatar { + margin-left: -1rem; +} + +.badge { + text-transform: uppercase; +} + +.badge a { + color: #fff; +} + +.btn .badge:not(:first-child) { + margin-left: .5rem; +} + +.btn .badge:not(:last-child) { + margin-right: .5rem; +} + +.badge-dot { + font-size: .875rem; + font-weight: 400; + padding-right: 0; + padding-left: 0; + text-transform: none; + background: transparent; +} + +.badge-dot strong { + color: #32325d; +} + +.badge-dot i { + display: inline-block; + width: .375rem; + height: .375rem; + margin-right: .375rem; + vertical-align: middle; + border-radius: 50%; +} + +.btn { + font-size: .875rem; + position: relative; + transition: all .15s ease; + letter-spacing: .025em; + text-transform: none; + will-change: transform; +} + +.btn:hover { + transform: translateY(-1px); + box-shadow: 0 7px 14px rgba(50, 50, 93, .1), 0 3px 6px rgba(0, 0, 0, .08); +} + +.btn:not(:last-child) { + margin-right: .5rem; +} + +.btn i:not(:first-child) { + margin-left: .5rem; +} + +.btn i:not(:last-child) { + margin-right: .5rem; +} + +.btn-sm { + font-size: .75rem; +} + +[class*='btn-outline-'] { + border-width: 1px; +} + +.btn-icon-only { + width: 2.375rem; + height: 2.375rem; + padding: 0; +} + +a.btn-icon-only { + line-height: 2.5; +} + +.btn-icon-only.btn-sm { + width: 2rem; + height: 2rem; +} + +.main-content { + position: relative; +} + +.dropdown { + display: inline-block; +} + +.dropdown-menu { + min-width: 12rem; +} +.dropdown-menu.show +{ + display: block; +} + + +.dropdown-menu .dropdown-item { + font-size: .875rem; + padding: .5rem 1rem; +} + +.dropdown-menu .dropdown-item > i { + font-size: 1rem; + margin-right: 1rem; + vertical-align: -17%; +} + +.dropdown-menu a.media > div:first-child { + line-height: 1; +} + +.dropdown-menu a.media p { + color: #8898aa; +} + +.dropdown-menu a.media:hover p { + color: #172b4d !important; +} + +.footer { + padding: 2.5rem 0; + background: #f7fafc; +} + +.footer .copyright { + font-size: .875rem; +} + +@media (min-width: 768px) { + +@keyframes show-navbar-dropdown { + 0% { + transition: visibility .25s, opacity .25s, transform .25s; + transform: translate(0, 10px) perspective(200px) rotateX(-2deg); + opacity: 0; + } + + 100% { + transform: translate(0, 0); + opacity: 1; + } +} + +@keyframes hide-navbar-dropdown { + from { + opacity: 1; + } + + to { + transform: translate(0, 10px); + opacity: 0; + } +} +} + +@keyframes show-navbar-collapse { + 0% { + transform: scale(.95); + transform-origin: 100% 0; + opacity: 0; + } + + 100% { + transform: scale(1); + opacity: 1; + } +} + +@keyframes hide-navbar-collapse { + from { + transform: scale(1); + transform-origin: 100% 0; + opacity: 1; + } + + to { + transform: scale(.95); + opacity: 0; + } +} + +.page-item.active .page-link { + box-shadow: 0 7px 14px rgba(50, 50, 93, .1), 0 3px 6px rgba(0, 0, 0, .08); +} + +.page-item .page-link, +.page-item span { + font-size: .875rem; + display: flex; + width: 36px; + height: 36px; + margin: 0 3px; + padding: 0; + border-radius: 50% !important; + align-items: center; + justify-content: center; +} + +.progress { + overflow: hidden; + height: 8px; + margin-bottom: 1rem; + border-radius: .25rem; + background-color: #e9ecef; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} + +.progress .sr-only { + font-size: 13px; + line-height: 20px; + left: 0; + clip: auto; + width: auto; + height: 20px; + margin: 0 0 0 30px; +} + +.progress-bar { + height: auto; + border-radius: 0; + box-shadow: none; +} + +.table thead th { + font-size: .65rem; + padding-top: .75rem; + padding-bottom: .75rem; + letter-spacing: 1px; + text-transform: uppercase; + border-bottom: 1px solid #e9ecef; +} + +.table th { + font-weight: 600; +} + +.table td .progress { + width: 120px; + height: 3px; + margin: 0; +} + +.table td, +.table th { + font-size: .8125rem; + white-space: nowrap; +} + +.table.align-items-center td, +.table.align-items-center th { + vertical-align: middle; +} + +.table .thead-dark th { + color: #4d7bca; + background-color: #1c345d; +} + +.table .thead-light th { + color: #8898aa; + background-color: #f6f9fc; +} + +.table-flush td, +.table-flush th { + border-right: 0; + border-left: 0; +} + +.table-flush tbody tr:first-child td, +.table-flush tbody tr:first-child th { + border-top: 0; +} + +.table-flush tbody tr:last-child td, +.table-flush tbody tr:last-child th { + border-bottom: 0; +} + +.card .table { + margin-bottom: 0; +} + +.card .table td, +.card .table th { + padding-right: 1.5rem; + padding-left: 1.5rem; +} + +p { + font-size: 1rem; + font-weight: 300; + line-height: 1.7; +} + +@media (max-width: 768px) { + .btn { + margin-bottom: 10px; + } +} + + +/* ! */ \ No newline at end of file diff --git a/client/index.html b/client/index.html index 3988203..c4d2963 100644 --- a/client/index.html +++ b/client/index.html @@ -70,9 +70,8 @@

Hey Kamu!

-
- +
- -

Hello There!

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#FirstLastHandle
1MarkOtto@mdo
2JacobThornton@fat
3Larrythe Bird@twitter
+ + + +
+
+
+ +
+
+
+

List Todo

+
+
+ + + + + + + + + + + + + +
TodoDeskripsiBatas TanggalStatus
+
+
+
+
+
+ + +
diff --git a/client/index.js b/client/index.js index 8fd5b96..1bc9086 100644 --- a/client/index.js +++ b/client/index.js @@ -40,6 +40,12 @@ const showHome = _ => { fetchTodo() } +const refreshHome = _ => { + $('.loginregister').hide() + $('.home').show() + fetchTodo() +} + const hideAll = _ => { $('.home').hide() $('.login').hide() @@ -49,7 +55,7 @@ const hideAll = _ => { const getLocation = _ => { if (navigator.geolocation) { if (!localStorage.getItem('userlocation')) { - swal({ + Swal.fire({ title: 'Tunggu!', text: 'Aku butuh akses lokasi kamu nih', icon: 'info', @@ -64,7 +70,7 @@ const getLocation = _ => { }) } } else { - swal({ + Swal.fire({ title: 'Maaf :(', text: 'Fitur Geolocation nggak disupport browser kamu...', icon: 'warning', @@ -86,7 +92,7 @@ const getLocation = _ => { function error() { hideAll() - swal({ + Swal.fire({ title: 'Oops...', text: 'Kamu tidak dapat lanjut ke website ini :(', icon: 'error' @@ -97,4 +103,104 @@ const getLocation = _ => { } }) } +} + +// * Toast SweetAlert +const Toast = Swal.mixin({ + toast: true, + position: 'top-end', + showConfirmButton: false, + timer: 3000, + timerProgressBar: true, + didOpen: toast => { + toast.addEventListener('mouseenter', Swal.stopTimer) + toast.addEventListener('mouseleave', Swal.resumeTimer) + } +}) + +// * Delete Todo +const deleteTodo = id => { + const access_token = localStorage.getItem('access_token') + const userlocation = localStorage.getItem('userlocation') + Swal.fire({ + title: `Delete Todo`, + text: `Kamu yakin mau hapus todo dengan id ${id}?`, + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#3085d6', + reverseButtons: true, + confirmButtonColor: '#d33', + confirmButtonText: 'Hapus' + }) + .then( button => { + if (button.isConfirmed) { + $.ajax({ + method: 'DELETE', + url: `http://127.0.0.1:3000/todos/${id}`, + headers: { + access_token, + userlocation + } + }) + .done( () => { + refreshHome() + Toast.fire({ + title: `Berhasil!`, + text: `Kamu berhasil menghapus Todo dengan id ${id}`, + icon: `success` + }) + }) + } + }) +} + +// * Edit Todo (Put) +const editTodo = (id, title, description, due_date) => { + // const access_token = localStorage.getItem('access_token') + // const userlocation = localStorage.getItem('userlocation') + Swal.fire({ + title: `Edit Todo`, + html: ` + + + + `, + focusConfirm: false, + showCancelButton: true, + confirmButtonColor: '#3085d6', + reverseButtons: true, + confirmButtonText: 'Edit', + preConfirm : _ => { + const title = Swal.getPopup().querySelector('#title').value + const description = Swal.getPopup().querySelector('#description').value + const due_date = Swal.getPopup().querySelector('#due_date').value + if (!title || !description || !due_date) { + Swal.showValidationMessage(`Tolong lengkapi semua kolom`) + } + return { title, description, due_date, status: false } + } + }) + .then( status => { + if (status.isConfirmed) { // ! WAIT FROM SERVER-SIDE (on-progress) + // $.ajax({ + // method: 'PUT', + // url: `http://127.0.0.1:3000/todos/${id}`, + // headers: { + // access_token, + // userlocation + // }, + // data: { + + // } + // }) + // .done( () => { + // refreshHome() + // Toast.fire({ + // title: `Berhasil!`, + // text: `Kamu berhasil menghapus Todo dengan id ${id}`, + // icon: `success` + // }) + // }) + } + }) } \ No newline at end of file diff --git a/client/js/home.js b/client/js/home.js index c578b04..44b337c 100644 --- a/client/js/home.js +++ b/client/js/home.js @@ -24,7 +24,7 @@ const fetchTodo = _ => { }) .done(data => { $('.weather').empty() - $('.data').empty() + $('#todos-tabledata').empty() new Promise( res => { const todayWeather = data.todayWeather const todos = data.todos @@ -37,7 +37,40 @@ const fetchTodo = _ => { $('.weather').append(weather) todos.forEach( todo => { - $('.data').append(`${todo.id}, ${todo.title}, ${todo.description}, ${todo.status}`) + let todostatus = todo.status + if (todostatus) { + todostatus = ` + + Done + + ` + } else { + todostatus = ` + + Pending + + ` + } + $('#todos-tabledata').append(` + + +
+
+ ${todo.title} +
+
+ + ${todo.description} + ${todo.due_date} + ${todostatus} + + + + + + + `) // ! FIX todo.due_date ON SERVER!!! CANNOT GET IN EDIT! + // ${todo.id}, ${todo.title}, ${todo.description}, ${todo.status} }) res() }) @@ -46,6 +79,9 @@ const fetchTodo = _ => { }) }) .fail(err => { + $('.loader-wrapper').fadeOut('slow') + localStorage.removeItem('access_token') + showLoginRegister() Swal.fire({ title: `${err.statusText}`, text: `${err.responseJSON.msg}\nStatus Code: ${err.status}`, @@ -60,6 +96,7 @@ const fetchTodo = _ => { }) $('#logout').on('click', e => { + e.preventDefault() Swal.fire({ title: 'Sebentar', text: 'Kamu yakin mau keluar?', From be1f9bbe8eb67fe3d1addb027666483f43148cfd Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 02:54:17 +0700 Subject: [PATCH 02/14] Fixed ISO Date, converted to normal date --- server/controllers/todo-controller.js | 8 +++++++- server/helpers/dateConverter.js | 6 +----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/server/controllers/todo-controller.js b/server/controllers/todo-controller.js index 3a07d9f..7bb5009 100644 --- a/server/controllers/todo-controller.js +++ b/server/controllers/todo-controller.js @@ -1,6 +1,7 @@ const { Todo } = require('../models') const convertPayload = require('../helpers/convertPayload') const checkWeatherByUserLocation = require('../helpers/get-weather') +const convertISOtoDate = require('../helpers/dateConverter') class Controller { @@ -25,8 +26,13 @@ class Controller { const whoIsLoggedIn = req.whoIsLoggedIn const userlocation = req.userLocation - const todos = await Todo.findAll({ where: { UserId: whoIsLoggedIn.id }}) + const findTodos = await Todo.findAll({ where: { UserId: whoIsLoggedIn.id }}) const todayWeather = await checkWeatherByUserLocation(userlocation) + const todos = [] + findTodos.forEach( todo => { + todo.dataValues.due_date = convertISOtoDate(todo.dataValues.due_date) + todos.push(todo) + }) res.status(200).json({ todos, todayWeather }) // * Returns All Todos + Today Weather ^^ } catch (err) { diff --git a/server/helpers/dateConverter.js b/server/helpers/dateConverter.js index 926b116..07c2f06 100644 --- a/server/helpers/dateConverter.js +++ b/server/helpers/dateConverter.js @@ -1,7 +1,3 @@ -const convertUNIXDateToISODate = date => { - return date * 1000 -} - const convertISOtoDate = date => { year = date.getFullYear() month = date.getMonth()+1 @@ -14,4 +10,4 @@ const convertISOtoDate = date => { return year+'-' + month + '-'+dt } -module.exports = { convertUNIXDateToISODate ,convertISOtoDate } \ No newline at end of file +module.exports = convertISOtoDate \ No newline at end of file From c184a3e2403e529e00b2fe1edd7e7fddb0e5db7a Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 03:36:19 +0700 Subject: [PATCH 03/14] Added Pending and Finished Todos Tables --- client/index.html | 86 +++++++++++++++++++++----------- client/index.js | 124 ++++++++++++++++++++++++++++++++++++++-------- client/js/home.js | 82 ++++++++++++++++++------------ 3 files changed, 209 insertions(+), 83 deletions(-) diff --git a/client/index.html b/client/index.html index c4d2963..fa728fe 100644 --- a/client/index.html +++ b/client/index.html @@ -92,40 +92,68 @@

Hey Kamu!

- -
-
-
- -
-
-
-

List Todo

-
-
- - - - - - - - - - - - - -
TodoDeskripsiBatas TanggalStatus
-
+ +
+
+
+ +
+
+
+
+

Pending Todos

+
+
+ + + + + + + + + + + + + + +
IDTodoDescriptionDue DateStatus
+
+ + +
+
+
+
+

Finished Todos

+
+
+ + + + + + + + + + + + + + +
IDTodoDescriptionDue DateStatus
+
+
+
+
+
- - -
diff --git a/client/index.js b/client/index.js index 1bc9086..0df0bcc 100644 --- a/client/index.js +++ b/client/index.js @@ -118,7 +118,7 @@ const Toast = Swal.mixin({ } }) -// * Delete Todo +// * Delete Todo (Delete) const deleteTodo = id => { const access_token = localStorage.getItem('access_token') const userlocation = localStorage.getItem('userlocation') @@ -156,8 +156,8 @@ const deleteTodo = id => { // * Edit Todo (Put) const editTodo = (id, title, description, due_date) => { - // const access_token = localStorage.getItem('access_token') - // const userlocation = localStorage.getItem('userlocation') + const access_token = localStorage.getItem('access_token') + const userlocation = localStorage.getItem('userlocation') Swal.fire({ title: `Edit Todo`, html: ` @@ -182,25 +182,105 @@ const editTodo = (id, title, description, due_date) => { }) .then( status => { if (status.isConfirmed) { // ! WAIT FROM SERVER-SIDE (on-progress) - // $.ajax({ - // method: 'PUT', - // url: `http://127.0.0.1:3000/todos/${id}`, - // headers: { - // access_token, - // userlocation - // }, - // data: { - - // } - // }) - // .done( () => { - // refreshHome() - // Toast.fire({ - // title: `Berhasil!`, - // text: `Kamu berhasil menghapus Todo dengan id ${id}`, - // icon: `success` - // }) - // }) + const data = { + title: status.value.title, + description: status.value.description, + due_date: status.value.due_date + } + + $.ajax({ + method: 'PUT', + url: `http://127.0.0.1:3000/todos/${id}`, + headers: { + access_token, + userlocation + }, + data + }) + .done( () => { + refreshHome() + Toast.fire({ + title: `Berhasil!`, + text: `Kamu berhasil Edit Todo dengan id ${id}`, + icon: `success` + }) + }) + } + }) +} + +// * Mark Done Todo (Patch) +const markDoneTodo = id => { + const access_token = localStorage.getItem('access_token') + const userlocation = localStorage.getItem('userlocation') + Swal.fire({ + title: `Update`, + text: `Kamu yakin mau selesaikan todo dengan id ${id}?`, + icon: 'question', + showCancelButton: true, + confirmButtonColor: '#3085d6', + reverseButtons: true, + confirmButtonText: 'Update' + }) + .then( status => { + if (status.isConfirmed) { + $.ajax({ + method: 'PATCH', + url: `http://127.0.0.1:3000/todos/${id}`, + headers: { + access_token, + userlocation + }, + data: { + status: true + } + }) + .done( () => { + refreshHome() + Toast.fire({ + title: `Berhasil!`, + text: `Kamu berhasil Menyelesaikan Todo dengan id ${id}`, + icon: `success` + }) + }) + } + }) +} + +// * Mark Done Todo (Patch) +const markUndoneTodo = id => { + const access_token = localStorage.getItem('access_token') + const userlocation = localStorage.getItem('userlocation') + Swal.fire({ + title: `Update`, + text: `Kamu yakin mau undo Status todo dengan id ${id}?`, + icon: 'question', + showCancelButton: true, + confirmButtonColor: '#3085d6', + reverseButtons: true, + confirmButtonText: 'Undo' + }) + .then( status => { + if (status.isConfirmed) { + $.ajax({ + method: 'PATCH', + url: `http://127.0.0.1:3000/todos/${id}`, + headers: { + access_token, + userlocation + }, + data: { + status: false + } + }) + .done( () => { + refreshHome() + Toast.fire({ + title: `Berhasil!`, + text: `Kamu berhasil Mengembalikan Status Todo dengan id ${id}`, + icon: `success` + }) + }) } }) } \ No newline at end of file diff --git a/client/js/home.js b/client/js/home.js index 44b337c..372e9a5 100644 --- a/client/js/home.js +++ b/client/js/home.js @@ -24,7 +24,8 @@ const fetchTodo = _ => { }) .done(data => { $('.weather').empty() - $('#todos-tabledata').empty() + $('#todos-pending').empty() + $('#todos-finished').empty() new Promise( res => { const todayWeather = data.todayWeather const todos = data.todos @@ -35,41 +36,58 @@ const fetchTodo = _ => {

` $('.weather').append(weather) - todos.forEach( todo => { let todostatus = todo.status - if (todostatus) { - todostatus = ` - - Done - - ` + if (!todostatus) { + $('#todos-pending').append(` + + ${todo.id} + +
+
+ ${todo.title} +
+
+ + ${todo.description} + ${todo.due_date} + + + Pending + + + + + + + + + `) } else { - todostatus = ` - - Pending - - ` + $('#todos-finished').append(` + + ${todo.id} + +
+
+ ${todo.title} +
+
+ + ${todo.description} + ${todo.due_date} + + + Done + + + + + + + + `) } - $('#todos-tabledata').append(` - - -
-
- ${todo.title} -
-
- - ${todo.description} - ${todo.due_date} - ${todostatus} - - - - - - - `) // ! FIX todo.due_date ON SERVER!!! CANNOT GET IN EDIT! // ${todo.id}, ${todo.title}, ${todo.description}, ${todo.status} }) res() From 2cec6bfc74d3108765d736776ff23b0b567e25c8 Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 03:48:47 +0700 Subject: [PATCH 04/14] Added Weather Icon and Convert Temperature --- server/controllers/todo-controller.js | 4 +++- server/helpers/get-weather.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/server/controllers/todo-controller.js b/server/controllers/todo-controller.js index 7bb5009..8425912 100644 --- a/server/controllers/todo-controller.js +++ b/server/controllers/todo-controller.js @@ -26,8 +26,10 @@ class Controller { const whoIsLoggedIn = req.whoIsLoggedIn const userlocation = req.userLocation - const findTodos = await Todo.findAll({ where: { UserId: whoIsLoggedIn.id }}) + const findTodos = await Todo.findAll({ where: { UserId: whoIsLoggedIn.id }, order: [['due_date', 'ASC']]}) const todayWeather = await checkWeatherByUserLocation(userlocation) + todayWeather.temperature.temp = (todayWeather.temperature.temp - 273.15).toFixed(1) + const todos = [] findTodos.forEach( todo => { todo.dataValues.due_date = convertISOtoDate(todo.dataValues.due_date) diff --git a/server/helpers/get-weather.js b/server/helpers/get-weather.js index 9224022..55a372e 100644 --- a/server/helpers/get-weather.js +++ b/server/helpers/get-weather.js @@ -13,8 +13,10 @@ const checkWeatherByIp = location => { weatherData.push(data.main) }) weatherData = weatherData.join(', ') + + const icon = `http://openweathermap.org/img/wn/${weather.data.weather.icon}@2x.png` - res({ location: weather.data.name, weather: weatherData, temperature: weather.data.main }) + res({ location: weather.data.name, weather: weatherData, temperature: weather.data.main, icon }) }) .catch( err => { rej({ name: 'Failed to fetch Weather from Server' }) From 8756aa9d96bf3168670788dbd418306e9919067c Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 03:50:53 +0700 Subject: [PATCH 05/14] Added Weather Icon on Client --- client/js/home.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/js/home.js b/client/js/home.js index 372e9a5..341f80f 100644 --- a/client/js/home.js +++ b/client/js/home.js @@ -30,6 +30,7 @@ const fetchTodo = _ => { const todayWeather = data.todayWeather const todos = data.todos const weather = ` +

You are currently in ${todayWeather.location}
Current Weather is ${todayWeather.weather} with temperature ${todayWeather.temperature.temp} Degree From 5871c5b13f0bfa736a7f543c0b9a8473b4f3b001 Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 03:53:42 +0700 Subject: [PATCH 06/14] Fixed Weather Icon from Server --- server/helpers/get-weather.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/helpers/get-weather.js b/server/helpers/get-weather.js index 55a372e..920e314 100644 --- a/server/helpers/get-weather.js +++ b/server/helpers/get-weather.js @@ -14,7 +14,7 @@ const checkWeatherByIp = location => { }) weatherData = weatherData.join(', ') - const icon = `http://openweathermap.org/img/wn/${weather.data.weather.icon}@2x.png` + const icon = `http://openweathermap.org/img/wn/${weather.data.weather[0].icon}@2x.png`; res({ location: weather.data.name, weather: weatherData, temperature: weather.data.main, icon }) }) From ce939d4c431de873e6762e336c3643746a0a64b1 Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 03:54:56 +0700 Subject: [PATCH 07/14] Fixed Weather Icon on Client --- client/js/home.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/js/home.js b/client/js/home.js index 341f80f..dbab28f 100644 --- a/client/js/home.js +++ b/client/js/home.js @@ -32,8 +32,8 @@ const fetchTodo = _ => { const weather = `

- You are currently in ${todayWeather.location}
- Current Weather is ${todayWeather.weather} with temperature ${todayWeather.temperature.temp} Degree + You are currently in ${todayWeather.location}
+ Current Weather is ${todayWeather.weather} with temperature ${todayWeather.temperature.temp} Celcius

` $('.weather').append(weather) From ee7859350b39e4b37593d6ae744121f6d3e6c014 Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 10:15:04 +0700 Subject: [PATCH 08/14] Added Add Todo Button in Client --- client/css/style.css | 1 - client/index.html | 19 +++++++++- client/index.js | 87 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/client/css/style.css b/client/css/style.css index 719edea..337dfe5 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -1603,7 +1603,6 @@ a.btn-icon-only { .footer { padding: 2.5rem 0; - background: #f7fafc; } .footer .copyright { diff --git a/client/index.html b/client/index.html index fa728fe..e42fd9c 100644 --- a/client/index.html +++ b/client/index.html @@ -87,7 +87,7 @@

Hey Kamu!

Beranda (current) - +
@@ -96,8 +96,15 @@

Hey Kamu!

-
+
+
+ +
+
+
+ +
@@ -152,6 +159,14 @@

Finished Todos

+
+
+
+ +
+
+
diff --git a/client/index.js b/client/index.js index 0df0bcc..21664fa 100644 --- a/client/index.js +++ b/client/index.js @@ -158,6 +158,11 @@ const deleteTodo = id => { const editTodo = (id, title, description, due_date) => { const access_token = localStorage.getItem('access_token') const userlocation = localStorage.getItem('userlocation') + + const judul = title || '' + const deskripsi = description || '' + const tanggal = due_date || '' + Swal.fire({ title: `Edit Todo`, html: ` @@ -181,7 +186,7 @@ const editTodo = (id, title, description, due_date) => { } }) .then( status => { - if (status.isConfirmed) { // ! WAIT FROM SERVER-SIDE (on-progress) + if (status.isConfirmed) { const data = { title: status.value.title, description: status.value.description, @@ -205,6 +210,16 @@ const editTodo = (id, title, description, due_date) => { icon: `success` }) }) + .fail(err => { + Swal.fire({ + title: 'Oops...', + text: err.responseJSON.msg, + icon: 'error' + }) + .then( () => { + editTodo(id, data.title, data.description, data.due_date) + }) + }) } }) } @@ -283,4 +298,74 @@ const markUndoneTodo = id => { }) } }) +} + +// * Add Todo (Put) +const addTodo = (title, description, due_date) => { + const access_token = localStorage.getItem('access_token') + const userlocation = localStorage.getItem('userlocation') + + const judul = title || '' + const deskripsi = description || '' + const tanggal = due_date || '' + + Swal.fire({ + title: `Add Todo`, + html: ` + + + + `, + focusConfirm: false, + showCancelButton: true, + confirmButtonColor: '#3085d6', + reverseButtons: true, + confirmButtonText: 'Add', + preConfirm : _ => { + const title = Swal.getPopup().querySelector('#title').value + const description = Swal.getPopup().querySelector('#description').value + const due_date = Swal.getPopup().querySelector('#due_date').value + if (!title || !description || !due_date) { + Swal.showValidationMessage(`Tolong lengkapi semua kolom`) + } + return { title, description, due_date, status: false } + } + }) + .then( status => { + if (status.isConfirmed) { + const data = { + title: status.value.title, + description: status.value.description, + due_date: status.value.due_date + } + + $.ajax({ + method: 'POST', + url: `http://127.0.0.1:3000/todos/`, + headers: { + access_token, + userlocation + }, + data + }) + .done( () => { + refreshHome() + Toast.fire({ + title: `Berhasil!`, + text: `Kamu berhasil Menambahkan Todo`, + icon: `success` + }) + }) + .fail(err => { + Swal.fire({ + title: 'Oops...', + text: err.responseJSON.msg, + icon: 'error' + }) + .then( () => { + addTodo(data.title, data.description, data.due_date) + }) + }) + } + }) } \ No newline at end of file From 320ff6679ee3298be7043919eb9256a2a903f1ed Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 11:43:01 +0700 Subject: [PATCH 09/14] Github Oauth Perfectly Done --- client/github-callback.html | 25 ++++++++++++++ client/index.html | 4 +-- client/js/home.js | 2 +- client/js/loginregister.js | 35 +++++++++++++++++++ server/controllers/user-controller.js | 49 ++++++++++++++++++++++++++- server/routes/userRouter.js | 3 ++ 6 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 client/github-callback.html diff --git a/client/github-callback.html b/client/github-callback.html new file mode 100644 index 0000000..5117655 --- /dev/null +++ b/client/github-callback.html @@ -0,0 +1,25 @@ + + + + + + Github Callback + + +

One moment please...

+

Redirecting you back to website...

+ + + + \ No newline at end of file diff --git a/client/index.html b/client/index.html index e42fd9c..cb9b8e6 100644 --- a/client/index.html +++ b/client/index.html @@ -30,7 +30,7 @@

Daftar Akun

@@ -44,7 +44,7 @@

Masuk FancyTodo

diff --git a/client/js/home.js b/client/js/home.js index dbab28f..a2e135f 100644 --- a/client/js/home.js +++ b/client/js/home.js @@ -110,7 +110,6 @@ const fetchTodo = _ => { $('.go-home').on('click', e => { e.preventDefault() - console.log('kepencet'); showHome() }) @@ -129,6 +128,7 @@ const fetchTodo = _ => { }) .then( button => { if (button.isConfirmed) { + container.classList.remove("right-panel-active") showLoginRegister() localStorage.removeItem('access_token') Toast.fire({ diff --git a/client/js/loginregister.js b/client/js/loginregister.js index 2647a4f..8371264 100644 --- a/client/js/loginregister.js +++ b/client/js/loginregister.js @@ -77,4 +77,39 @@ const login = _ => { }) }) }) + + $('#github-login').on('click', e => { + e.preventDefault() + githubLoginRegister() + }) + + $('#github-register').on('click', e => { + e.preventDefault() + githubLoginRegister() + }) + + const githubLoginRegister = _ => { + const loginPage = window.open('https://github.com/login/oauth/authorize?client_id=31b94fb7d76b5c38f316') + let check = setInterval( _ => { + if(loginPage.closed) { + clearInterval(check) + const access_token = localStorage.getItem('access_token') + const email = localStorage.getItem('email') + + if (access_token) { + Toast.fire({ + icon: 'success', + title: `Haloo ${email}`, + text: 'Kamu berhasil login via Github!' + }) + showHome() + } else { + Swal.fire({ + title: 'Oops...', + text: 'Login via github gagal :(' + }) + } + } + }, 1000) + } } diff --git a/server/controllers/user-controller.js b/server/controllers/user-controller.js index 4a6cc5c..a6f0a7f 100644 --- a/server/controllers/user-controller.js +++ b/server/controllers/user-controller.js @@ -1,6 +1,7 @@ const { User } = require('../models') const { verifyPassword } = require('../helpers/bcrypt') const { jwtSign } = require('../helpers/jwt') +const axios = require('axios').default class UserController { static async postUserRegister(req, res, next) { @@ -27,7 +28,6 @@ class UserController { if (verify) { const payload = { id: user.id, email } const access_token = jwtSign(payload) - res.status(200).json({ access_token }) } else { throw { name: 'Email atau Password salah' } @@ -39,6 +39,53 @@ class UserController { next(error) } } + + static async githubLogin(req, res, next) { + try { + const requestToken = req.query.code + const getAccessToken = await axios({ + method: 'POST', + url: `https://github.com/login/oauth/access_token?code=${requestToken}&client_id=31b94fb7d76b5c38f316&client_secret=2fa414f669d4a2f137633555c24fe02957dfca97` + }) + + const github_access_token = getAccessToken.data.substring(getAccessToken.data.indexOf('=') + 1, getAccessToken.data.indexOf('&')) + + const githubUser = await axios({ + method: 'GET', + url: 'https://api.github.com/user', + headers: { + Authorization: `token ${github_access_token}` + } + }) + const email = githubUser.data.email || `${githubUser.data.id}_fancytodo@github.com` + const password = githubUser.data.node_id.substring(0, 15) + + const user = await User.findOne({ where: { email } }) + if (user) { + const verify = verifyPassword(password, user.password) + if (verify) { + const payload = { id: user.id, email } + const access_token = jwtSign(payload) + + res.status(200).json({ access_token, email }) + } else { + throw { name: 'Login via github gagal' } + } + } else { + const newGithubUser = { + email, + password + } + const newUser = await User.create(newGithubUser) + const payload = { id: newUser.id, email: newUser.email } + const access_token = jwtSign(payload) + + res.status(200).json({ access_token, email }) + } + } catch (error) { + next(error) + } + } } module.exports = UserController \ No newline at end of file diff --git a/server/routes/userRouter.js b/server/routes/userRouter.js index 8be45d7..b4557bb 100644 --- a/server/routes/userRouter.js +++ b/server/routes/userRouter.js @@ -5,4 +5,7 @@ const UserController = require('../controllers/user-controller') router.post('/login', UserController.postUserLogin) router.post('/register', UserController.postUserRegister) +// * Oauth Login Router +router.post('/githublogin', UserController.githubLogin) + module.exports = router \ No newline at end of file From b05a4b5dd0fc7480eee2bff01979d4366181664a Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 11:53:32 +0700 Subject: [PATCH 10/14] Hiding API-Key --- server/controllers/user-controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/controllers/user-controller.js b/server/controllers/user-controller.js index a6f0a7f..4bc1860 100644 --- a/server/controllers/user-controller.js +++ b/server/controllers/user-controller.js @@ -45,14 +45,14 @@ class UserController { const requestToken = req.query.code const getAccessToken = await axios({ method: 'POST', - url: `https://github.com/login/oauth/access_token?code=${requestToken}&client_id=31b94fb7d76b5c38f316&client_secret=2fa414f669d4a2f137633555c24fe02957dfca97` + url: `${process.env.PRIVATE_GITHUB_URL}?code=${requestToken}&client_id=${process.env.PRIVATE_GITHUB_CLIENT_ID}&client_secret=${process.env.PRIVATE_GITHUB_CLIENT_SECRET}` }) const github_access_token = getAccessToken.data.substring(getAccessToken.data.indexOf('=') + 1, getAccessToken.data.indexOf('&')) const githubUser = await axios({ method: 'GET', - url: 'https://api.github.com/user', + url: process.env.PRIVATE_GITHUB_API_URL, headers: { Authorization: `token ${github_access_token}` } From 64cf9ea236258ef0caffb0b1762b61c86b4034e1 Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 12:03:13 +0700 Subject: [PATCH 11/14] Added gitignore and Fixed Login GetLocation --- .gitignore | 1 + client/index.js | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a7aa70 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +client_firebase \ No newline at end of file diff --git a/client/index.js b/client/index.js index 21664fa..9917780 100644 --- a/client/index.js +++ b/client/index.js @@ -59,10 +59,14 @@ const getLocation = _ => { title: 'Tunggu!', text: 'Aku butuh akses lokasi kamu nih', icon: 'info', - buttons: ['Nggak Boleh', 'Boleh Dong'] + showCancelButton: true, + confirmButtonColor: '#3085d6', + reverseButtons: true, + cancelButtonText: 'Gak Boleh', + confirmButtonText: 'Boleh Dong' }) .then( akses => { - if(akses) { + if(akses.isConfirmed) { navigator.geolocation.getCurrentPosition(success, error) } else { error() @@ -166,9 +170,9 @@ const editTodo = (id, title, description, due_date) => { Swal.fire({ title: `Edit Todo`, html: ` - - - + + + `, focusConfirm: false, showCancelButton: true, From d02463774687c285930f981d404d4fb823194e76 Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 12:13:39 +0700 Subject: [PATCH 12/14] Fixed User email on Logout --- client/js/home.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/js/home.js b/client/js/home.js index a2e135f..c055797 100644 --- a/client/js/home.js +++ b/client/js/home.js @@ -131,6 +131,7 @@ const fetchTodo = _ => { container.classList.remove("right-panel-active") showLoginRegister() localStorage.removeItem('access_token') + localStorage.removeItem('email') Toast.fire({ icon: 'success', title: 'Berhasil Keluar' From 6edaf4a5f1f728b5bd71c9906dd07bb1ca099486 Mon Sep 17 00:00:00 2001 From: Muhammad Akbar Habiby Khalid Date: Sun, 1 Nov 2020 12:16:10 +0700 Subject: [PATCH 13/14] Fixed HTTP or 80 Request to openweathermap --- server/helpers/get-weather.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/helpers/get-weather.js b/server/helpers/get-weather.js index 920e314..fe5d104 100644 --- a/server/helpers/get-weather.js +++ b/server/helpers/get-weather.js @@ -14,7 +14,7 @@ const checkWeatherByIp = location => { }) weatherData = weatherData.join(', ') - const icon = `http://openweathermap.org/img/wn/${weather.data.weather[0].icon}@2x.png`; + const icon = `https://openweathermap.org/img/wn/${weather.data.weather[0].icon}@2x.png`; res({ location: weather.data.name, weather: weatherData, temperature: weather.data.main, icon }) }) From fa66332639eee9cdd728d7025bd4df266e8c2687 Mon Sep 17 00:00:00 2001 From: akbarhabiby Date: Sun, 1 Nov 2020 14:52:32 +0700 Subject: [PATCH 14/14] Finalizing --- client/index.html | 7 +- client/js/home.js | 3 + client/js/loginregister.js | 51 ++++++ server/controllers/user-controller.js | 43 +++++ server/package-lock.json | 224 ++++++++++++++++++++++++++ server/package.json | 1 + server/routes/userRouter.js | 1 + 7 files changed, 326 insertions(+), 4 deletions(-) diff --git a/client/index.html b/client/index.html index cb9b8e6..ee51dbc 100644 --- a/client/index.html +++ b/client/index.html @@ -28,8 +28,7 @@