commit
0db78661bb
658
css/main.css
658
css/main.css
@ -17,29 +17,29 @@ body {
|
|||||||
body {
|
body {
|
||||||
--accent-color: #256eff;
|
--accent-color: #256eff;
|
||||||
--text-color: 20, 20, 20;
|
--text-color: 20, 20, 20;
|
||||||
--background-color: 240, 240, 240;
|
--foreground-color: 252, 253, 255;
|
||||||
--foreground-color: 250, 250, 250;
|
--background-color: 241, 243, 248;
|
||||||
--danger-color: rgb(255, 75, 75);
|
--danger-color: rgb(255, 75, 75);
|
||||||
--green: #1cad59;
|
--green: #1cad59;
|
||||||
|
--yellow: rgb(220, 165, 0);
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
color: rgba(var(--text-color), 1);
|
color: rgba(var(--text-color), 1);
|
||||||
background-color: rgba(var(--background-color), 1);
|
background-color: rgba(var(--background-color), 1);
|
||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body[data-theme=dark] {
|
body[data-theme=dark] {
|
||||||
--accent-color: #86afff;
|
--accent-color: #90b8f8;
|
||||||
--text-color: 220, 220, 220;
|
--text-color: 220, 220, 220;
|
||||||
--background-color: 10, 10, 10;
|
--foreground-color: 27, 28, 29;
|
||||||
--foreground-color: 24, 24, 24;
|
--background-color: 21, 22, 22;
|
||||||
--danger-color: rgb(255, 106, 106);
|
--danger-color: rgb(255, 106, 106);
|
||||||
--green: #00e676;
|
--green: #00e676;
|
||||||
|
--yellow: rgb(255, 213, 5);
|
||||||
}
|
}
|
||||||
body[data-theme=dark] sm-popup::part(popup) {
|
body[data-theme=dark] sm-popup::part(popup) {
|
||||||
background-color: rgba(var(--foreground-color), 1);
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
@ -64,6 +64,13 @@ a {
|
|||||||
a:focus-visible {
|
a:focus-visible {
|
||||||
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
|
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
|
||||||
}
|
}
|
||||||
|
a.button {
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
@ -95,23 +102,21 @@ button:not(:disabled) {
|
|||||||
.button {
|
.button {
|
||||||
background-color: rgba(var(--text-color), 0.06);
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
}
|
}
|
||||||
|
.button--primary, .button--danger {
|
||||||
.button--primary,
|
|
||||||
.button--danger {
|
|
||||||
color: rgba(var(--background-color), 1);
|
color: rgba(var(--background-color), 1);
|
||||||
}
|
}
|
||||||
.button--primary .icon,
|
.button--primary .icon, .button--danger .icon {
|
||||||
.button--danger .icon {
|
|
||||||
fill: rgba(var(--background-color), 1);
|
fill: rgba(var(--background-color), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button--primary {
|
.button--primary {
|
||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button--danger {
|
.button--danger {
|
||||||
background-color: var(--danger-color);
|
background-color: var(--danger-color);
|
||||||
}
|
}
|
||||||
|
.button--small {
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
.cta {
|
.cta {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@ -177,7 +182,7 @@ sm-textarea button .icon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sm-button {
|
sm-button {
|
||||||
--padding: 0.6rem 0.8rem;
|
--padding: 0.8rem;
|
||||||
}
|
}
|
||||||
sm-button[variant=primary] .icon {
|
sm-button[variant=primary] .icon {
|
||||||
fill: rgba(var(--background-color), 1);
|
fill: rgba(var(--background-color), 1);
|
||||||
@ -199,6 +204,10 @@ sm-form {
|
|||||||
--gap: 1rem;
|
--gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sm-select {
|
||||||
|
--padding: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
strip-select {
|
strip-select {
|
||||||
--gap: 0;
|
--gap: 0;
|
||||||
background-color: rgba(var(--text-color), 0.06);
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
@ -239,27 +248,6 @@ ul {
|
|||||||
grid-column: 1/-1;
|
grid-column: 1/-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h1 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h2 {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h4 {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h5 {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uppercase {
|
.uppercase {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
@ -338,7 +326,7 @@ h3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.justify-start {
|
.justify-start {
|
||||||
justify-content: start;
|
justify-items: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.justify-center {
|
.justify-center {
|
||||||
@ -458,6 +446,36 @@ h3 {
|
|||||||
fill: var(--accent-color);
|
fill: var(--accent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.page__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.page__header .grid {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
.page__header h1 {
|
||||||
|
margin-top: auto;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
.page__header .illustration {
|
||||||
|
height: 8rem;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-layout {
|
||||||
|
display: grid;
|
||||||
|
gap: 1.5rem 0;
|
||||||
|
grid-template-columns: 1.5rem minmax(0, 1fr) 1.5rem;
|
||||||
|
align-content: flex-start;
|
||||||
|
}
|
||||||
|
.page-layout > * {
|
||||||
|
grid-column: 2/3;
|
||||||
|
}
|
||||||
|
|
||||||
#confirmation_popup,
|
#confirmation_popup,
|
||||||
#prompt_popup {
|
#prompt_popup {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -492,7 +510,6 @@ h3 {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 1.5rem;
|
padding: 0 1.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-auto-flow: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__header__close {
|
.popup__header__close {
|
||||||
@ -501,24 +518,88 @@ h3 {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flo-icon {
|
||||||
|
margin-right: 0.3rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
width: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#secondary_pages {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#secondary_pages header {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
#secondary_pages .inner-page {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#landing > section {
|
||||||
|
justify-content: center;
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
padding: 8vw 0;
|
||||||
|
}
|
||||||
|
#landing h1 {
|
||||||
|
font-size: clamp(2rem, 5vw, 5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sign_in,
|
||||||
|
#sign_up {
|
||||||
|
justify-items: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
#sign_in section,
|
||||||
|
#sign_up section {
|
||||||
|
margin-top: -8rem;
|
||||||
|
width: min(24rem, 100%);
|
||||||
|
}
|
||||||
|
#sign_in sm-form,
|
||||||
|
#sign_up sm-form {
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sign_up .h2 {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
#sign_up .card {
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
#sign_up h5 {
|
||||||
|
font-weight: 500;
|
||||||
|
color: rgba(var(--text-color), 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#flo_id_warning {
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
border-bottom: thin solid rgba(var(--text-color), 0.3);
|
||||||
|
}
|
||||||
|
#flo_id_warning .icon {
|
||||||
|
height: 4rem;
|
||||||
|
width: 4rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: #ffc107;
|
||||||
|
border-radius: 3rem;
|
||||||
|
fill: rgba(0, 0, 0, 0.8);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
#main_header {
|
#main_header {
|
||||||
padding: 1rem 1.5rem;
|
padding: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main_card {
|
#main_card {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: rgba(var(--foreground-color), 1);
|
|
||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pages_container {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main_navbar {
|
#main_navbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
background: rgba(var(--text-color), 0.03);
|
background: rgba(var(--text-color), 0.03);
|
||||||
@ -545,15 +626,15 @@ h3 {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0.5rem 0.3rem;
|
padding: 0.5rem 0.3rem;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
font-size: 0.7rem;
|
font-size: 0.8rem;
|
||||||
border-radius: 0.3rem;
|
border-radius: 0.3rem;
|
||||||
}
|
}
|
||||||
.nav-item .icon {
|
.nav-item .icon {
|
||||||
transition: transform 0.2s;
|
transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
}
|
}
|
||||||
.nav-item__title {
|
.nav-item__title {
|
||||||
margin-top: 0.3rem;
|
margin-top: 0.3rem;
|
||||||
transition: opacity 0.2s, transform 0.2s;
|
transition: opacity 0.2s, transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
}
|
}
|
||||||
.nav-item--active {
|
.nav-item--active {
|
||||||
color: var(--accent-color);
|
color: var(--accent-color);
|
||||||
@ -575,6 +656,30 @@ h3 {
|
|||||||
border-radius: 1rem 1rem 0 0;
|
border-radius: 1rem 1rem 0 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
.nav-item .badge {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
content: attr(data-notifications);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0.3rem;
|
||||||
|
background: var(--accent-color);
|
||||||
|
color: rgba(var(--background-color), 1);
|
||||||
|
aspect-ratio: 1/1;
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
margin: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-page {
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
align-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
.password-field label {
|
.password-field label {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -598,58 +703,230 @@ h3 {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clip {
|
#home {
|
||||||
-webkit-clip-path: circle(0);
|
padding: 0;
|
||||||
clip-path: circle(0);
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user,
|
||||||
|
#cashier {
|
||||||
|
position: relative;
|
||||||
|
gap: 2rem;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: 5rem;
|
||||||
|
align-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#quick_actions_container {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-action {
|
.primary-action {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0.8rem 1rem;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.8rem;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
background-color: transparent;
|
font-weight: 400;
|
||||||
border: thin solid rgba(var(--text-color), 0.3);
|
background-color: rgba(var(--text-color), 0.03);
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
.primary-action .icon {
|
.primary-action .icon {
|
||||||
fill: var(--accent-color);
|
fill: var(--accent-color);
|
||||||
}
|
}
|
||||||
.primary-action:not(:last-of-type) {
|
|
||||||
margin-right: 0.5rem;
|
#rupee_balance span:first-of-type {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
#rupee_balance span:last-of-type {
|
||||||
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page {
|
.wallet-action {
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow-y: auto;
|
|
||||||
align-content: flex-start;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#wallet_section {
|
|
||||||
background-color: rgba(var(--text-color), 0.03);
|
background-color: rgba(var(--text-color), 0.03);
|
||||||
border-radius: 0.5rem;
|
flex: 1;
|
||||||
padding: 1.5rem;
|
}
|
||||||
|
.wallet-action:nth-of-type(2) {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
.wallet-action .icon {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
fill: var(--accent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#transactions_list {
|
#saved_ids_list {
|
||||||
flex-direction: column;
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved-id {
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
gap: 0 0.8rem;
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
padding: 0.8rem 0;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.saved-id.highlight {
|
||||||
|
box-shadow: 0 0 0.1rem 0.1rem var(--accent-color) inset;
|
||||||
|
}
|
||||||
|
.saved-id .edit-saved {
|
||||||
|
grid-area: 1/1/3/2;
|
||||||
|
padding: 0.3rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.saved-id .edit-saved .icon {
|
||||||
|
position: absolute;
|
||||||
|
height: 1.2rem;
|
||||||
|
width: 1.2rem;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 0.2rem;
|
||||||
|
background-color: rgba(var(--background-color), 1);
|
||||||
|
}
|
||||||
|
.saved-id__initials {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 2.4rem;
|
||||||
|
width: 2.4rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(var(--background-color), 1);
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
justify-self: flex-start;
|
||||||
|
border-radius: 2rem;
|
||||||
|
}
|
||||||
|
.saved-id__title {
|
||||||
|
align-self: flex-end;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.saved-id__flo-id {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#contact {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
#contact > * {
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
}
|
||||||
|
#contact > :first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
#contact > :last-child {
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
background-color: rgba(var(--text-color), 0.03);
|
||||||
|
}
|
||||||
|
#contact > :last-child button {
|
||||||
|
padding: 0.8rem 2rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
color: var(--accent-color);
|
||||||
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#contact__transactions {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 max(1rem, 8vw) 1rem max(1rem, 8vw);
|
||||||
|
align-content: flex-start;
|
||||||
|
}
|
||||||
|
#contact__transactions sm-spinner {
|
||||||
|
position: absolute;
|
||||||
|
justify-self: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-message {
|
||||||
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
justify-self: flex-start;
|
||||||
|
border-radius: 0 1rem 1rem 1rem;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.transaction-message.received {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
color: rgba(var(--background-color), 1);
|
||||||
|
}
|
||||||
|
.transaction-message.received + .transaction-message.received {
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
.transaction-message.sent {
|
||||||
|
margin-left: auto;
|
||||||
|
justify-self: flex-end;
|
||||||
|
border-radius: 1rem 1rem 0 1rem;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.transaction-message__amount {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
.transaction-message__time {
|
||||||
|
opacity: 0.8;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wallet_history_wrapper {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
padding-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#payments_history {
|
||||||
|
display: grid;
|
||||||
|
gap: 2rem;
|
||||||
padding-bottom: 4rem;
|
padding-bottom: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transaction {
|
.transaction {
|
||||||
grid-template-columns: auto 1fr auto;
|
grid-template-columns: auto 1fr auto;
|
||||||
gap: 0.5rem 1rem;
|
gap: 0.5rem 1rem;
|
||||||
padding: 0.8rem;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: rgba(var(--text-color), 0.03);
|
|
||||||
border-radius: 0.3rem;
|
|
||||||
}
|
}
|
||||||
.transaction:not(:last-of-type) {
|
.transaction.sent .icon {
|
||||||
margin-bottom: 0.5rem;
|
fill: rgba(var(--text-color), 0.8);
|
||||||
|
}
|
||||||
|
.transaction.sent .transaction__amount {
|
||||||
|
color: rgba(var(--text-color), 0.8);
|
||||||
|
}
|
||||||
|
.transaction.sent .transaction__amount::before {
|
||||||
|
content: "- ";
|
||||||
|
}
|
||||||
|
.transaction.received .icon {
|
||||||
|
fill: var(--green);
|
||||||
|
}
|
||||||
|
.transaction.received .transaction__amount {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
.transaction.received .transaction__amount::before {
|
||||||
|
content: "+ ";
|
||||||
}
|
}
|
||||||
.transaction__icon {
|
.transaction__icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -659,15 +936,10 @@ h3 {
|
|||||||
width: 2.5rem;
|
width: 2.5rem;
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
background-color: rgba(var(--text-color), 0.03);
|
background-color: rgba(var(--text-color), 0.03);
|
||||||
border-radius: 0.5rem;
|
border-radius: 2rem;
|
||||||
}
|
|
||||||
.transaction__icon .icon {
|
|
||||||
fill: var(--accent-color);
|
|
||||||
}
|
}
|
||||||
.transaction__receiver {
|
.transaction__receiver {
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: rgba(var(--text-color), 0.8);
|
|
||||||
}
|
}
|
||||||
.transaction__time {
|
.transaction__time {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
@ -678,12 +950,6 @@ h3 {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
grid-area: 1/3/3/4;
|
grid-area: 1/3/3/4;
|
||||||
}
|
}
|
||||||
.transaction__amount.sent::before {
|
|
||||||
content: "-";
|
|
||||||
}
|
|
||||||
.transaction__amount.received::before {
|
|
||||||
content: "+";
|
|
||||||
}
|
|
||||||
|
|
||||||
.fab {
|
.fab {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -699,46 +965,33 @@ h3 {
|
|||||||
background-color: rgba(var(--foreground-color), 1);
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#transaction_result {
|
#add_address_button {
|
||||||
display: grid;
|
border-radius: 0.5rem;
|
||||||
gap: 0.5rem;
|
color: rgba(var(--background-color), 1);
|
||||||
height: max(40vh, 24rem);
|
background-color: var(--accent-color);
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
align-content: center;
|
|
||||||
}
|
}
|
||||||
#transaction_result.success .icon--failed {
|
#add_address_button .icon {
|
||||||
display: none;
|
fill: rgba(var(--background-color), 1);
|
||||||
}
|
}
|
||||||
#transaction_result.failed .icon--success {
|
|
||||||
display: none;
|
.user-action-result__icon {
|
||||||
}
|
|
||||||
#transaction_result h3 {
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
#transaction_result .icon {
|
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
border-radius: 5rem;
|
border-radius: 5rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 2rem;
|
||||||
-webkit-animation: popup 1s;
|
-webkit-animation: popup 1s;
|
||||||
animation: popup 1s;
|
animation: popup 1s;
|
||||||
}
|
}
|
||||||
#transaction_result .icon--success {
|
.user-action-result__icon.success {
|
||||||
fill: rgba(var(--background-color), 1);
|
fill: rgba(var(--background-color), 1);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background-color: #0bbe56;
|
background-color: #0bbe56;
|
||||||
}
|
}
|
||||||
#transaction_result .icon--failed {
|
.user-action-result__icon.failed {
|
||||||
background-color: rgba(var(--text-color), 0.03);
|
background-color: rgba(var(--text-color), 0.03);
|
||||||
fill: var(--danger-color);
|
fill: var(--danger-color);
|
||||||
}
|
}
|
||||||
#transaction_result sm-copy {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes popup {
|
@-webkit-keyframes popup {
|
||||||
0% {
|
0% {
|
||||||
@ -780,65 +1033,195 @@ h3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.cashier-request,
|
.cashier-request,
|
||||||
.wallet-request,
|
|
||||||
.payment-request {
|
.payment-request {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0.8rem;
|
padding: 0.8rem;
|
||||||
border-radius: 0.3rem;
|
border-radius: 0.3rem;
|
||||||
background-color: rgba(var(--text-color), 0.06);
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
}
|
}
|
||||||
.cashier-request:not(:last-of-type),
|
.cashier-request:not(:last-of-type),
|
||||||
.wallet-request:not(:last-of-type),
|
|
||||||
.payment-request:not(:last-of-type) {
|
.payment-request:not(:last-of-type) {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
.cashier-request__time,
|
.cashier-request__time,
|
||||||
.wallet-request__time,
|
|
||||||
.payment-request__time {
|
.payment-request__time {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#payment_request_history {
|
||||||
|
padding-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
.payment-request {
|
.payment-request {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
gap: 0.5rem 1rem;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
|
color: rgba(var(--text-color), 1);
|
||||||
|
}
|
||||||
|
.payment-request__requestor {
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.payment-request__amount {
|
.payment-request__amount {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.payment-request__status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
.payment-request__status .icon {
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
margin-left: 0.3rem;
|
||||||
|
}
|
||||||
|
.payment-request .icon.paid {
|
||||||
|
fill: var(--green);
|
||||||
|
}
|
||||||
|
.payment-request .icon.declined {
|
||||||
|
fill: var(--danger-color);
|
||||||
|
}
|
||||||
|
.payment-request .button {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0.6rem 0.8rem;
|
||||||
|
color: var(--accent-color);
|
||||||
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet-request {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem 1rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
}
|
||||||
|
.wallet-request:not(.rejected, .pending).withdrawn .wallet-request__amount::before {
|
||||||
|
content: "- ";
|
||||||
|
}
|
||||||
|
.wallet-request:not(.rejected, .pending).added .wallet-request__amount {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
.wallet-request:not(.rejected, .pending).added .wallet-request__amount::before {
|
||||||
|
content: "+ ";
|
||||||
|
}
|
||||||
|
.wallet-request .icon.pending {
|
||||||
|
fill: var(--yellow);
|
||||||
|
}
|
||||||
|
.wallet-request .icon.failed {
|
||||||
|
fill: var(--danger-color);
|
||||||
|
}
|
||||||
|
.wallet-request__icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
grid-area: 1/1/3/2;
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
border-radius: 2rem;
|
||||||
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
|
}
|
||||||
|
.wallet-request__icon .icon {
|
||||||
|
fill: var(--accent-color);
|
||||||
|
}
|
||||||
|
.wallet-request__details {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.wallet-request__details, .wallet-request__amount {
|
||||||
|
color: rgba(var(--text-color), 1);
|
||||||
|
}
|
||||||
|
.wallet-request__amount {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.wallet-request__time, .wallet-request__status {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: rgba(var(--text-color), 0.8);
|
||||||
|
}
|
||||||
|
.wallet-request__status {
|
||||||
|
text-transform: capitalize;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.wallet-request__status .icon {
|
||||||
|
margin-left: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#transaction__remark,
|
||||||
|
#transaction__note {
|
||||||
|
line-height: 1.6;
|
||||||
|
justify-self: flex-start;
|
||||||
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#transaction__note .icon {
|
||||||
|
fill: var(--danger-color);
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#saved_upi_ids_list {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
width: min(24rem, 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved-upi {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.4rem 0.4rem 0.4rem 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 40rem) {
|
||||||
|
#main_navbar.hide-away {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@media screen and (min-width: 40rem) {
|
@media screen and (min-width: 40rem) {
|
||||||
sm-popup {
|
sm-popup {
|
||||||
--width: 24rem;
|
--width: 24rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-layout {
|
||||||
|
grid-template-columns: 1fr 90vw 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.popup__header {
|
.popup__header {
|
||||||
grid-column: 1/-1;
|
grid-column: 1/-1;
|
||||||
padding: 1rem 1.5rem 0 1.5rem;
|
padding: 1rem 1.5rem 0 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
#main_card {
|
#main_card {
|
||||||
display: grid;
|
height: calc(100vh - 3rem);
|
||||||
grid-template-columns: auto 1fr;
|
width: calc(100vw - 3rem);
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-areas: "header" ".";
|
||||||
grid-template-areas: "nav header" "nav main";
|
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background-color: rgba(var(--background-color), 1);
|
||||||
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.05), 0 1rem 3rem rgba(0, 0, 0, 0.2);
|
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.05), 0 1rem 3rem rgba(0, 0, 0, 0.2);
|
||||||
background-color: rgba(var(--foreground-color), 0.9);
|
}
|
||||||
|
#main_card:not(.nav-hidden) {
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
grid-template-areas: "nav header" "nav .";
|
||||||
}
|
}
|
||||||
|
|
||||||
#main_header {
|
#main_header {
|
||||||
grid-area: header;
|
grid-area: header;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pages_container {
|
|
||||||
grid-area: main;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main_navbar {
|
#main_navbar {
|
||||||
grid-area: nav;
|
grid-area: nav;
|
||||||
border-top: none;
|
border-top: none;
|
||||||
@ -865,16 +1248,19 @@ h3 {
|
|||||||
bottom: auto;
|
bottom: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#user {
|
.card {
|
||||||
grid-template-columns: 1fr 20rem;
|
padding: 1.5rem;
|
||||||
align-content: flex-start;
|
}
|
||||||
align-items: flex-start;
|
|
||||||
|
#saved_ids_list {
|
||||||
|
gap: 1rem;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 56rem) {
|
@media screen and (min-width: 56rem) {
|
||||||
#main_card {
|
#main_card {
|
||||||
height: 80vh;
|
|
||||||
width: 56rem;
|
width: 56rem;
|
||||||
|
height: min(90vh, 48rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (any-hover: hover) {
|
@media (any-hover: hover) {
|
||||||
@ -898,10 +1284,12 @@ h3 {
|
|||||||
background-color: rgba(var(--text-color), 0.06);
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:not([disabled]) {
|
button:not([disabled]),
|
||||||
|
.button:not([disabled]) {
|
||||||
transition: background-color 0.3s, filter 0.3s;
|
transition: background-color 0.3s, filter 0.3s;
|
||||||
}
|
}
|
||||||
.button:not([disabled]):hover {
|
button:not([disabled]):hover,
|
||||||
|
.button:not([disabled]):hover {
|
||||||
filter: contrast(2);
|
filter: contrast(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
693
css/main.scss
693
css/main.scss
@ -4,7 +4,6 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
font-size: clamp(1rem, 1.2vmax, 1.2rem);
|
font-size: clamp(1rem, 1.2vmax, 1.2rem);
|
||||||
}
|
}
|
||||||
@ -17,29 +16,29 @@ body {
|
|||||||
body {
|
body {
|
||||||
--accent-color: #256eff;
|
--accent-color: #256eff;
|
||||||
--text-color: 20, 20, 20;
|
--text-color: 20, 20, 20;
|
||||||
--background-color: 240, 240, 240;
|
--foreground-color: 252, 253, 255;
|
||||||
--foreground-color: 250, 250, 250;
|
--background-color: 241, 243, 248;
|
||||||
--danger-color: rgb(255, 75, 75);
|
--danger-color: rgb(255, 75, 75);
|
||||||
--green: #1cad59;
|
--green: #1cad59;
|
||||||
|
--yellow: rgb(220, 165, 0);
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
color: rgba(var(--text-color), 1);
|
color: rgba(var(--text-color), 1);
|
||||||
background-color: rgba(var(--background-color), 1);
|
background-color: rgba(var(--background-color), 1);
|
||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body[data-theme="dark"] {
|
body[data-theme="dark"] {
|
||||||
--accent-color: #86afff;
|
--accent-color: #90b8f8;
|
||||||
--text-color: 220, 220, 220;
|
--text-color: 220, 220, 220;
|
||||||
--background-color: 10, 10, 10;
|
--foreground-color: 27, 28, 29;
|
||||||
--foreground-color: 24, 24, 24;
|
--background-color: 21, 22, 22;
|
||||||
--danger-color: rgb(255, 106, 106);
|
--danger-color: rgb(255, 106, 106);
|
||||||
--green: #00e676;
|
--green: #00e676;
|
||||||
|
--yellow: rgb(255, 213, 5);
|
||||||
sm-popup::part(popup) {
|
sm-popup::part(popup) {
|
||||||
background-color: rgba(var(--foreground-color), 1);
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
}
|
}
|
||||||
@ -63,6 +62,13 @@ a {
|
|||||||
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
|
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
a.button {
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -89,19 +95,22 @@ button {
|
|||||||
}
|
}
|
||||||
.button {
|
.button {
|
||||||
background-color: rgba(var(--text-color), 0.06);
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
}
|
&--primary,
|
||||||
.button--primary,
|
&--danger {
|
||||||
.button--danger {
|
color: rgba(var(--background-color), 1);
|
||||||
color: rgba(var(--background-color), 1);
|
.icon {
|
||||||
.icon {
|
fill: rgba(var(--background-color), 1);
|
||||||
fill: rgba(var(--background-color), 1);
|
}
|
||||||
|
}
|
||||||
|
&--primary {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
&--danger {
|
||||||
|
background-color: var(--danger-color);
|
||||||
|
}
|
||||||
|
&--small {
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.button--primary {
|
|
||||||
background-color: var(--accent-color);
|
|
||||||
}
|
|
||||||
.button--danger {
|
|
||||||
background-color: var(--danger-color);
|
|
||||||
}
|
}
|
||||||
.cta {
|
.cta {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@ -156,7 +165,7 @@ sm-textarea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
sm-button {
|
sm-button {
|
||||||
--padding: 0.6rem 0.8rem;
|
--padding: 0.8rem;
|
||||||
&[variant="primary"] {
|
&[variant="primary"] {
|
||||||
.icon {
|
.icon {
|
||||||
fill: rgba(var(--background-color), 1);
|
fill: rgba(var(--background-color), 1);
|
||||||
@ -180,6 +189,9 @@ sm-spinner {
|
|||||||
sm-form {
|
sm-form {
|
||||||
--gap: 1rem;
|
--gap: 1rem;
|
||||||
}
|
}
|
||||||
|
sm-select {
|
||||||
|
--padding: 0.8rem;
|
||||||
|
}
|
||||||
strip-select {
|
strip-select {
|
||||||
--gap: 0;
|
--gap: 0;
|
||||||
background-color: rgba(var(--text-color), 0.06);
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
@ -212,27 +224,6 @@ ul {
|
|||||||
.full-bleed {
|
.full-bleed {
|
||||||
grid-column: 1/-1;
|
grid-column: 1/-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h1 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h2 {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h4 {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h5 {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
.uppercase {
|
.uppercase {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
@ -305,7 +296,7 @@ h3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.justify-start {
|
.justify-start {
|
||||||
justify-content: start;
|
justify-items: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.justify-center {
|
.justify-center {
|
||||||
@ -403,6 +394,7 @@ h3 {
|
|||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 1.2rem;
|
width: 1.2rem;
|
||||||
height: 1.2rem;
|
height: 1.2rem;
|
||||||
@ -426,6 +418,34 @@ h3 {
|
|||||||
fill: var(--accent-color);
|
fill: var(--accent-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.page {
|
||||||
|
height: 100%;
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
.grid {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin-top: auto;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
.illustration {
|
||||||
|
height: 8rem;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.page-layout {
|
||||||
|
display: grid;
|
||||||
|
gap: 1.5rem 0;
|
||||||
|
grid-template-columns: 1.5rem minmax(0, 1fr) 1.5rem;
|
||||||
|
align-content: flex-start;
|
||||||
|
& > * {
|
||||||
|
grid-column: 2/3;
|
||||||
|
}
|
||||||
|
}
|
||||||
#confirmation_popup,
|
#confirmation_popup,
|
||||||
#prompt_popup {
|
#prompt_popup {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -455,7 +475,6 @@ h3 {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 1.5rem;
|
padding: 0 1.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-auto-flow: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__header__close {
|
.popup__header__close {
|
||||||
@ -463,21 +482,83 @@ h3 {
|
|||||||
margin-left: -0.5rem;
|
margin-left: -0.5rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
#main_header {
|
.flo-icon {
|
||||||
padding: 1rem 1.5rem;
|
margin-right: 0.3rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
width: 1.5rem;
|
||||||
}
|
}
|
||||||
#main_card {
|
#secondary_pages {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
header {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
.inner-page {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#landing {
|
||||||
|
& > section {
|
||||||
|
justify-content: center;
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
padding: 8vw 0;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: clamp(2rem, 5vw, 5rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#sign_in,
|
||||||
|
#sign_up {
|
||||||
|
justify-items: center;
|
||||||
|
align-content: center;
|
||||||
|
section {
|
||||||
|
margin-top: -8rem;
|
||||||
|
width: min(24rem, 100%);
|
||||||
|
}
|
||||||
|
sm-form {
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#sign_up {
|
||||||
|
.h2 {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
h5 {
|
||||||
|
font-weight: 500;
|
||||||
|
color: rgba(var(--text-color), 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#flo_id_warning {
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
border-bottom: thin solid rgba(var(--text-color), 0.3);
|
||||||
|
.icon {
|
||||||
|
height: 4rem;
|
||||||
|
width: 4rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: #ffc107;
|
||||||
|
border-radius: 3rem;
|
||||||
|
fill: rgba(0, 0, 0, 0.8);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#main_header {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
#main_card {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: rgba(var(--foreground-color), 1);
|
|
||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
}
|
}
|
||||||
#pages_container {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main_navbar {
|
#main_navbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -504,14 +585,15 @@ h3 {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0.5rem 0.3rem;
|
padding: 0.5rem 0.3rem;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
font-size: 0.7rem;
|
font-size: 0.8rem;
|
||||||
border-radius: 0.3rem;
|
border-radius: 0.3rem;
|
||||||
.icon {
|
.icon {
|
||||||
transition: transform 0.2s;
|
transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
}
|
}
|
||||||
&__title {
|
&__title {
|
||||||
margin-top: 0.3rem;
|
margin-top: 0.3rem;
|
||||||
transition: opacity 0.2s, transform 0.2s;
|
transition: opacity 0.2s,
|
||||||
|
transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
}
|
}
|
||||||
&--active {
|
&--active {
|
||||||
color: var(--accent-color);
|
color: var(--accent-color);
|
||||||
@ -533,6 +615,29 @@ h3 {
|
|||||||
border-radius: 1rem 1rem 0 0;
|
border-radius: 1rem 1rem 0 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
.badge {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
content: attr(data-notifications);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0.3rem;
|
||||||
|
background: var(--accent-color);
|
||||||
|
color: rgba(var(--background-color), 1);
|
||||||
|
aspect-ratio: 1/1;
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
margin: 0.3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.inner-page {
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
align-content: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-field {
|
.password-field {
|
||||||
@ -557,54 +662,222 @@ h3 {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.clip {
|
#home {
|
||||||
clip-path: circle(0);
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
#user,
|
||||||
|
#cashier {
|
||||||
|
position: relative;
|
||||||
|
gap: 2rem;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: 5rem;
|
||||||
|
align-content: flex-start;
|
||||||
|
}
|
||||||
|
#quick_actions_container {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr));
|
||||||
}
|
}
|
||||||
.primary-action {
|
.primary-action {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0.8rem 1rem;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.8rem;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
background-color: transparent;
|
font-weight: 400;
|
||||||
border: thin solid rgba(var(--text-color), 0.3);
|
background-color: rgba(var(--text-color), 0.03);
|
||||||
|
text-align: center;
|
||||||
.icon {
|
.icon {
|
||||||
fill: var(--accent-color);
|
fill: var(--accent-color);
|
||||||
}
|
}
|
||||||
&:not(:last-of-type) {
|
}
|
||||||
|
#rupee_balance {
|
||||||
|
span:first-of-type {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
span:last-of-type {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.wallet-action {
|
||||||
|
background-color: rgba(var(--text-color), 0.03);
|
||||||
|
flex: 1;
|
||||||
|
&:nth-of-type(2) {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
|
fill: var(--accent-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.page {
|
#saved_ids_list {
|
||||||
position: relative;
|
display: grid;
|
||||||
display: flex;
|
grid-template-columns: minmax(0, 1fr);
|
||||||
flex-direction: column;
|
margin-top: 1rem;
|
||||||
overflow-y: auto;
|
}
|
||||||
align-content: flex-start;
|
.saved-id {
|
||||||
padding: 1.5rem;
|
grid-template-columns: auto 1fr;
|
||||||
|
gap: 0 0.8rem;
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
padding: 0.8rem 0;
|
||||||
|
user-select: none;
|
||||||
|
&.highlight {
|
||||||
|
box-shadow: 0 0 0.1rem 0.1rem var(--accent-color) inset;
|
||||||
|
}
|
||||||
|
.edit-saved {
|
||||||
|
grid-area: 1/1/3/2;
|
||||||
|
padding: 0.3rem;
|
||||||
|
position: relative;
|
||||||
|
.icon {
|
||||||
|
position: absolute;
|
||||||
|
height: 1.2rem;
|
||||||
|
width: 1.2rem;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 0.2rem;
|
||||||
|
background-color: rgba(var(--background-color), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__initials {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 2.4rem;
|
||||||
|
width: 2.4rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(var(--background-color), 1);
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
justify-self: flex-start;
|
||||||
|
border-radius: 2rem;
|
||||||
|
}
|
||||||
|
&__title {
|
||||||
|
align-self: flex-end;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
&__flo-id {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#wallet_section {
|
.card {
|
||||||
background-color: rgba(var(--text-color), 0.03);
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
padding: 1.5rem;
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
#contact {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
& > * {
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
}
|
||||||
|
& > :first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
& > :last-child {
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
background-color: rgba(var(--text-color), 0.03);
|
||||||
|
button {
|
||||||
|
padding: 0.8rem 2rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
color: var(--accent-color);
|
||||||
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#contact__transactions {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 max(1rem, 8vw) 1rem max(1rem, 8vw);
|
||||||
|
align-content: flex-start;
|
||||||
|
sm-spinner {
|
||||||
|
position: absolute;
|
||||||
|
justify-self: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.transaction-message {
|
||||||
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
justify-self: flex-start;
|
||||||
|
border-radius: 0 1rem 1rem 1rem;
|
||||||
|
gap: 0.5rem;
|
||||||
|
&.received {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
color: rgba(var(--background-color), 1);
|
||||||
|
& + & {
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.sent {
|
||||||
|
margin-left: auto;
|
||||||
|
justify-self: flex-end;
|
||||||
|
border-radius: 1rem 1rem 0 1rem;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__amount {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
&__time {
|
||||||
|
opacity: 0.8;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#transactions_list {
|
#wallet_history_wrapper {
|
||||||
flex-direction: column;
|
margin-top: 1.5rem;
|
||||||
|
padding-bottom: 3rem;
|
||||||
|
}
|
||||||
|
#payments_history {
|
||||||
|
display: grid;
|
||||||
|
gap: 2rem;
|
||||||
padding-bottom: 4rem;
|
padding-bottom: 4rem;
|
||||||
}
|
}
|
||||||
.transaction {
|
.transaction {
|
||||||
grid-template-columns: auto 1fr auto;
|
grid-template-columns: auto 1fr auto;
|
||||||
gap: 0.5rem 1rem;
|
gap: 0.5rem 1rem;
|
||||||
padding: 0.8rem;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: rgba(var(--text-color), 0.03);
|
&.sent {
|
||||||
border-radius: 0.3rem;
|
.icon {
|
||||||
&:not(:last-of-type) {
|
fill: rgba(var(--text-color), 0.8);
|
||||||
margin-bottom: 0.5rem;
|
}
|
||||||
|
.transaction__amount {
|
||||||
|
color: rgba(var(--text-color), 0.8);
|
||||||
|
&::before {
|
||||||
|
content: "- ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.received {
|
||||||
|
.icon {
|
||||||
|
fill: var(--green);
|
||||||
|
}
|
||||||
|
.transaction__amount {
|
||||||
|
color: var(--green);
|
||||||
|
&::before {
|
||||||
|
content: "+ ";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&__icon {
|
&__icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -614,15 +887,10 @@ h3 {
|
|||||||
width: 2.5rem;
|
width: 2.5rem;
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
background-color: rgba(var(--text-color), 0.03);
|
background-color: rgba(var(--text-color), 0.03);
|
||||||
border-radius: 0.5rem;
|
border-radius: 2rem;
|
||||||
.icon {
|
|
||||||
fill: var(--accent-color);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&__receiver {
|
&__receiver {
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: rgba(var(--text-color), 0.8);
|
|
||||||
}
|
}
|
||||||
&__time {
|
&__time {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
@ -632,16 +900,6 @@ h3 {
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
grid-area: 1/3/3/4;
|
grid-area: 1/3/3/4;
|
||||||
&.sent {
|
|
||||||
&::before {
|
|
||||||
content: "-";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.received {
|
|
||||||
&::before {
|
|
||||||
content: "+";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fab {
|
.fab {
|
||||||
@ -656,48 +914,29 @@ h3 {
|
|||||||
border-radius: 3rem;
|
border-radius: 3rem;
|
||||||
background-color: rgba(var(--foreground-color), 1);
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
}
|
}
|
||||||
|
#add_address_button {
|
||||||
#transaction_result {
|
border-radius: 0.5rem;
|
||||||
display: grid;
|
color: rgba(var(--background-color), 1);
|
||||||
gap: 0.5rem;
|
background-color: var(--accent-color);
|
||||||
height: max(40vh, 24rem);
|
.icon {
|
||||||
align-items: center;
|
fill: rgba(var(--background-color), 1);
|
||||||
justify-content: center;
|
}
|
||||||
text-align: center;
|
}
|
||||||
align-content: center;
|
.user-action-result__icon {
|
||||||
|
justify-self: center;
|
||||||
|
height: 4rem;
|
||||||
|
width: 4rem;
|
||||||
|
border-radius: 5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
animation: popup 1s;
|
||||||
&.success {
|
&.success {
|
||||||
.icon--failed {
|
fill: rgba(var(--background-color), 1);
|
||||||
display: none;
|
padding: 1rem;
|
||||||
}
|
background-color: #0bbe56;
|
||||||
}
|
}
|
||||||
&.failed {
|
&.failed {
|
||||||
.icon--success {
|
background-color: rgba(var(--text-color), 0.03);
|
||||||
display: none;
|
fill: var(--danger-color);
|
||||||
}
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.icon {
|
|
||||||
justify-self: center;
|
|
||||||
height: 4rem;
|
|
||||||
width: 4rem;
|
|
||||||
border-radius: 5rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
animation: popup 1s;
|
|
||||||
&--success {
|
|
||||||
fill: rgba(var(--background-color), 1);
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: #0bbe56;
|
|
||||||
}
|
|
||||||
&--failed {
|
|
||||||
background-color: rgba(var(--text-color), 0.03);
|
|
||||||
fill: var(--danger-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sm-copy {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@keyframes popup {
|
@keyframes popup {
|
||||||
@ -720,60 +959,202 @@ h3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#settings {
|
|
||||||
}
|
|
||||||
|
|
||||||
.cashier-request,
|
.cashier-request,
|
||||||
.wallet-request,
|
|
||||||
.payment-request {
|
.payment-request {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0.8rem;
|
padding: 0.8rem;
|
||||||
border-radius: 0.3rem;
|
border-radius: 0.3rem;
|
||||||
background-color: rgba(var(--text-color), 0.06);
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
&:not(:last-of-type) {
|
&:not(:last-of-type) {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
&__time {
|
&__time {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#payment_request_history {
|
||||||
|
padding-bottom: 3rem;
|
||||||
|
}
|
||||||
.payment-request {
|
.payment-request {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
gap: 0.5rem 1rem;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
|
color: rgba(var(--text-color), 1);
|
||||||
|
&__requestor {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
&__amount {
|
&__amount {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
&__status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-transform: capitalize;
|
||||||
|
.icon {
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
margin-left: 0.3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.icon.paid {
|
||||||
|
fill: var(--green);
|
||||||
|
}
|
||||||
|
.icon.declined {
|
||||||
|
fill: var(--danger-color);
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0.6rem 0.8rem;
|
||||||
|
color: var(--accent-color);
|
||||||
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.wallet-request {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem 1rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
&:not(.rejected, .pending) {
|
||||||
|
&.withdrawn {
|
||||||
|
.wallet-request__amount {
|
||||||
|
&::before {
|
||||||
|
content: "- ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.added {
|
||||||
|
.wallet-request__amount {
|
||||||
|
color: var(--green);
|
||||||
|
&::before {
|
||||||
|
content: "+ ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.icon.pending {
|
||||||
|
fill: var(--yellow);
|
||||||
|
}
|
||||||
|
.icon.failed {
|
||||||
|
fill: var(--danger-color);
|
||||||
|
}
|
||||||
|
&__icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
grid-area: 1/1/3/2;
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
border-radius: 2rem;
|
||||||
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
|
.icon {
|
||||||
|
fill: var(--accent-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__details {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
&__details,
|
||||||
|
&__amount {
|
||||||
|
color: rgba(var(--text-color), 1);
|
||||||
|
}
|
||||||
|
&__amount {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
&__time,
|
||||||
|
&__status {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: rgba(var(--text-color), 0.8);
|
||||||
|
}
|
||||||
|
&__status {
|
||||||
|
text-transform: capitalize;
|
||||||
|
text-align: right;
|
||||||
|
.icon {
|
||||||
|
margin-left: 0.3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#transaction {
|
||||||
|
// justify-content: center;
|
||||||
|
// text-align: center;
|
||||||
|
// justify-items: center;
|
||||||
|
}
|
||||||
|
#transaction__remark,
|
||||||
|
#transaction__note {
|
||||||
|
line-height: 1.6;
|
||||||
|
justify-self: flex-start;
|
||||||
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 0.8rem;
|
||||||
|
}
|
||||||
|
#transaction__note {
|
||||||
|
.icon {
|
||||||
|
fill: var(--danger-color);
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#saved_upi_ids_list {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
width: min(24rem, 100%);
|
||||||
|
}
|
||||||
|
.saved-upi {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.4rem 0.4rem 0.4rem 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 40rem) {
|
||||||
|
#main_navbar {
|
||||||
|
&.hide-away {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 40rem) {
|
@media screen and (min-width: 40rem) {
|
||||||
sm-popup {
|
sm-popup {
|
||||||
--width: 24rem;
|
--width: 24rem;
|
||||||
}
|
}
|
||||||
|
.page-layout {
|
||||||
|
grid-template-columns: 1fr 90vw 1fr;
|
||||||
|
}
|
||||||
.popup__header {
|
.popup__header {
|
||||||
grid-column: 1/-1;
|
grid-column: 1/-1;
|
||||||
padding: 1rem 1.5rem 0 1.5rem;
|
padding: 1rem 1.5rem 0 1.5rem;
|
||||||
}
|
}
|
||||||
|
body {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
#main_card {
|
#main_card {
|
||||||
display: grid;
|
height: calc(100vh - 3rem);
|
||||||
grid-template-columns: auto 1fr;
|
width: calc(100vw - 3rem);
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-areas: "header" ".";
|
||||||
grid-template-areas: "nav header" "nav main";
|
&:not(.nav-hidden) {
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
grid-template-areas: "nav header" "nav .";
|
||||||
|
}
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background-color: rgba(var(--background-color), 1);
|
||||||
|
|
||||||
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.05),
|
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.05),
|
||||||
0 1rem 3rem rgba(0, 0, 0, 0.2);
|
0 1rem 3rem rgba(0, 0, 0, 0.2);
|
||||||
// backdrop-filter: blur(2rem);
|
|
||||||
background-color: rgba(var(--foreground-color), 0.9);
|
|
||||||
}
|
}
|
||||||
#main_header {
|
#main_header {
|
||||||
grid-area: header;
|
grid-area: header;
|
||||||
}
|
}
|
||||||
#pages_container {
|
|
||||||
grid-area: main;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main_navbar {
|
#main_navbar {
|
||||||
grid-area: nav;
|
grid-area: nav;
|
||||||
@ -800,19 +1181,20 @@ h3 {
|
|||||||
bottom: auto;
|
bottom: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#user {
|
.card {
|
||||||
grid-template-columns: 1fr 20rem;
|
padding: 1.5rem;
|
||||||
align-content: flex-start;
|
}
|
||||||
align-items: flex-start;
|
#saved_ids_list {
|
||||||
|
gap: 1rem;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 56rem) {
|
@media screen and (min-width: 56rem) {
|
||||||
#main_card {
|
#main_card {
|
||||||
height: 80vh;
|
|
||||||
width: 56rem;
|
width: 56rem;
|
||||||
|
height: min(90vh, 48rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (any-hover: hover) {
|
@media (any-hover: hover) {
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 0.5rem;
|
width: 0.5rem;
|
||||||
@ -833,6 +1215,7 @@ h3 {
|
|||||||
background-color: rgba(var(--text-color), 0.06);
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
button:not([disabled]),
|
||||||
.button:not([disabled]) {
|
.button:not([disabled]) {
|
||||||
transition: background-color 0.3s, filter 0.3s;
|
transition: background-color 0.3s, filter 0.3s;
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
1260
index.html
1260
index.html
File diff suppressed because one or more lines are too long
14749
old_index.html
Normal file
14749
old_index.html
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -39,6 +39,7 @@ User.init = function () {
|
|||||||
}));
|
}));
|
||||||
*/
|
*/
|
||||||
promises.push(User.getCashierUPI());
|
promises.push(User.getCashierUPI());
|
||||||
|
promises.push(organizeSyncedData('savedUserData'));
|
||||||
Promise.all(promises)
|
Promise.all(promises)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
@ -85,8 +86,10 @@ User.findCashier = function () {
|
|||||||
online.push(c);
|
online.push(c);
|
||||||
if (!online.length)
|
if (!online.length)
|
||||||
return null;
|
return null;
|
||||||
else
|
else {
|
||||||
return online[floCrypto.randInt(0, online.length)];
|
const random = floCrypto.randInt(0, online.length - 1)
|
||||||
|
return online[random];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
User.cashToToken = function (cashier, amount, upiTxID) {
|
User.cashToToken = function (cashier, amount, upiTxID) {
|
||||||
@ -166,6 +169,7 @@ Cashier.init = function () {
|
|||||||
callback: UI_RENDER_FN
|
callback: UI_RENDER_FN
|
||||||
}));
|
}));
|
||||||
*/
|
*/
|
||||||
|
promises.push(User.getCashierUPI());
|
||||||
Promise.all(promises)
|
Promise.all(promises)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
|
|||||||
646
scripts/fn_ui.js
646
scripts/fn_ui.js
@ -1,43 +1,157 @@
|
|||||||
/*jshint esversion: 6 */
|
/*jshint esversion: 8 */
|
||||||
const userUI = {};
|
/**
|
||||||
|
* @yaireo/relative-time - javascript function to transform timestamp or date to local relative-time
|
||||||
|
*
|
||||||
|
* @version v1.0.0
|
||||||
|
* @homepage https://github.com/yairEO/relative-time
|
||||||
|
*/
|
||||||
|
|
||||||
userUI.requestTokenFromCashier = function () {
|
!function (e, t) { var o = o || {}; "function" == typeof o && o.amd ? o([], t) : "object" == typeof exports && "object" == typeof module ? module.exports = t() : "object" == typeof exports ? exports.RelativeTime = t() : e.RelativeTime = t() }(this, (function () { const e = { year: 31536e6, month: 2628e6, day: 864e5, hour: 36e5, minute: 6e4, second: 1e3 }, t = "en", o = { numeric: "auto" }; function n(e) { e = { locale: (e = e || {}).locale || t, options: { ...o, ...e.options } }, this.rtf = new Intl.RelativeTimeFormat(e.locale, e.options) } return n.prototype = { from(t, o) { const n = t - (o || new Date); for (let t in e) if (Math.abs(n) > e[t] || "second" == t) return this.rtf.format(Math.round(n / e[t]), t) } }, n }));
|
||||||
|
|
||||||
|
const relativeTime = new RelativeTime({ style: 'narrow' });
|
||||||
|
|
||||||
|
function syncUserData(obsName, data) {
|
||||||
|
const dataToSend = Crypto.AES.encrypt(JSON.stringify(data), myPrivKey);
|
||||||
|
return floCloudAPI.sendApplicationData(dataToSend, obsName, { receiverID: myFloID });
|
||||||
|
}
|
||||||
|
async function organizeSyncedData(obsName) {
|
||||||
|
const fetchedData = await floCloudAPI.requestApplicationData(obsName, { mostRecent: true, senderIDs: [myFloID], receiverID: myFloID });
|
||||||
|
if (fetchedData.length && await compactIDB.readData(obsName, 'lastSyncTime') !== fetchedData[0].time) {
|
||||||
|
await compactIDB.clearData(obsName);
|
||||||
|
const dataToDecrypt = floCloudAPI.util.decodeMessage(fetchedData[0].message);
|
||||||
|
const decryptedData = JSON.parse(Crypto.AES.decrypt(dataToDecrypt, myPrivKey));
|
||||||
|
for (let key in decryptedData) {
|
||||||
|
floGlobals[obsName][key] = decryptedData[key];
|
||||||
|
compactIDB.addData(obsName, decryptedData[key], key);
|
||||||
|
}
|
||||||
|
compactIDB.addData(obsName, fetchedData[0].time, 'lastSyncTime');
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
const idbData = await compactIDB.readAllData(obsName);
|
||||||
|
for (const key in idbData) {
|
||||||
|
if (key !== 'lastSyncTime')
|
||||||
|
floGlobals[obsName][key] = idbData[key];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const userUI = {};
|
||||||
|
function continueWalletTopup() {
|
||||||
let cashier = User.findCashier();
|
let cashier = User.findCashier();
|
||||||
if (!cashier)
|
if (!cashier)
|
||||||
return alert("No cashier online");
|
return notify("No cashier online. Please try again in a while.", 'error');
|
||||||
let amount = parseFloat(getRef('request_cashier_amount').value.trim());
|
let amount = parseFloat(getRef('request_cashier_amount').value.trim());
|
||||||
if (!amount)
|
getRef('topup_wallet__details').innerHTML = `Send <b>${formatAmount(amount)}</b> to UPI ID below`;
|
||||||
return alert("Enter amount");
|
getRef('topup_wallet__upi_id').value = cashierUPI[cashier];
|
||||||
//get UPI txid from user
|
showProcessStage('topup_wallet_process', 1)
|
||||||
let upiTxID = prompt(`Send Rs. ${amount} to ${cashierUPI[cashier]} and enter UPI txid`);
|
getRef('topup_wallet__txid').focusIn();
|
||||||
if (!upiTxID)
|
}
|
||||||
return alert("Cancelled");
|
function depositMoneyToWallet() {
|
||||||
|
let cashier = User.findCashier();
|
||||||
|
if (!cashier)
|
||||||
|
return notify("No cashier online. Please try again in a while.", 'error');
|
||||||
|
let amount = parseFloat(getRef('request_cashier_amount').value.trim());
|
||||||
|
let upiTxID = getRef('topup_wallet__txid').value.trim();
|
||||||
|
if (upiTxID === '')
|
||||||
|
return notify("Please enter UPI transaction ID", 'error');
|
||||||
|
buttonLoader('topup_wallet_button', true);
|
||||||
User.cashToToken(cashier, amount, upiTxID).then(result => {
|
User.cashToToken(cashier, amount, upiTxID).then(result => {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
alert("Requested cashier. please wait!");
|
showProcessStage('topup_wallet_process', 2);
|
||||||
}).catch(error => console.error(error))
|
}).catch(error => {
|
||||||
|
console.error(error)
|
||||||
|
getRef('topup_failed_reason').textContent = error;
|
||||||
|
showProcessStage('topup_wallet_process', 3);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
userUI.withdrawCashFromCashier = function () {
|
function withdrawMoneyFromWallet() {
|
||||||
let cashier = User.findCashier();
|
let cashier = User.findCashier();
|
||||||
if (!cashier)
|
if (!cashier)
|
||||||
return alert("No cashier online");
|
return notify("No cashier online. Please try again in a while.", 'error');
|
||||||
let amount = parseFloat(getRef('request_cashier_amount').value.trim());
|
let amount = parseFloat(getRef('send_cashier_amount').value.trim());
|
||||||
if (!amount)
|
const upiId = getRef('select_upi_id').value;
|
||||||
return alert("Enter amount");
|
if (!upiId)
|
||||||
//get confirmation from user
|
return notify("Please add an UPI ID to continue", 'error');
|
||||||
let upiID = prompt(`${amount} ${floGlobals.currency}# will be sent to ${cashier}. Enter UPI ID`);
|
buttonLoader('withdraw_rupee_button', true);
|
||||||
if (!upiID)
|
|
||||||
return alert("Cancelled");
|
|
||||||
User.sendToken(cashier, amount, 'for token-to-cash').then(txid => {
|
User.sendToken(cashier, amount, 'for token-to-cash').then(txid => {
|
||||||
console.warn(`Withdraw ${amount} from cashier ${cashier}`, txid);
|
console.warn(`Withdraw ${amount} from cashier ${cashier}`, txid);
|
||||||
User.tokenToCash(cashier, amount, txid, upiID).then(result => {
|
User.tokenToCash(cashier, amount, txid, upiId).then(result => {
|
||||||
|
showProcessStage('withdraw_wallet_process', 1);
|
||||||
console.log(result);
|
console.log(result);
|
||||||
alert("Requested cashier. please wait!");
|
}).catch(error => {
|
||||||
}).catch(error => console.error(error))
|
getRef('withdrawal_failed_reason').textContent = error;
|
||||||
}).catch(error => console.error(error))
|
showProcessStage('withdraw_wallet_process', 2);
|
||||||
|
console.error(error)
|
||||||
|
}).finally(() => {
|
||||||
|
buttonLoader('withdraw_rupee_button', false);
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
getRef('withdrawal_failed_reason').textContent = error;
|
||||||
|
showProcessStage('withdraw_wallet_process', 2);
|
||||||
|
buttonLoader('withdraw_rupee_button', false);
|
||||||
|
console.error(error)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function renderSavedUpiIds() {
|
||||||
|
const frag = document.createDocumentFragment();
|
||||||
|
for (const upiId in floGlobals.savedUserData.upiIds) {
|
||||||
|
frag.append(render.savedUpiId(upiId));
|
||||||
|
}
|
||||||
|
getRef('saved_upi_ids_list').innerHTML = '';
|
||||||
|
getRef('saved_upi_ids_list').append(frag);
|
||||||
|
}
|
||||||
|
function saveUpiId() {
|
||||||
|
const upiId = getRef('get_upi_id').value.trim();
|
||||||
|
if (upiId === '')
|
||||||
|
return notify("Please add an UPI ID to continue", 'error');
|
||||||
|
if (floGlobals.savedUserData.upiIds.hasOwnProperty(upiId))
|
||||||
|
return notify('This UPI ID is already saved', 'error');
|
||||||
|
floGlobals.savedUserData.upiIds[upiId] = {}
|
||||||
|
syncUserData('savedUserData', floGlobals.savedUserData).then(() => {
|
||||||
|
notify(`Saved ${upiId}`, 'success');
|
||||||
|
if (pagesData.lastPage === 'settings') {
|
||||||
|
getRef('saved_upi_ids_list').append(render.savedUpiId(upiId));
|
||||||
|
} else if (pagesData.lastPage === 'wallet') {
|
||||||
|
getRef('select_upi_id').append(
|
||||||
|
createElement('sm-option', {
|
||||||
|
textContent: upiId,
|
||||||
|
attributes: {
|
||||||
|
value: upiId,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
getRef('select_upi_id').parentNode.classList.remove('hide')
|
||||||
|
}
|
||||||
|
hidePopup();
|
||||||
|
}).catch(error => {
|
||||||
|
notify(error, 'error');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
delegate(getRef('saved_upi_ids_list'), 'click', '.saved-upi', e => {
|
||||||
|
if (e.target.closest('.delete-upi')) {
|
||||||
|
const upiId = e.delegateTarget.dataset.upiId;
|
||||||
|
getConfirmation('Do you want delete this UPI ID?', {
|
||||||
|
confirmText: 'Delete',
|
||||||
|
}).then(res => {
|
||||||
|
if (res) {
|
||||||
|
const toDelete = getRef('saved_upi_ids_list').querySelector(`.saved-upi[data-upi-id="${upiId}"]`);
|
||||||
|
if (toDelete)
|
||||||
|
toDelete.remove();
|
||||||
|
delete floGlobals.savedUserData.upiIds[upiId];
|
||||||
|
hidePopup();
|
||||||
|
syncUserData('savedUserData', floGlobals.savedUserData).then(() => {
|
||||||
|
notify(`Deleted UPI ID`, 'success');
|
||||||
|
}).catch(error => {
|
||||||
|
notify(error, 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
userUI.sendMoneyToUser = function (floID, amount, remark) {
|
userUI.sendMoneyToUser = function (floID, amount, remark) {
|
||||||
getConfirmation('Confirm', { message: `Do you want to SEND ${amount} to ${floID}?` }).then(confirmation => {
|
getConfirmation('Confirm', { message: `Do you want to SEND ${amount} to ${floID}?` }).then(confirmation => {
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
@ -67,36 +181,115 @@ userUI.renderCashierRequests = function (requests, error = null) {
|
|||||||
return console.error(error);
|
return console.error(error);
|
||||||
else if (typeof requests !== "object" || requests === null)
|
else if (typeof requests !== "object" || requests === null)
|
||||||
return;
|
return;
|
||||||
const frag = document.createDocumentFragment()
|
if (pagesData.lastPage === 'wallet') {
|
||||||
for (let r in requests) {
|
for (let transactionID in requests) {
|
||||||
let oldCard = document.getElementById(r);
|
const { note, tag } = requests[transactionID];
|
||||||
if (oldCard) oldCard.remove();
|
let status = tag ? 'done' : (note ? 'failed' : "pending");
|
||||||
frag.append(render.walletRequestCard(requests[r]))
|
getRef('wallet_history_wrapper').querySelectorAll(`[data-vc="${transactionID}"]`).forEach(card => card.remove());
|
||||||
|
getRef(status !== 'pending' ? 'wallet_history' : 'pending_wallet_transactions').prepend(render.walletRequestCard(requests[transactionID]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
getRef('user-cashier-requests').append(frag)
|
};
|
||||||
}
|
|
||||||
|
const pendingTransactionsObserver = new MutationObserver((mutations) => {
|
||||||
|
mutations.forEach(mutation => {
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
if (mutation.target.children.length)
|
||||||
|
mutation.target.parentNode.classList.remove('hide')
|
||||||
|
else
|
||||||
|
mutation.target.parentNode.classList.add('hide')
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
userUI.renderMoneyRequests = function (requests, error = null) {
|
userUI.renderMoneyRequests = function (requests, error = null) {
|
||||||
if (error)
|
if (error)
|
||||||
return console.error(error);
|
return console.error(error);
|
||||||
else if (typeof requests !== "object" || requests === null)
|
else if (typeof requests !== "object" || requests === null)
|
||||||
return;
|
return;
|
||||||
const frag = document.createDocumentFragment()
|
if (pagesData.lastPage === 'requests') {
|
||||||
for (let r in requests) {
|
for (let r in requests) {
|
||||||
let oldCard = document.getElementById(r);
|
getRef('requests_history_wrapper').querySelectorAll(`[data-vc="${r}"]`).forEach(card => card.remove());
|
||||||
if (oldCard) oldCard.remove();
|
if (requests[r].note) {
|
||||||
frag.append(render.paymentRequestCard(requests[r]))
|
getRef('payment_request_history').prepend(render.paymentRequestCard(requests[r]));
|
||||||
|
} else {
|
||||||
|
getRef('pending_payment_requests').prepend(render.paymentRequestCard(requests[r]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
getRef('user-money-requests').append(frag)
|
if (floGlobals.loaded) {
|
||||||
}
|
for (let r in requests) {
|
||||||
|
if (!requests[r].note) {
|
||||||
|
notify(`You have received payment request from ${getFloIdTitle(requests[r].senderID)}`, '', {
|
||||||
|
pinned: true,
|
||||||
|
action: {
|
||||||
|
label: 'View',
|
||||||
|
callback: () => {
|
||||||
|
window.location.hash = `#/requests`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let totalRequests = 0;
|
||||||
|
for (const request in User.moneyRequests) {
|
||||||
|
if (!User.moneyRequests[request].note) totalRequests++;
|
||||||
|
}
|
||||||
|
const animOptions = {
|
||||||
|
duration: 200,
|
||||||
|
fill: 'forwards',
|
||||||
|
easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
|
||||||
|
}
|
||||||
|
if (totalRequests) {
|
||||||
|
if (!getRef('requests_page_button').querySelector('.badge')) {
|
||||||
|
const badge = createElement('span', {
|
||||||
|
className: 'badge',
|
||||||
|
textContent: totalRequests
|
||||||
|
})
|
||||||
|
getRef('requests_page_button').append(badge)
|
||||||
|
badge.animate([
|
||||||
|
{
|
||||||
|
transform: 'scale(0) translateY(0.5rem)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: 'scale(1) translateY(0)'
|
||||||
|
},
|
||||||
|
], animOptions)
|
||||||
|
} else {
|
||||||
|
const badge = getRef('requests_page_button').querySelector('.badge');
|
||||||
|
badge.textContent = totalRequests;
|
||||||
|
badge.animate([
|
||||||
|
{ transform: 'scale(1)' },
|
||||||
|
{ transform: `scale(1.5)` },
|
||||||
|
{ transform: 'scale(1)' }
|
||||||
|
], animOptions)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (getRef('requests_page_button').querySelector('.badge')) {
|
||||||
|
const badge = getRef('requests_page_button').querySelector('.badge')
|
||||||
|
badge.animate([
|
||||||
|
{
|
||||||
|
transform: 'scale(1) translateY(0)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: 'scale(0) translateY(0.5rem)'
|
||||||
|
},
|
||||||
|
], animOptions).onfinish = () => {
|
||||||
|
badge.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
userUI.payRequest = function (reqID) {
|
userUI.payRequest = function (reqID) {
|
||||||
let request = User.moneyRequests[reqID];
|
let { message: { amount, remark }, senderID } = User.moneyRequests[reqID];
|
||||||
getConfirmation('Pay?', { message: `Do you want to pay ${request.message.amount} to ${request.senderID}?` }).then(confirmation => {
|
getConfirmation('Pay?', { message: `Do you want to pay ${request.message.amount} to ${request.senderID}?`, confirmText: 'Pay' }).then(confirmation => {
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
User.sendToken(request.senderID, request.message.amount, "|" + request.message.remark).then(txid => {
|
User.sendToken(senderID, amount, "|" + remark).then(txid => {
|
||||||
console.warn(`Sent ${request.message.amount} to ${request.senderID}`, txid);
|
console.warn(`Sent ${amount} to ${senderID}`, txid);
|
||||||
notify(`Sent ${request.message.amount} to ${request.senderID}. It may take a few mins to reflect in their wallet`, 'success');
|
notify(`Sent ${formatAmount(amount)} to ${getFloIdTitle(senderID)}. It may take a few mins to reflect in their wallet`, 'success');
|
||||||
User.decideRequest(request, 'PAID: ' + txid)
|
User.decideRequest(request, 'PAID: ' + txid)
|
||||||
.then(result => console.log(result))
|
.then(result => console.log(result))
|
||||||
.catch(error => console.error(error))
|
.catch(error => console.error(error))
|
||||||
@ -107,7 +300,7 @@ userUI.payRequest = function (reqID) {
|
|||||||
|
|
||||||
userUI.declineRequest = function (reqID) {
|
userUI.declineRequest = function (reqID) {
|
||||||
let request = User.moneyRequests[reqID];
|
let request = User.moneyRequests[reqID];
|
||||||
getConfirmation('Decline payment?').then(confirmation => {
|
getConfirmation('Decline payment?', { confirmText: 'Decline' }).then(confirmation => {
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
User.decideRequest(request, "DECLINED").then(result => {
|
User.decideRequest(request, "DECLINED").then(result => {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
@ -117,6 +310,15 @@ userUI.declineRequest = function (reqID) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delegate(getRef('pending_payment_requests'), 'click', '.pay-requested', e => {
|
||||||
|
const vectorClock = e.target.closest('.payment-request').dataset.vc;
|
||||||
|
userUI.payRequest(vectorClock);
|
||||||
|
})
|
||||||
|
delegate(getRef('pending_payment_requests'), 'click', '.decline-payment', e => {
|
||||||
|
const vectorClock = e.target.closest('.payment-request').dataset.vc;
|
||||||
|
userUI.declineRequest(vectorClock);
|
||||||
|
})
|
||||||
|
|
||||||
//Cashier
|
//Cashier
|
||||||
const cashierUI = {};
|
const cashierUI = {};
|
||||||
|
|
||||||
@ -129,9 +331,9 @@ cashierUI.renderRequests = function (requests, error = null) {
|
|||||||
for (let r in requests) {
|
for (let r in requests) {
|
||||||
const oldCard = document.getElementById(r);
|
const oldCard = document.getElementById(r);
|
||||||
if (oldCard) oldCard.remove();
|
if (oldCard) oldCard.remove();
|
||||||
frag.append(render.cashierRequestCard(requests[r]));
|
frag.prepend(render.cashierRequestCard(requests[r]));
|
||||||
}
|
}
|
||||||
getRef('cashier_request_list').append(frag)
|
getRef('cashier_request_list').prepend(frag)
|
||||||
}
|
}
|
||||||
|
|
||||||
cashierUI.completeRequest = function (reqID) {
|
cashierUI.completeRequest = function (reqID) {
|
||||||
@ -187,123 +389,337 @@ function completeTokenToCashRequest(request) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderAllTokenTransactions() {
|
function getFloIdTitle(floID) {
|
||||||
tokenAPI.getAllTxs(myFloID).then(result => {
|
return floGlobals.savedIds[floID] ? floGlobals.savedIds[floID].title : floID;
|
||||||
getRef('token_transactions').innerHTML = ''
|
}
|
||||||
const frag = document.createDocumentFragment();
|
|
||||||
for (let txid in result.transactions) {
|
function formatAmount(amount) {
|
||||||
frag.append(render.transactionCard(txid, tokenAPI.util.parseTxData(result.transactions[txid])))
|
return amount.toLocaleString(`en-IN`, { style: 'currency', currency: 'INR' })
|
||||||
}
|
}
|
||||||
getRef('token_transactions').append(frag)
|
|
||||||
}).catch(error => console.error(error))
|
function getStatusIcon(status) {
|
||||||
|
switch (status) {
|
||||||
|
case 'PENDING':
|
||||||
|
return '<i class="fas fa-clock"></i>';
|
||||||
|
case 'COMPLETED':
|
||||||
|
return '<i class="fas fa-check"></i>';
|
||||||
|
case 'REJECTED':
|
||||||
|
return '<i class="fas fa-times"></i>';
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const render = {
|
const render = {
|
||||||
transactionCard(txid, transactionDetails) {
|
savedId(floID, details) {
|
||||||
const { time, sender, receiver, tokenAmount } = transactionDetails
|
const { title } = details;
|
||||||
|
const clone = getRef('saved_id_template').content.cloneNode(true).firstElementChild;
|
||||||
|
clone.dataset.floId = floID;
|
||||||
|
clone.querySelector('.saved-id__initials').textContent = title.charAt(0);
|
||||||
|
clone.querySelector('.saved-id__title').textContent = title;
|
||||||
|
clone.querySelector('.saved-id__flo-id').textContent = floID;
|
||||||
|
return clone;
|
||||||
|
},
|
||||||
|
transactionCard(transactionDetails) {
|
||||||
|
const { txid, time, sender, receiver, tokenAmount } = transactionDetails;
|
||||||
const clone = getRef('transaction_template').content.cloneNode(true).firstElementChild;
|
const clone = getRef('transaction_template').content.cloneNode(true).firstElementChild;
|
||||||
clone.dataset.txid = txid
|
clone.dataset.txid = txid;
|
||||||
clone.querySelector('.transaction__time').textContent = getFormattedTime(time * 1000)
|
clone.querySelector('.transaction__time').textContent = getFormattedTime(time * 1000);
|
||||||
clone.querySelector('.transaction__amount').textContent = tokenAmount
|
clone.querySelector('.transaction__amount').textContent = formatAmount(tokenAmount);
|
||||||
if (sender === myFloID) {
|
if (sender === myFloID) {
|
||||||
clone.querySelector('.transaction__amount').classList.add('sent')
|
clone.classList.add('sent');
|
||||||
clone.querySelector('.transaction__receiver').textContent = `Sent to ${receiver || 'Myself'}`
|
clone.querySelector('.transaction__receiver').textContent = `Sent to ${getFloIdTitle(receiver) || 'Myself'}`;
|
||||||
clone.querySelector('.transaction__icon').innerHTML = `<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z"/></svg>`
|
clone.querySelector('.transaction__icon').innerHTML = `<svg class="icon sent" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/></svg>`;
|
||||||
} else if (receiver === myFloID) {
|
} else if (receiver === myFloID) {
|
||||||
clone.querySelector('.transaction__amount').classList.add('received')
|
clone.classList.add('received');
|
||||||
clone.querySelector('.transaction__receiver').textContent = `Received from ${sender}`
|
clone.querySelector('.transaction__receiver').textContent = `Received from ${getFloIdTitle(sender)}`;
|
||||||
clone.querySelector('.transaction__icon').innerHTML = `<svg class="icon xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 5.41L18.59 4 7 15.59V9H5v10h10v-2H8.41z"/></svg>`
|
clone.querySelector('.transaction__icon').innerHTML = `<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/></svg>`;
|
||||||
} else { //This should not happen unless API returns transaction that does not involve myFloID
|
} else { //This should not happen unless API returns transaction that does not involve myFloID
|
||||||
row.insertCell().textContent = tx.sender;
|
row.insertCell().textContent = tx.sender;
|
||||||
row.insertCell().textContent = tx.receiver;
|
row.insertCell().textContent = tx.receiver;
|
||||||
}
|
}
|
||||||
return clone
|
return clone;
|
||||||
},
|
},
|
||||||
cashierRequestCard(details) {
|
cashierRequestCard(details) {
|
||||||
const { time, senderID, message: { mode }, note, tag, vectorClock } = details;
|
const { time, senderID, message: { mode }, note, tag, vectorClock } = details;
|
||||||
const clone = getRef('cashier_request_template').content.cloneNode(true).firstElementChild;
|
const clone = getRef('cashier_request_template').content.cloneNode(true).firstElementChild;
|
||||||
clone.id = vectorClock
|
clone.id = vectorClock;
|
||||||
const status = tag || note; //status tag for completed, note for rejected
|
const status = tag || note; //status tag for completed, note for rejected
|
||||||
clone.querySelector('.cashier-request__requestor').textContent = senderID
|
clone.querySelector('.cashier-request__requestor').textContent = senderID;
|
||||||
clone.querySelector('.cashier-request__time').textContent = getFormattedTime(time)
|
clone.querySelector('.cashier-request__time').textContent = getFormattedTime(time);
|
||||||
clone.querySelector('.cashier-request__mode').textContent = mode
|
clone.querySelector('.cashier-request__mode').textContent = mode;
|
||||||
if (status)
|
if (status)
|
||||||
clone.querySelector('.cashier-request__status').textContent = status
|
clone.querySelector('.cashier-request__status').textContent = status;
|
||||||
else
|
else
|
||||||
clone.querySelector('.cashier-request__status').innerHTML = `<button class="button" onclick="cashierUI.completeRequest('${vectorClock}')">Process</button>`
|
clone.querySelector('.cashier-request__status').innerHTML = `<button class="button" onclick="cashierUI.completeRequest('${vectorClock}')">Process</button>`;
|
||||||
return clone
|
return clone;
|
||||||
},
|
},
|
||||||
walletRequestCard(details) {
|
walletRequestCard(details) {
|
||||||
const { time, receiverID, message: { mode }, note, tag, vectorClock } = details;
|
const { time, message: { mode, amount }, note, tag, vectorClock } = details;
|
||||||
const clone = getRef('wallet_request_template').content.cloneNode(true).firstElementChild;
|
const clone = getRef('wallet_request_template').content.cloneNode(true).firstElementChild.firstElementChild;
|
||||||
clone.id = vectorClock
|
const type = mode === 'cash-to-token' ? 'Wallet top-up' : 'Transfer to bank';
|
||||||
clone.querySelector('.wallet-request__requestor').textContent = receiverID
|
let status = tag ? tag : (note ? 'REJECTED' : "PENDING");
|
||||||
clone.querySelector('.wallet-request__time').textContent = getFormattedTime(time)
|
clone.classList.add(status.toLowerCase());
|
||||||
clone.querySelector('.wallet-request__mode').textContent = mode === 'cash-to-token' ? 'Deposit' : 'Withdraw'
|
clone.classList.add(mode === 'cash-to-token' ? 'added' : 'withdrawn');
|
||||||
let status = tag ? (tag + ":" + note) : (note || "PENDING");
|
clone.dataset.vc = vectorClock;
|
||||||
clone.querySelector('.wallet-request__status').textContent = status
|
clone.href = `#/transaction?transactionId=${vectorClock}&type=wallet`;
|
||||||
return clone
|
clone.querySelector('.wallet-request__icon').innerHTML = mode === 'cash-to-token' ?
|
||||||
|
`<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none" /><path d="M21 18v1c0 1.1-.9 2-2 2H5c-1.11 0-2-.9-2-2V5c0-1.1.89-2 2-2h14c1.1 0 2 .9 2 2v1h-9c-1.11 0-2 .9-2 2v8c0 1.1.89 2 2 2h9zm-9-2h10V8H12v8zm4-2.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" /></svg>`
|
||||||
|
:
|
||||||
|
`<svg class="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24" /></g><g><g><rect height="7" width="3" x="4" y="10" /><rect height="7" width="3" x="10.5" y="10" /><rect height="3" width="20" x="2" y="19" /><rect height="7" width="3" x="17" y="10" /><polygon points="12,1 2,6 2,8 22,8 22,6" /></g></g></svg>`;
|
||||||
|
clone.querySelector('.wallet-request__details').textContent = type;
|
||||||
|
clone.querySelector('.wallet-request__amount').textContent = formatAmount(amount);
|
||||||
|
clone.querySelector('.wallet-request__time').textContent = getFormattedTime(time);
|
||||||
|
let icon = '';
|
||||||
|
if (status === 'REJECTED') {
|
||||||
|
icon = `<svg class="icon failed" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>`
|
||||||
|
clone.querySelector('.wallet-request__status').innerHTML = `Failed ${icon}`;
|
||||||
|
}
|
||||||
|
return clone;
|
||||||
},
|
},
|
||||||
paymentRequestCard(details) {
|
paymentRequestCard(details) {
|
||||||
const { time, senderID, message: { amount, remark }, note, vectorClock } = details;
|
const { time, senderID, message: { amount, remark }, note, vectorClock } = details;
|
||||||
const clone = getRef('payment_request_template').content.cloneNode(true).firstElementChild;
|
const clone = getRef(`${note ? 'processed' : 'pending'}_payment_request_template`).content.cloneNode(true).firstElementChild;
|
||||||
clone.id = vectorClock
|
clone.dataset.vc = vectorClock;
|
||||||
clone.querySelector('.payment-request__requestor').textContent = senderID
|
clone.querySelector('.payment-request__requestor').textContent = getFloIdTitle(senderID);
|
||||||
clone.querySelector('.payment-request__time').textContent = getFormattedTime(time)
|
clone.querySelector('.payment-request__remark').textContent = remark;
|
||||||
clone.querySelector('.payment-request__amount').textContent = amount.toLocaleString(`en-IN`, { style: 'currency', currency: 'INR' })
|
clone.querySelector('.payment-request__time').textContent = getFormattedTime(time);
|
||||||
clone.querySelector('.payment-request__remark').textContent = remark
|
clone.querySelector('.payment-request__amount').textContent = amount.toLocaleString(`en-IN`, { style: 'currency', currency: 'INR' });
|
||||||
|
const status = note ? note.split(':')[0] : 'PENDING';
|
||||||
let status = note;
|
if (note) {
|
||||||
if (status)
|
clone.firstElementChild.href = `#/transaction?transactionId=${vectorClock}&type=request`;
|
||||||
clone.querySelector('.payment-request__actions').textContent = note;
|
let icon
|
||||||
else
|
if (status === 'PAID')
|
||||||
clone.querySelector('.payment-request__actions').innerHTML =
|
icon = `<svg class="icon paid" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><path d="M23,12l-2.44-2.79l0.34-3.69l-3.61-0.82L15.4,1.5L12,2.96L8.6,1.5L6.71,4.69L3.1,5.5L3.44,9.2L1,12l2.44,2.79l-0.34,3.7 l3.61,0.82L8.6,22.5l3.4-1.47l3.4,1.46l1.89-3.19l3.61-0.82l-0.34-3.69L23,12z M10.09,16.72l-3.8-3.81l1.48-1.48l2.32,2.33 l5.85-5.87l1.48,1.48L10.09,16.72z"/></g></svg>`
|
||||||
`<button class="button" onclick="userUI.payRequest('${vectorClock}')">Pay</button>
|
else
|
||||||
<button class="button" onclick="userUI.declineRequest('${vectorClock}')">Decline</button>`;
|
icon = `<svg class="icon declined" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69 16.9C4.63 15.55 4 13.85 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1C19.37 8.45 20 10.15 20 12c0 4.42-3.58 8-8 8z"/></svg>`
|
||||||
|
clone.querySelector('.payment-request__status').innerHTML = `${status.toLowerCase()} ${icon}`;
|
||||||
return clone
|
}
|
||||||
|
return clone;
|
||||||
},
|
},
|
||||||
|
transactionMessage(details) {
|
||||||
|
const { tokenAmount, time, sender, receiver } = tokenAPI.util.parseTxData(details)
|
||||||
|
let messageType = sender === receiver ? 'self' : sender === myFloID ? 'sent' : 'received';
|
||||||
|
const clone = getRef('transaction_message_template').content.cloneNode(true).firstElementChild;
|
||||||
|
clone.classList.add(messageType);
|
||||||
|
clone.querySelector('.transaction-message__amount').textContent = formatAmount(tokenAmount);
|
||||||
|
clone.querySelector('.transaction-message__time').textContent = getFormattedTime(time * 1000);
|
||||||
|
return clone;
|
||||||
|
},
|
||||||
|
savedUpiId(upiId) {
|
||||||
|
const clone = getRef('saved_upi_template').content.cloneNode(true).firstElementChild;
|
||||||
|
clone.dataset.upiId = upiId;
|
||||||
|
clone.querySelector('.saved-upi__id').textContent = upiId;
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function buttonLoader(id, show) {
|
||||||
|
getRef(id).disabled = show;
|
||||||
|
const animOptions = {
|
||||||
|
duration: 200,
|
||||||
|
fill: 'forwards',
|
||||||
|
easing: 'ease'
|
||||||
|
}
|
||||||
|
if (show) {
|
||||||
|
getRef(id).animate([
|
||||||
|
{
|
||||||
|
clipPath: 'circle(100%)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
clipPath: 'circle(0)',
|
||||||
|
},
|
||||||
|
], animOptions).onfinish = e => {
|
||||||
|
e.target.commitStyles()
|
||||||
|
e.target.cancel()
|
||||||
|
}
|
||||||
|
getRef(id).parentNode.append(createElement('sm-spinner'))
|
||||||
|
} else {
|
||||||
|
getRef(id).style = ''
|
||||||
|
const potentialTarget = getRef(id).parentNode.querySelector('sm-spinner')
|
||||||
|
if (potentialTarget) potentialTarget.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentUserAction
|
function getArrayOfSavedIds() {
|
||||||
|
const arr = [];
|
||||||
|
for (const key in floGlobals.savedIds) {
|
||||||
|
arr.push({
|
||||||
|
floID: key,
|
||||||
|
details: floGlobals.savedIds[key]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return arr.sort((a, b) => a.details.title.localeCompare(b.details.title));
|
||||||
|
}
|
||||||
|
userUI.renderSavedIds = async function () {
|
||||||
|
const frag = document.createDocumentFragment();
|
||||||
|
await organizeSyncedData('savedIds');
|
||||||
|
getArrayOfSavedIds().forEach(({ floID, details }) => {
|
||||||
|
frag.append(render.savedId(floID, details));
|
||||||
|
})
|
||||||
|
getRef('saved_ids_list').append(frag);
|
||||||
|
}
|
||||||
|
async function saveId() {
|
||||||
|
const floID = getRef('flo_id_to_save').value.trim();
|
||||||
|
if (floGlobals.savedIds.hasOwnProperty(floID))
|
||||||
|
return notify('This FLO ID is already saved', 'error');
|
||||||
|
const title = getRef('flo_id_title_to_save').value.trim();
|
||||||
|
floGlobals.savedIds[floID] = { title }
|
||||||
|
syncUserData('savedIds', floGlobals.savedIds).then(() => {
|
||||||
|
insertElementAlphabetically(title, render.savedId(floID, { title }))
|
||||||
|
notify(`Saved ${floID}`, 'success');
|
||||||
|
hidePopup();
|
||||||
|
}).catch(error => {
|
||||||
|
notify(error, 'error');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
delegate(getRef('saved_ids_list'), 'click', '.saved-id', e => {
|
||||||
|
if (e.target.closest('.edit-saved')) {
|
||||||
|
const target = e.target.closest('.saved-id');
|
||||||
|
getRef('edit_saved_id').setAttribute('value', target.dataset.floId);
|
||||||
|
getRef('get_new_title').value = getFloIdTitle(target.dataset.floId);
|
||||||
|
showPopup('edit_saved_popup');
|
||||||
|
} else if (e.target.closest('.copy-saved-id')) {
|
||||||
|
const target = e.target.closest('.saved-id');
|
||||||
|
navigator.clipboard.writeText(target.dataset.floId)
|
||||||
|
target.dispatchEvent(
|
||||||
|
new CustomEvent('copy', {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const target = e.target.closest('.saved-id');
|
||||||
|
window.location.hash = `#/contact?floId=${target.dataset.floId}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function saveIdChanges() {
|
||||||
|
const floID = getRef('edit_saved_id').value;
|
||||||
|
let title = getRef('get_new_title').value.trim();
|
||||||
|
if (title == '')
|
||||||
|
title = 'Unknown';
|
||||||
|
floGlobals.savedIds[floID] = { title }
|
||||||
|
syncUserData('savedIds', floGlobals.savedIds).then(() => {
|
||||||
|
const potentialTarget = getRef('saved_ids_list').querySelector(`.saved-id[data-flo-id="${floID}"]`)
|
||||||
|
if (potentialTarget) {
|
||||||
|
potentialTarget.querySelector('.saved-id__title').textContent = title;
|
||||||
|
potentialTarget.querySelector('.saved-id__initials').textContent = title.charAt(0).toUpperCase();
|
||||||
|
// place the renamed card in alphabetically correct position
|
||||||
|
const clone = potentialTarget.cloneNode(true);
|
||||||
|
potentialTarget.remove();
|
||||||
|
insertElementAlphabetically(title, clone)
|
||||||
|
}
|
||||||
|
hidePopup();
|
||||||
|
}).catch(error => {
|
||||||
|
notify(error, 'error');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function deleteSavedId() {
|
||||||
|
getConfirmation('Do you want delete this FLO ID?', {
|
||||||
|
confirmText: 'Delete',
|
||||||
|
}).then(res => {
|
||||||
|
if (res) {
|
||||||
|
const toDelete = getRef('saved_ids_list').querySelector(`.saved-id[data-flo-id="${getRef('edit_saved_id').value}"]`);
|
||||||
|
if (toDelete)
|
||||||
|
toDelete.remove();
|
||||||
|
delete floGlobals.savedIds[getRef('edit_saved_id').value];
|
||||||
|
hidePopup();
|
||||||
|
syncUserData('savedIds', floGlobals.savedIds).then(() => {
|
||||||
|
notify(`Deleted saved ID`, 'success');
|
||||||
|
}).catch(error => {
|
||||||
|
notify(error, 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const savedIdsObserver = new MutationObserver((mutationList) => {
|
||||||
|
mutationList.forEach(mutation => {
|
||||||
|
getRef('saved_ids_tip').textContent = mutation.target.children.length === 0 ? `Click 'Add FLO ID' to add a new FLO ID.` : `Tap on saved IDs to see transaction history.`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
savedIdsObserver.observe(getRef('saved_ids_list'), {
|
||||||
|
childList: true,
|
||||||
|
})
|
||||||
|
function insertElementAlphabetically(name, elementToInsert) {
|
||||||
|
const elementInserted = [...getRef('saved_ids_list').children].some(child => {
|
||||||
|
const floID = child.dataset.floId;
|
||||||
|
if (floGlobals.savedIds[floID].title.localeCompare(name) > 0) {
|
||||||
|
child.before(elementToInsert)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!elementInserted) {
|
||||||
|
getRef('saved_ids_list').append(elementToInsert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentUserAction;
|
||||||
function showTokenTransfer(type) {
|
function showTokenTransfer(type) {
|
||||||
getRef('tt_button').textContent = type;
|
getRef('tt_button').textContent = type;
|
||||||
currentUserAction = type
|
currentUserAction = type;
|
||||||
if (type === 'send') {
|
if (type === 'send') {
|
||||||
getRef('token_transfer__title').textContent = 'Send money to FLO ID';
|
getRef('token_transfer__title').textContent = 'Send money to FLO ID';
|
||||||
} else {
|
} else {
|
||||||
getRef('token_transfer__title').textContent = 'Request money from FLO ID';
|
getRef('token_transfer__title').textContent = 'Request money from FLO ID';
|
||||||
}
|
}
|
||||||
showPopup('token_transfer_popup')
|
if (pagesData.lastPage === 'contact') {
|
||||||
|
getRef('token_transfer__receiver').value = pagesData.params.floId;
|
||||||
|
getRef('token_transfer__receiver').readOnly = true;
|
||||||
|
} else {
|
||||||
|
getRef('token_transfer__receiver').readOnly = false;
|
||||||
|
}
|
||||||
|
showPopup('token_transfer_popup');
|
||||||
|
if (pagesData.lastPage === 'contact') {
|
||||||
|
getRef('token_transfer__amount').focusIn();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeUserAction() {
|
function executeUserAction() {
|
||||||
const floID = getRef('tt_flo_id').value.trim(),
|
const floID = getRef('token_transfer__receiver').value.trim(),
|
||||||
amount = parseFloat(getRef('tt_amount').value),
|
amount = parseFloat(getRef('token_transfer__amount').value),
|
||||||
remark = getRef('tt_remark').value.trim();
|
remark = getRef('tt_remark').value.trim();
|
||||||
if (currentUserAction === 'send') {
|
if (currentUserAction === 'send') {
|
||||||
userUI.sendMoneyToUser(floID, amount, remark)
|
userUI.sendMoneyToUser(floID, amount, remark);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
userUI.requestMoneyFromUser(floID, amount, remark)
|
userUI.requestMoneyFromUser(floID, amount, remark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeUpi() {
|
function changeUpi() {
|
||||||
const upiID = getRef('upi_id').value.trim()
|
const upiId = getRef('upi_id').value.trim();
|
||||||
Cashier.updateUPI(upiID).then(() => {
|
Cashier.updateUPI(upiId).then(() => {
|
||||||
notify('UPI ID updated successfully', 'success')
|
notify('UPI ID updated successfully', 'success');
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
notify(err, 'error')
|
notify(err, 'error');
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
function getSignedIn() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (window.location.hash.includes('sign_in') || window.location.hash.includes('sign_up')) {
|
||||||
|
showPage(window.location.hash);
|
||||||
|
} else {
|
||||||
|
location.hash = `#/sign_in`;
|
||||||
|
}
|
||||||
|
getRef('sign_in_button').onclick = () => {
|
||||||
|
resolve(getRef('private_key_field').value.trim());
|
||||||
|
getRef('private_key_field').value = '';
|
||||||
|
showPage('loading');
|
||||||
|
};
|
||||||
|
getRef('sign_up_button').onclick = () => {
|
||||||
|
resolve(getRef('generated_private_key').value.trim());
|
||||||
|
getRef('generated_private_key').value = '';
|
||||||
|
showPage('loading');
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
function signOut() {
|
function signOut() {
|
||||||
getConfirmation('Sign out?', 'You are about to sign out of the app, continue?', 'Stay', 'Leave')
|
getConfirmation('Sign out?', 'You are about to sign out of the app, continue?', 'Stay', 'Leave')
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
await floDapps.clearCredentials()
|
await floDapps.clearCredentials();
|
||||||
location.reload()
|
location.reload();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
@ -7496,7 +7496,7 @@
|
|||||||
return reject("Invalid amount");
|
return reject("Invalid amount");
|
||||||
this.getBalance(senderID, token).then(bal => {
|
this.getBalance(senderID, token).then(bal => {
|
||||||
if (amount > bal)
|
if (amount > bal)
|
||||||
return reject("Insufficiant token balance");
|
return reject(`Insufficiant ${token} balance`);
|
||||||
floBlockchainAPI.writeData(senderID, `send ${amount} ${token}# ${message}`, privKey, receiverID)
|
floBlockchainAPI.writeData(senderID, `send ${amount} ${token}# ${message}`, privKey, receiverID)
|
||||||
.then(txid => resolve(txid))
|
.then(txid => resolve(txid))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
/*jshint esversion: 6 */
|
/*jshint esversion: 9 */
|
||||||
// Global variables
|
// Global variables
|
||||||
const domRefs = {};
|
const domRefs = {};
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
|
let paymentsHistoryLoader = null;
|
||||||
|
let walletHistoryLoader = null;
|
||||||
|
let contactHistoryLoader = null;
|
||||||
|
let paymentRequestsLoader = null;
|
||||||
|
|
||||||
//Checks for internet connection status
|
//Checks for internet connection status
|
||||||
if (!navigator.onLine)
|
if (!navigator.onLine)
|
||||||
@ -85,9 +89,9 @@ function hidePopup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('popupopened', async e => {
|
document.addEventListener('popupopened', async e => {
|
||||||
|
const frag = document.createDocumentFragment()
|
||||||
switch (e.target.id) {
|
switch (e.target.id) {
|
||||||
case 'saved_ids_popup':
|
case 'saved_ids_popup':
|
||||||
const frag = document.createDocumentFragment()
|
|
||||||
const allSavedIds = await getArrayOfSavedIds()
|
const allSavedIds = await getArrayOfSavedIds()
|
||||||
allSavedIds.forEach(({ floID, name }) => {
|
allSavedIds.forEach(({ floID, name }) => {
|
||||||
frag.append(render.savedIdPickerCard(floID, name))
|
frag.append(render.savedIdPickerCard(floID, name))
|
||||||
@ -96,7 +100,22 @@ document.addEventListener('popupopened', async e => {
|
|||||||
getRef('saved_ids_picker_list').append(frag)
|
getRef('saved_ids_picker_list').append(frag)
|
||||||
getRef('search_saved_ids_picker').focusIn()
|
getRef('search_saved_ids_picker').focusIn()
|
||||||
break;
|
break;
|
||||||
case 'get_private_key_popup':
|
case 'withdraw_wallet_popup':
|
||||||
|
let hasSavedIds = false
|
||||||
|
for (const upiId in floGlobals.savedUserData.upiIds) {
|
||||||
|
frag.append(createElement('sm-option', {
|
||||||
|
textContent: upiId,
|
||||||
|
attributes: {
|
||||||
|
value: upiId,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
hasSavedIds = true
|
||||||
|
}
|
||||||
|
if (hasSavedIds) {
|
||||||
|
getRef('select_upi_id').parentNode.classList.remove('hide')
|
||||||
|
getRef('select_upi_id').append(frag)
|
||||||
|
}
|
||||||
|
showProcessStage('withdraw_wallet_process', 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -107,14 +126,12 @@ document.addEventListener('popupclosed', e => {
|
|||||||
getRef('saved_ids_picker_list').innerHTML = ''
|
getRef('saved_ids_picker_list').innerHTML = ''
|
||||||
getRef('search_saved_ids_picker').value = ''
|
getRef('search_saved_ids_picker').value = ''
|
||||||
break;
|
break;
|
||||||
case 'get_private_key_popup':
|
case 'topup_wallet_popup':
|
||||||
getRef('get_private_key').classList.remove('hide')
|
showProcessStage('topup_wallet_process', 0)
|
||||||
getRef('transaction_result').classList.add('hide')
|
|
||||||
getRef('confirm_transaction_button').classList.remove('hide')
|
|
||||||
getRef('confirm_transaction_button').nextElementSibling.classList.add('hide')
|
|
||||||
break;
|
break;
|
||||||
case 'retrieve_flo_id_popup':
|
case 'withdraw_wallet_popup':
|
||||||
getRef('recovered_flo_id_wrapper').classList.add('hide')
|
getRef('select_upi_id').parentNode.classList.add('hide')
|
||||||
|
getRef('select_upi_id').innerHTML = ''
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -143,7 +160,6 @@ const getConfirmation = (title, options = {}) => {
|
|||||||
|
|
||||||
//Function for displaying toast notifications. pass in error for mode param if you want to show an error.
|
//Function for displaying toast notifications. pass in error for mode param if you want to show an error.
|
||||||
function notify(message, mode, options = {}) {
|
function notify(message, mode, options = {}) {
|
||||||
const { pinned = false, sound = false } = options
|
|
||||||
let icon
|
let icon
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'success':
|
case 'success':
|
||||||
@ -153,7 +169,7 @@ function notify(message, mode, options = {}) {
|
|||||||
icon = `<svg class="icon icon--error" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm0-8v6h2V7h-2z"/></svg>`
|
icon = `<svg class="icon icon--error" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm0-8v6h2V7h-2z"/></svg>`
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
getRef("notification_drawer").push(message, { pinned, icon });
|
getRef("notification_drawer").push(message, { icon, ...options });
|
||||||
if (mode === 'error') {
|
if (mode === 'error') {
|
||||||
console.error(message)
|
console.error(message)
|
||||||
}
|
}
|
||||||
@ -186,7 +202,7 @@ function getFormattedTime(time, format) {
|
|||||||
return `${month} ${date}, ${year}`;
|
return `${month} ${date}, ${year}`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return `${month} ${date} ${year}, ${finalHours}`;
|
return `${month} ${date}, ${year} at ${finalHours}`;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -253,17 +269,21 @@ function createRipple(event, target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pagesData = {
|
const pagesData = {
|
||||||
params: {}
|
params: {},
|
||||||
|
openedPages: new Set(),
|
||||||
}
|
}
|
||||||
|
|
||||||
let tempData
|
|
||||||
async function showPage(targetPage, options = {}) {
|
async function showPage(targetPage, options = {}) {
|
||||||
const { firstLoad, hashChange, isPreview } = options
|
const { firstLoad, hashChange } = options
|
||||||
let pageId
|
let pageId
|
||||||
let params = {}
|
let params = {}
|
||||||
let searchParams
|
let searchParams
|
||||||
if (targetPage === '') {
|
if (targetPage === '') {
|
||||||
pageId = 'home'
|
if (typeof myFloID === "undefined") {
|
||||||
|
pageId = 'sign_in'
|
||||||
|
} else {
|
||||||
|
pageId = 'home'
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (targetPage.includes('/')) {
|
if (targetPage.includes('/')) {
|
||||||
if (targetPage.includes('?')) {
|
if (targetPage.includes('?')) {
|
||||||
@ -281,115 +301,308 @@ async function showPage(targetPage, options = {}) {
|
|||||||
pageId = targetPage
|
pageId = targetPage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (typeof myFloID === "undefined" && !(['sign_up', 'sign_in', 'loading', 'landing'].includes(pageId))) return
|
||||||
|
else if (typeof myFloID !== "undefined" && (['sign_up', 'sign_in', 'loading', 'landing'].includes(pageId))) {
|
||||||
|
history.replaceState(null, null, '#/home');
|
||||||
|
pageId = 'home'
|
||||||
|
}
|
||||||
if (searchParams) {
|
if (searchParams) {
|
||||||
const urlSearchParams = new URLSearchParams('?' + searchParams);
|
const urlSearchParams = new URLSearchParams('?' + searchParams);
|
||||||
params = Object.fromEntries(urlSearchParams.entries());
|
params = Object.fromEntries(urlSearchParams.entries());
|
||||||
}
|
}
|
||||||
|
switch (pageId) {
|
||||||
|
case 'sign_in':
|
||||||
|
setTimeout(() => {
|
||||||
|
getRef('private_key_field').focusIn()
|
||||||
|
}, 0);
|
||||||
|
targetPage = 'sign_in'
|
||||||
|
break;
|
||||||
|
case 'sign_up':
|
||||||
|
const { floID, privKey } = floCrypto.generateNewID()
|
||||||
|
getRef('generated_flo_id').value = floID
|
||||||
|
getRef('generated_private_key').value = privKey
|
||||||
|
targetPage = 'sign_up'
|
||||||
|
break;
|
||||||
|
case 'contact':
|
||||||
|
getRef('contact__title').textContent = getFloIdTitle(params.floId)
|
||||||
|
getRef('contact__transactions').innerHTML = '<sm-spinner></sm-spinner>'
|
||||||
|
Promise.all([
|
||||||
|
tokenAPI.fetch_api(`api/v1.0/getTokenTransactions?token=rupee&senderFloAddress=${myFloID}&destFloAddress=${params.floId}`),
|
||||||
|
tokenAPI.fetch_api(`api/v1.0/getTokenTransactions?token=rupee&senderFloAddress=${params.floId}&destFloAddress=${myFloID}`)])
|
||||||
|
.then(([sentTransactions, receivedTransactions]) => {
|
||||||
|
const allTransactions = Object.values({ ...sentTransactions.transactions, ...receivedTransactions.transactions }).sort((a, b) => b.transactionDetails.time - a.transactionDetails.time)
|
||||||
|
if (contactHistoryLoader) {
|
||||||
|
contactHistoryLoader.update(allTransactions)
|
||||||
|
} else {
|
||||||
|
contactHistoryLoader = new LazyLoader('#contact__transactions', allTransactions, render.transactionMessage, { bottomFirst: true });
|
||||||
|
}
|
||||||
|
contactHistoryLoader.init()
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
case 'history':
|
||||||
|
const paymentTransactions = []
|
||||||
|
if (paymentsHistoryLoader)
|
||||||
|
paymentsHistoryLoader.clear()
|
||||||
|
getRef('payments_history').innerHTML = '<sm-spinner></sm-spinner>';
|
||||||
|
tokenAPI.getAllTxs(myFloID).then(({ transactions }) => {
|
||||||
|
for (const transactionId in transactions) {
|
||||||
|
paymentTransactions.push({
|
||||||
|
...tokenAPI.util.parseTxData(transactions[transactionId]),
|
||||||
|
txid: transactionId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (paymentsHistoryLoader) {
|
||||||
|
paymentsHistoryLoader.update(paymentTransactions);
|
||||||
|
} else {
|
||||||
|
paymentsHistoryLoader = new LazyLoader('#payments_history', paymentTransactions, render.transactionCard);
|
||||||
|
}
|
||||||
|
paymentsHistoryLoader.init();
|
||||||
|
}).catch(e => {
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
case 'requests':
|
||||||
|
const paymentRequests = [];
|
||||||
|
if (paymentRequestsLoader)
|
||||||
|
paymentRequestsLoader.clear();
|
||||||
|
|
||||||
|
const pendingPaymentRequests = document.createDocumentFragment();
|
||||||
|
let arePaymentsPending = false
|
||||||
|
for (const transactionId in User.moneyRequests) {
|
||||||
|
if (!User.moneyRequests[transactionId].note) {
|
||||||
|
arePaymentsPending = true
|
||||||
|
pendingPaymentRequests.prepend(render.paymentRequestCard(User.moneyRequests[transactionId]))
|
||||||
|
} else {
|
||||||
|
paymentRequests.unshift(User.moneyRequests[transactionId])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paymentRequestsLoader) {
|
||||||
|
paymentRequestsLoader.update(paymentRequests)
|
||||||
|
} else {
|
||||||
|
paymentRequestsLoader = new LazyLoader('#payment_request_history', paymentRequests, render.paymentRequestCard);
|
||||||
|
pendingTransactionsObserver.observe(getRef('pending_payment_requests'), { childList: true });
|
||||||
|
}
|
||||||
|
if (arePaymentsPending) {
|
||||||
|
getRef('pending_payment_requests').innerHTML = ''
|
||||||
|
getRef('pending_payment_requests').append(pendingPaymentRequests)
|
||||||
|
}
|
||||||
|
paymentRequestsLoader.init()
|
||||||
|
break;
|
||||||
|
case 'wallet':
|
||||||
|
const walletTransactions = []
|
||||||
|
if (walletHistoryLoader)
|
||||||
|
walletHistoryLoader.clear()
|
||||||
|
const pendingWalletTransactions = document.createDocumentFragment()
|
||||||
|
|
||||||
|
let areTransactionsPending = false
|
||||||
|
for (const transactionId in User.cashierRequests) {
|
||||||
|
if (!User.cashierRequests[transactionId].note) {
|
||||||
|
areTransactionsPending = true
|
||||||
|
pendingWalletTransactions.prepend(render.walletRequestCard(User.cashierRequests[transactionId]))
|
||||||
|
} else {
|
||||||
|
walletTransactions.unshift(User.cashierRequests[transactionId])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (walletHistoryLoader) {
|
||||||
|
walletHistoryLoader.update(walletTransactions)
|
||||||
|
} else {
|
||||||
|
walletHistoryLoader = new LazyLoader('#wallet_history', walletTransactions, render.walletRequestCard);
|
||||||
|
pendingTransactionsObserver.observe(getRef('pending_wallet_transactions'), { childList: true });
|
||||||
|
}
|
||||||
|
if (areTransactionsPending) {
|
||||||
|
getRef('pending_wallet_transactions').innerHTML = ''
|
||||||
|
getRef('pending_wallet_transactions').append(pendingWalletTransactions)
|
||||||
|
}
|
||||||
|
walletHistoryLoader.init()
|
||||||
|
break;
|
||||||
|
case 'transaction':
|
||||||
|
let transactionDetails
|
||||||
|
let status
|
||||||
|
getRef('transaction__link').classList.add('hide')
|
||||||
|
getRef('transaction__remark').classList.add('hide')
|
||||||
|
getRef('transaction__note').classList.add('hide')
|
||||||
|
if (params.type === 'request') {
|
||||||
|
transactionDetails = User.moneyRequests[params.transactionId]
|
||||||
|
const { message: { remark }, note, tag } = transactionDetails
|
||||||
|
status = note ? note.split(':')[0] : 'PENDING';
|
||||||
|
getRef('transaction__type').textContent = 'Payment request'
|
||||||
|
if (status === 'PAID') {
|
||||||
|
getRef('transaction__link').href = `https://flosight.duckdns.org/tx/${note.split(':')[1].trim()}`
|
||||||
|
getRef('transaction__link').classList.remove('hide')
|
||||||
|
}
|
||||||
|
if (remark !== '') {
|
||||||
|
getRef('transaction__remark').textContent = remark
|
||||||
|
getRef('transaction__remark').classList.remove('hide')
|
||||||
|
}
|
||||||
|
} else if (params.type === 'wallet') {
|
||||||
|
transactionDetails = User.cashierRequests[params.transactionId]
|
||||||
|
const { message: { amount, mode, upi_id, upi_txid }, note, tag } = transactionDetails
|
||||||
|
status = tag ? tag : (note ? 'REJECTED' : "PENDING");
|
||||||
|
getRef('transaction__type').textContent = mode === 'cash-to-token' ? 'Wallet top-up' : 'Transfer to bank';
|
||||||
|
if (status === 'COMPLETED') {
|
||||||
|
getRef('transaction__link').href = `https://flosight.duckdns.org/tx/${note}`
|
||||||
|
getRef('transaction__link').classList.remove('hide')
|
||||||
|
} else if (status === 'REJECTED') {
|
||||||
|
getRef('transaction__note').innerHTML = `<svg class="icon failed" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"></path><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></svg> ${note.split(':')[1]}`
|
||||||
|
getRef('transaction__note').classList.remove('hide')
|
||||||
|
}
|
||||||
|
if (mode === 'cash-to-token') {
|
||||||
|
getRef('transaction__note').textContent = `UPI transaction ID: ${upi_txid}`
|
||||||
|
getRef('transaction__note').classList.remove('hide')
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (status === 'PENDING') {
|
||||||
|
getRef('transaction__note').textContent = `Pending transfer of ${formatAmount(amount)} to bank account linked to ${upi_id}`
|
||||||
|
} else if (status === 'COMPLETED') {
|
||||||
|
getRef('transaction__note').textContent = `Transfer of ${formatAmount(amount)} to bank account linked to ${upi_id} completed`
|
||||||
|
}
|
||||||
|
getRef('transaction__note').classList.remove('hide')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { message: { amount }, time } = transactionDetails
|
||||||
|
getRef('transaction__time').textContent = getFormattedTime(time)
|
||||||
|
getRef('transaction__amount').textContent = formatAmount(amount)
|
||||||
|
getRef('transaction__status').textContent = status
|
||||||
|
break;
|
||||||
|
case 'settings':
|
||||||
|
renderSavedUpiIds()
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (pageId !== 'history') {
|
||||||
|
if (paymentsHistoryLoader)
|
||||||
|
paymentsHistoryLoader.clear()
|
||||||
|
}
|
||||||
|
if (pageId !== 'contact') {
|
||||||
|
if (contactHistoryLoader)
|
||||||
|
contactHistoryLoader.clear()
|
||||||
|
}
|
||||||
|
if (pageId !== 'wallet') {
|
||||||
|
if (walletHistoryLoader)
|
||||||
|
walletHistoryLoader.clear()
|
||||||
|
}
|
||||||
|
if (pageId !== 'settings') {
|
||||||
|
getRef('saved_upi_ids_list').innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (pagesData.lastPage !== pageId) {
|
if (pagesData.lastPage !== pageId) {
|
||||||
|
const animOptions = {
|
||||||
|
duration: 100,
|
||||||
|
fill: 'forwards',
|
||||||
|
easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
|
||||||
|
}
|
||||||
|
let previousActiveElement = getRef('main_navbar').querySelector('.nav-item--active')
|
||||||
|
const currentActiveElement = document.querySelector(`.nav-item[href="#/${pageId}"]`)
|
||||||
|
if (currentActiveElement) {
|
||||||
|
if (getRef('main_navbar').classList.contains('hide')) {
|
||||||
|
getRef('main_card').classList.remove('nav-hidden')
|
||||||
|
getRef('main_navbar').classList.remove('hide-away')
|
||||||
|
getRef('main_navbar').classList.remove('hide')
|
||||||
|
getRef('main_navbar').animate([
|
||||||
|
{
|
||||||
|
transform: isMobileView ? `translateY(100%)` : `translateX(-100%)`,
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: `none`,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
], { ...animOptions, easing: 'ease-in' })
|
||||||
|
}
|
||||||
|
const previousActiveElementIndex = [...getRef('main_navbar').querySelectorAll('.nav-item')].indexOf(previousActiveElement)
|
||||||
|
const currentActiveElementIndex = [...getRef('main_navbar').querySelectorAll('.nav-item')].indexOf(currentActiveElement)
|
||||||
|
const isOnTop = previousActiveElementIndex < currentActiveElementIndex
|
||||||
|
const currentIndicator = createElement('div', { className: 'nav-item__indicator' });
|
||||||
|
let previousIndicator = getRef('main_navbar').querySelector('.nav-item__indicator')
|
||||||
|
if (!previousIndicator) {
|
||||||
|
previousIndicator = currentIndicator.cloneNode(true)
|
||||||
|
previousActiveElement = currentActiveElement
|
||||||
|
previousActiveElement.append(previousIndicator)
|
||||||
|
} else if (currentActiveElementIndex !== previousActiveElementIndex) {
|
||||||
|
const indicatorDimensions = previousIndicator.getBoundingClientRect()
|
||||||
|
const currentActiveElementDimensions = currentActiveElement.getBoundingClientRect()
|
||||||
|
let moveBy
|
||||||
|
if (isMobileView) {
|
||||||
|
moveBy = ((currentActiveElementDimensions.width - indicatorDimensions.width) / 2) + indicatorDimensions.width
|
||||||
|
} else {
|
||||||
|
moveBy = ((currentActiveElementDimensions.height - indicatorDimensions.height) / 2) + indicatorDimensions.height
|
||||||
|
}
|
||||||
|
indicatorObserver.observe(previousIndicator)
|
||||||
|
previousIndicator.animate([
|
||||||
|
{
|
||||||
|
transform: 'none',
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: `translate${isMobileView ? 'X' : 'Y'}(${isOnTop ? `${moveBy}px` : `-${moveBy}px`})`,
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
], { ...animOptions, easing: 'ease-in' }).onfinish = () => {
|
||||||
|
previousIndicator.remove()
|
||||||
|
}
|
||||||
|
tempData = {
|
||||||
|
currentActiveElement,
|
||||||
|
currentIndicator,
|
||||||
|
isOnTop,
|
||||||
|
animOptions,
|
||||||
|
moveBy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousActiveElement.classList.remove('nav-item--active');
|
||||||
|
currentActiveElement.classList.add('nav-item--active')
|
||||||
|
} else {
|
||||||
|
if (!getRef('main_navbar').classList.contains('hide')) {
|
||||||
|
getRef('main_card').classList.add('nav-hidden')
|
||||||
|
getRef('main_navbar').classList.add('hide-away')
|
||||||
|
getRef('main_navbar').animate([
|
||||||
|
{
|
||||||
|
transform: `none`,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: isMobileView ? `translateY(100%)` : `translateX(-100%)`,
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
], {
|
||||||
|
duration: 200,
|
||||||
|
fill: 'forwards',
|
||||||
|
easing: 'ease'
|
||||||
|
}).onfinish = () => {
|
||||||
|
getRef('main_navbar').classList.add('hide')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.querySelectorAll('.page').forEach(page => page.classList.add('hide'))
|
||||||
|
getRef(pageId).closest('.page').classList.remove('hide')
|
||||||
|
document.querySelectorAll('.inner-page').forEach(page => page.classList.add('hide'))
|
||||||
|
getRef(pageId).classList.remove('hide')
|
||||||
|
getRef('main_card').style.overflowY = "hidden";
|
||||||
|
getRef(pageId).animate([
|
||||||
|
{
|
||||||
|
opacity: 0,
|
||||||
|
transform: 'translateY(1rem)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
opacity: 1,
|
||||||
|
transform: 'translateY(0)'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
duration: 300,
|
||||||
|
easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
|
||||||
|
}).onfinish = () => {
|
||||||
|
getRef('main_card').style.overflowY = "";
|
||||||
|
}
|
||||||
pagesData.lastPage = pageId
|
pagesData.lastPage = pageId
|
||||||
}
|
}
|
||||||
if (params)
|
if (params)
|
||||||
pagesData.params = params
|
pagesData.params = params
|
||||||
switch (pageId) {
|
pagesData.openedPages.add(pageId)
|
||||||
case 'transactions':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
|
|
||||||
}
|
|
||||||
const animOptions = {
|
|
||||||
duration: 100,
|
|
||||||
fill: 'forwards',
|
|
||||||
}
|
|
||||||
let previousActiveElement = getRef('main_navbar').querySelector('.nav-item--active')
|
|
||||||
const currentActiveElement = document.querySelector(`.nav-item[href="#/${pageId}"]`)
|
|
||||||
if (currentActiveElement) {
|
|
||||||
if (getRef('main_navbar').classList.contains('hide')) {
|
|
||||||
getRef('main_navbar').classList.remove('hide-away')
|
|
||||||
getRef('main_navbar').classList.remove('hide')
|
|
||||||
getRef('main_navbar').animate([
|
|
||||||
{
|
|
||||||
transform: isMobileView ? `translateY(100%)` : `translateX(-100%)`,
|
|
||||||
opacity: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
transform: `none`,
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
], {
|
|
||||||
duration: 100,
|
|
||||||
fill: 'forwards',
|
|
||||||
easing: 'ease'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
getRef('main_header').classList.remove('hide')
|
|
||||||
const previousActiveElementIndex = [...getRef('main_navbar').querySelectorAll('.nav-item')].indexOf(previousActiveElement)
|
|
||||||
const currentActiveElementIndex = [...getRef('main_navbar').querySelectorAll('.nav-item')].indexOf(currentActiveElement)
|
|
||||||
const isOnTop = previousActiveElementIndex < currentActiveElementIndex
|
|
||||||
const currentIndicator = createElement('div', { className: 'nav-item__indicator' });
|
|
||||||
let previousIndicator = getRef('main_navbar').querySelector('.nav-item__indicator')
|
|
||||||
if (!previousIndicator) {
|
|
||||||
previousIndicator = currentIndicator.cloneNode(true)
|
|
||||||
previousActiveElement = currentActiveElement
|
|
||||||
previousActiveElement.append(previousIndicator)
|
|
||||||
} else if (currentActiveElementIndex !== previousActiveElementIndex) {
|
|
||||||
const indicatorDimensions = previousIndicator.getBoundingClientRect()
|
|
||||||
const currentActiveElementDimensions = currentActiveElement.getBoundingClientRect()
|
|
||||||
let moveBy
|
|
||||||
if (isMobileView) {
|
|
||||||
moveBy = ((currentActiveElementDimensions.width - indicatorDimensions.width) / 2) + indicatorDimensions.width
|
|
||||||
} else {
|
|
||||||
moveBy = ((currentActiveElementDimensions.height - indicatorDimensions.height) / 2) + indicatorDimensions.height
|
|
||||||
}
|
|
||||||
indicatorObserver.observe(previousIndicator)
|
|
||||||
previousIndicator.animate([
|
|
||||||
{
|
|
||||||
transform: 'none',
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
transform: `translate${isMobileView ? 'X' : 'Y'}(${isOnTop ? `${moveBy}px` : `-${moveBy}px`})`,
|
|
||||||
opacity: 0,
|
|
||||||
},
|
|
||||||
], { ...animOptions, easing: 'ease-in' }).onfinish = () => {
|
|
||||||
previousIndicator.remove()
|
|
||||||
}
|
|
||||||
tempData = {
|
|
||||||
currentActiveElement,
|
|
||||||
currentIndicator,
|
|
||||||
isOnTop,
|
|
||||||
animOptions,
|
|
||||||
moveBy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
previousActiveElement.classList.remove('nav-item--active');
|
|
||||||
currentActiveElement.classList.add('nav-item--active')
|
|
||||||
} else {
|
|
||||||
if (!getRef('main_navbar').classList.contains('hide')) {
|
|
||||||
getRef('main_navbar').classList.add('hide-away')
|
|
||||||
getRef('main_navbar').animate([
|
|
||||||
{
|
|
||||||
transform: `none`,
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
transform: isMobileView ? `translateY(100%)` : `translateX(-100%)`,
|
|
||||||
opacity: 0,
|
|
||||||
},
|
|
||||||
], {
|
|
||||||
duration: 200,
|
|
||||||
fill: 'forwards',
|
|
||||||
easing: 'ease'
|
|
||||||
}).onfinish = () => {
|
|
||||||
getRef('main_navbar').classList.add('hide')
|
|
||||||
}
|
|
||||||
getRef('main_header').classList.add('hide')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.querySelectorAll('.page').forEach(page => page.classList.add('hide'))
|
|
||||||
getRef(pageId).classList.remove('hide')
|
|
||||||
getRef(pageId).animate([{ opacity: 0 }, { opacity: 1 }], { duration: 300, fill: 'forwards', easing: 'ease' })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const indicatorObserver = new IntersectionObserver(entries => {
|
const indicatorObserver = new IntersectionObserver(entries => {
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
if (!entry.isIntersecting) {
|
if (!entry.isIntersecting) {
|
||||||
@ -414,7 +627,7 @@ const indicatorObserver = new IntersectionObserver(entries => {
|
|||||||
// class based lazy loading
|
// class based lazy loading
|
||||||
class LazyLoader {
|
class LazyLoader {
|
||||||
constructor(container, elementsToRender, renderFn, options = {}) {
|
constructor(container, elementsToRender, renderFn, options = {}) {
|
||||||
const { batchSize = 10, freshRender } = options
|
const { batchSize = 10, freshRender, bottomFirst = false } = options
|
||||||
|
|
||||||
this.elementsToRender = elementsToRender
|
this.elementsToRender = elementsToRender
|
||||||
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
|
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
|
||||||
@ -423,6 +636,7 @@ class LazyLoader {
|
|||||||
|
|
||||||
this.batchSize = batchSize
|
this.batchSize = batchSize
|
||||||
this.freshRender = freshRender
|
this.freshRender = freshRender
|
||||||
|
this.bottomFirst = bottomFirst
|
||||||
|
|
||||||
this.lazyContainer = document.querySelector(container)
|
this.lazyContainer = document.querySelector(container)
|
||||||
|
|
||||||
@ -446,7 +660,10 @@ class LazyLoader {
|
|||||||
mutationList.forEach(mutation => {
|
mutationList.forEach(mutation => {
|
||||||
if (mutation.type === 'childList') {
|
if (mutation.type === 'childList') {
|
||||||
if (mutation.addedNodes.length) {
|
if (mutation.addedNodes.length) {
|
||||||
this.intersectionObserver.observe(this.lazyContainer.lastElementChild)
|
if (this.bottomFirst)
|
||||||
|
this.intersectionObserver.observe(this.lazyContainer.firstElementChild)
|
||||||
|
else
|
||||||
|
this.intersectionObserver.observe(this.lazyContainer.lastElementChild)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -458,7 +675,6 @@ class LazyLoader {
|
|||||||
}
|
}
|
||||||
update(elementsToRender) {
|
update(elementsToRender) {
|
||||||
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
|
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
|
||||||
this.render()
|
|
||||||
}
|
}
|
||||||
render(options = {}) {
|
render(options = {}) {
|
||||||
let { lazyLoad = false } = options
|
let { lazyLoad = false } = options
|
||||||
@ -472,10 +688,21 @@ class LazyLoader {
|
|||||||
this.updateStartIndex = 0
|
this.updateStartIndex = 0
|
||||||
this.updateEndIndex = this.arrayOfElements.length > this.batchSize ? this.batchSize : this.arrayOfElements.length
|
this.updateEndIndex = this.arrayOfElements.length > this.batchSize ? this.batchSize : this.arrayOfElements.length
|
||||||
}
|
}
|
||||||
for (let index = this.updateStartIndex; index < this.updateEndIndex; index++) {
|
if (this.bottomFirst) {
|
||||||
frag.append(this.renderFn(this.arrayOfElements[index]))
|
for (let index = this.updateStartIndex; index < this.updateEndIndex; index++) {
|
||||||
|
frag.prepend(this.renderFn(this.arrayOfElements[index]))
|
||||||
|
}
|
||||||
|
this.lazyContainer.prepend(frag)
|
||||||
|
} else {
|
||||||
|
for (let index = this.updateStartIndex; index < this.updateEndIndex; index++) {
|
||||||
|
frag.append(this.renderFn(this.arrayOfElements[index]))
|
||||||
|
}
|
||||||
|
this.lazyContainer.append(frag)
|
||||||
}
|
}
|
||||||
this.lazyContainer.append(frag)
|
if (!lazyLoad && this.bottomFirst)
|
||||||
|
this.lazyContainer.scrollTo({
|
||||||
|
top: this.lazyContainer.scrollHeight,
|
||||||
|
})
|
||||||
// Callback to be called if elements are updated or rendered for first time
|
// Callback to be called if elements are updated or rendered for first time
|
||||||
if (!lazyLoad && this.freshRender)
|
if (!lazyLoad && this.freshRender)
|
||||||
this.freshRender()
|
this.freshRender()
|
||||||
@ -505,3 +732,12 @@ function handleMobileChange(e) {
|
|||||||
}
|
}
|
||||||
mobileQuery.addEventListener('change', handleMobileChange)
|
mobileQuery.addEventListener('change', handleMobileChange)
|
||||||
handleMobileChange(mobileQuery)
|
handleMobileChange(mobileQuery)
|
||||||
|
|
||||||
|
function showProcessStage(id, index) {
|
||||||
|
[...getRef(id).children].forEach((child, i) => {
|
||||||
|
if (i === index)
|
||||||
|
child.classList.remove('hide')
|
||||||
|
else
|
||||||
|
child.classList.add('hide')
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user