Basic backend list and import/export toggle in WebUI.

This commit is contained in:
abdulmohsen
2024-05-01 21:33:29 +03:00
parent c10ebea137
commit a98699269d
10 changed files with 1218 additions and 71 deletions

View File

@@ -0,0 +1,966 @@
.switch[type=checkbox] {
outline: 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
display: inline-block;
position: absolute;
opacity: 0
}
.switch[type=checkbox]:focus + label::after, .switch[type=checkbox]:focus + label::before, .switch[type=checkbox]:focus + label:after, .switch[type=checkbox]:focus + label:before {
outline: 1px dotted #b5b5b5
}
.switch[type=checkbox][disabled] {
cursor: not-allowed
}
.switch[type=checkbox][disabled] + label {
opacity: .5
}
.switch[type=checkbox][disabled] + label::before, .switch[type=checkbox][disabled] + label:before {
opacity: .5
}
.switch[type=checkbox][disabled] + label::after, .switch[type=checkbox][disabled] + label:after {
opacity: .5
}
.switch[type=checkbox][disabled] + label:hover {
cursor: not-allowed
}
.switch[type=checkbox] + label {
position: relative;
display: inline-flex;
align-items: center;
justify-content: flex-start;
font-size: 1rem;
height: 2.5em;
line-height: 1.5;
padding-left: 3.5rem;
padding-top: .2rem;
cursor: pointer
}
.switch[type=checkbox] + label::before, .switch[type=checkbox] + label:before {
position: absolute;
display: block;
top: calc(50% - 1.5rem * .5);
left: 0;
width: 3rem;
height: 1.5rem;
border: .1rem solid transparent;
border-radius: 4px;
background: #b5b5b5;
content: ""
}
.switch[type=checkbox] + label::after, .switch[type=checkbox] + label:after {
display: block;
position: absolute;
top: calc(50% - 1rem * .5);
left: .25rem;
width: 1rem;
height: 1rem;
transform: translate3d(0, 0, 0);
border-radius: 4px;
background: #fff;
transition: all .25s ease-out;
content: ""
}
.switch[type=checkbox] + label .switch-active, .switch[type=checkbox] + label .switch-inactive {
font-size: .9rem;
z-index: 1;
margin-top: -4px
}
.switch[type=checkbox] + label.has-text-inside .switch-inactive {
margin-left: -1.925rem
}
.switch[type=checkbox] + label.has-text-inside .switch-active {
margin-left: -3.25rem
}
.switch[type=checkbox].is-rtl + label {
padding-left: 0;
padding-right: 3.5rem
}
.switch[type=checkbox].is-rtl + label::before, .switch[type=checkbox].is-rtl + label:before {
left: auto;
right: 0
}
.switch[type=checkbox].is-rtl + label::after, .switch[type=checkbox].is-rtl + label:after {
left: auto;
right: 1.625rem
}
.switch[type=checkbox]:checked + label::before, .switch[type=checkbox]:checked + label:before {
background: #00d1b2
}
.switch[type=checkbox]:checked + label::after {
left: 1.625rem
}
.switch[type=checkbox]:checked.is-rtl + label::after, .switch[type=checkbox]:checked.is-rtl + label:after {
left: auto;
right: .25rem
}
.switch[type=checkbox].is-outlined + label::before, .switch[type=checkbox].is-outlined + label:before {
background-color: transparent;
border-color: #b5b5b5
}
.switch[type=checkbox].is-outlined + label::after, .switch[type=checkbox].is-outlined + label:after {
background: #b5b5b5
}
.switch[type=checkbox].is-outlined:checked + label::before, .switch[type=checkbox].is-outlined:checked + label:before {
background-color: transparent;
border-color: #00d1b2
}
.switch[type=checkbox].is-outlined:checked + label::after, .switch[type=checkbox].is-outlined:checked + label:after {
background: #00d1b2
}
.switch[type=checkbox].is-thin + label::before, .switch[type=checkbox].is-thin + label:before {
top: .5454545456rem;
height: .375rem
}
.switch[type=checkbox].is-thin + label::after, .switch[type=checkbox].is-thin + label:after {
box-shadow: 0 0 3px #7a7a7a
}
.switch[type=checkbox].is-rounded + label::before, .switch[type=checkbox].is-rounded + label:before {
border-radius: 24px
}
.switch[type=checkbox].is-rounded + label::after, .switch[type=checkbox].is-rounded + label:after {
border-radius: 50%
}
.switch[type=checkbox].is-small + label {
position: relative;
display: inline-flex;
align-items: center;
justify-content: flex-start;
font-size: .75rem;
height: 2.5em;
line-height: 1.5;
padding-left: 2.75rem;
padding-top: .2rem;
cursor: pointer
}
.switch[type=checkbox].is-small + label::before, .switch[type=checkbox].is-small + label:before {
position: absolute;
display: block;
top: calc(50% - 1.125rem * .5);
left: 0;
width: 2.25rem;
height: 1.125rem;
border: .1rem solid transparent;
border-radius: 4px;
background: #b5b5b5;
content: ""
}
.switch[type=checkbox].is-small + label::after, .switch[type=checkbox].is-small + label:after {
display: block;
position: absolute;
top: calc(50% - .625rem * .5);
left: .25rem;
width: .625rem;
height: .625rem;
transform: translate3d(0, 0, 0);
border-radius: 4px;
background: #fff;
transition: all .25s ease-out;
content: ""
}
.switch[type=checkbox].is-small + label .switch-active, .switch[type=checkbox].is-small + label .switch-inactive {
font-size: .65rem;
z-index: 1;
margin-top: -4px
}
.switch[type=checkbox].is-small + label.has-text-inside .switch-inactive {
margin-left: -1.55rem
}
.switch[type=checkbox].is-small + label.has-text-inside .switch-active {
margin-left: -2.5rem
}
.switch[type=checkbox].is-small.is-rtl + label {
padding-left: 0;
padding-right: 2.75rem
}
.switch[type=checkbox].is-small.is-rtl + label::before, .switch[type=checkbox].is-small.is-rtl + label:before {
left: auto;
right: 0
}
.switch[type=checkbox].is-small.is-rtl + label::after, .switch[type=checkbox].is-small.is-rtl + label:after {
left: auto;
right: 1.25rem
}
.switch[type=checkbox].is-small:checked + label::before, .switch[type=checkbox].is-small:checked + label:before {
background: #00d1b2
}
.switch[type=checkbox].is-small:checked + label::after {
left: 1.25rem
}
.switch[type=checkbox].is-small:checked.is-rtl + label::after, .switch[type=checkbox].is-small:checked.is-rtl + label:after {
left: auto;
right: .25rem
}
.switch[type=checkbox].is-small.is-outlined + label::before, .switch[type=checkbox].is-small.is-outlined + label:before {
background-color: transparent;
border-color: #b5b5b5
}
.switch[type=checkbox].is-small.is-outlined + label::after, .switch[type=checkbox].is-small.is-outlined + label:after {
background: #b5b5b5
}
.switch[type=checkbox].is-small.is-outlined:checked + label::before, .switch[type=checkbox].is-small.is-outlined:checked + label:before {
background-color: transparent;
border-color: #00d1b2
}
.switch[type=checkbox].is-small.is-outlined:checked + label::after, .switch[type=checkbox].is-small.is-outlined:checked + label:after {
background: #00d1b2
}
.switch[type=checkbox].is-small.is-thin + label::before, .switch[type=checkbox].is-small.is-thin + label:before {
top: .4090909093rem;
height: .28125rem
}
.switch[type=checkbox].is-small.is-thin + label::after, .switch[type=checkbox].is-small.is-thin + label:after {
box-shadow: 0 0 3px #7a7a7a
}
.switch[type=checkbox].is-small.is-rounded + label::before, .switch[type=checkbox].is-small.is-rounded + label:before {
border-radius: 24px
}
.switch[type=checkbox].is-small.is-rounded + label::after, .switch[type=checkbox].is-small.is-rounded + label:after {
border-radius: 50%
}
.switch[type=checkbox].is-medium + label {
position: relative;
display: inline-flex;
align-items: center;
justify-content: flex-start;
font-size: 1.25rem;
height: 2.5em;
line-height: 1.5;
padding-left: 4.25rem;
padding-top: .2rem;
cursor: pointer
}
.switch[type=checkbox].is-medium + label::before, .switch[type=checkbox].is-medium + label:before {
position: absolute;
display: block;
top: calc(50% - 1.875rem * .5);
left: 0;
width: 3.75rem;
height: 1.875rem;
border: .1rem solid transparent;
border-radius: 4px;
background: #b5b5b5;
content: ""
}
.switch[type=checkbox].is-medium + label::after, .switch[type=checkbox].is-medium + label:after {
display: block;
position: absolute;
top: calc(50% - 1.375rem * .5);
left: .25rem;
width: 1.375rem;
height: 1.375rem;
transform: translate3d(0, 0, 0);
border-radius: 4px;
background: #fff;
transition: all .25s ease-out;
content: ""
}
.switch[type=checkbox].is-medium + label .switch-active, .switch[type=checkbox].is-medium + label .switch-inactive {
font-size: 1.15rem;
z-index: 1;
margin-top: -4px
}
.switch[type=checkbox].is-medium + label.has-text-inside .switch-inactive {
margin-left: -2.3rem
}
.switch[type=checkbox].is-medium + label.has-text-inside .switch-active {
margin-left: -4rem
}
.switch[type=checkbox].is-medium.is-rtl + label {
padding-left: 0;
padding-right: 4.25rem
}
.switch[type=checkbox].is-medium.is-rtl + label::before, .switch[type=checkbox].is-medium.is-rtl + label:before {
left: auto;
right: 0
}
.switch[type=checkbox].is-medium.is-rtl + label::after, .switch[type=checkbox].is-medium.is-rtl + label:after {
left: auto;
right: 2rem
}
.switch[type=checkbox].is-medium:checked + label::before, .switch[type=checkbox].is-medium:checked + label:before {
background: #00d1b2
}
.switch[type=checkbox].is-medium:checked + label::after {
left: 2rem
}
.switch[type=checkbox].is-medium:checked.is-rtl + label::after, .switch[type=checkbox].is-medium:checked.is-rtl + label:after {
left: auto;
right: .25rem
}
.switch[type=checkbox].is-medium.is-outlined + label::before, .switch[type=checkbox].is-medium.is-outlined + label:before {
background-color: transparent;
border-color: #b5b5b5
}
.switch[type=checkbox].is-medium.is-outlined + label::after, .switch[type=checkbox].is-medium.is-outlined + label:after {
background: #b5b5b5
}
.switch[type=checkbox].is-medium.is-outlined:checked + label::before, .switch[type=checkbox].is-medium.is-outlined:checked + label:before {
background-color: transparent;
border-color: #00d1b2
}
.switch[type=checkbox].is-medium.is-outlined:checked + label::after, .switch[type=checkbox].is-medium.is-outlined:checked + label:after {
background: #00d1b2
}
.switch[type=checkbox].is-medium.is-thin + label::before, .switch[type=checkbox].is-medium.is-thin + label:before {
top: .6818181819rem;
height: .46875rem
}
.switch[type=checkbox].is-medium.is-thin + label::after, .switch[type=checkbox].is-medium.is-thin + label:after {
box-shadow: 0 0 3px #7a7a7a
}
.switch[type=checkbox].is-medium.is-rounded + label::before, .switch[type=checkbox].is-medium.is-rounded + label:before {
border-radius: 24px
}
.switch[type=checkbox].is-medium.is-rounded + label::after, .switch[type=checkbox].is-medium.is-rounded + label:after {
border-radius: 50%
}
.switch[type=checkbox].is-large + label {
position: relative;
display: inline-flex;
align-items: center;
justify-content: flex-start;
font-size: 1.5rem;
height: 2.5em;
line-height: 1.5;
padding-left: 5rem;
padding-top: .2rem;
cursor: pointer
}
.switch[type=checkbox].is-large + label::before, .switch[type=checkbox].is-large + label:before {
position: absolute;
display: block;
top: calc(50% - 2.25rem * .5);
left: 0;
width: 4.5rem;
height: 2.25rem;
border: .1rem solid transparent;
border-radius: 4px;
background: #b5b5b5;
content: ""
}
.switch[type=checkbox].is-large + label::after, .switch[type=checkbox].is-large + label:after {
display: block;
position: absolute;
top: calc(50% - 1.75rem * .5);
left: .25rem;
width: 1.75rem;
height: 1.75rem;
transform: translate3d(0, 0, 0);
border-radius: 4px;
background: #fff;
transition: all .25s ease-out;
content: ""
}
.switch[type=checkbox].is-large + label .switch-active, .switch[type=checkbox].is-large + label .switch-inactive {
font-size: 1.4rem;
z-index: 1;
margin-top: -4px
}
.switch[type=checkbox].is-large + label.has-text-inside .switch-inactive {
margin-left: -2.675rem
}
.switch[type=checkbox].is-large + label.has-text-inside .switch-active {
margin-left: -4.75rem
}
.switch[type=checkbox].is-large.is-rtl + label {
padding-left: 0;
padding-right: 5rem
}
.switch[type=checkbox].is-large.is-rtl + label::before, .switch[type=checkbox].is-large.is-rtl + label:before {
left: auto;
right: 0
}
.switch[type=checkbox].is-large.is-rtl + label::after, .switch[type=checkbox].is-large.is-rtl + label:after {
left: auto;
right: 2.375rem
}
.switch[type=checkbox].is-large:checked + label::before, .switch[type=checkbox].is-large:checked + label:before {
background: #00d1b2
}
.switch[type=checkbox].is-large:checked + label::after {
left: 2.375rem
}
.switch[type=checkbox].is-large:checked.is-rtl + label::after, .switch[type=checkbox].is-large:checked.is-rtl + label:after {
left: auto;
right: .25rem
}
.switch[type=checkbox].is-large.is-outlined + label::before, .switch[type=checkbox].is-large.is-outlined + label:before {
background-color: transparent;
border-color: #b5b5b5
}
.switch[type=checkbox].is-large.is-outlined + label::after, .switch[type=checkbox].is-large.is-outlined + label:after {
background: #b5b5b5
}
.switch[type=checkbox].is-large.is-outlined:checked + label::before, .switch[type=checkbox].is-large.is-outlined:checked + label:before {
background-color: transparent;
border-color: #00d1b2
}
.switch[type=checkbox].is-large.is-outlined:checked + label::after, .switch[type=checkbox].is-large.is-outlined:checked + label:after {
background: #00d1b2
}
.switch[type=checkbox].is-large.is-thin + label::before, .switch[type=checkbox].is-large.is-thin + label:before {
top: .8181818183rem;
height: .5625rem
}
.switch[type=checkbox].is-large.is-thin + label::after, .switch[type=checkbox].is-large.is-thin + label:after {
box-shadow: 0 0 3px #7a7a7a
}
.switch[type=checkbox].is-large.is-rounded + label::before, .switch[type=checkbox].is-large.is-rounded + label:before {
border-radius: 24px
}
.switch[type=checkbox].is-large.is-rounded + label::after, .switch[type=checkbox].is-large.is-rounded + label:after {
border-radius: 50%
}
.switch[type=checkbox].is-white + label .switch-active {
display: none
}
.switch[type=checkbox].is-white + label .switch-inactive {
display: inline-block
}
.switch[type=checkbox].is-white:checked + label::before, .switch[type=checkbox].is-white:checked + label:before {
background: #fff
}
.switch[type=checkbox].is-white:checked + label .switch-active {
display: inline-block
}
.switch[type=checkbox].is-white:checked + label .switch-inactive {
display: none
}
.switch[type=checkbox].is-white.is-outlined:checked + label::before, .switch[type=checkbox].is-white.is-outlined:checked + label:before {
background-color: transparent;
border-color: #fff !important
}
.switch[type=checkbox].is-white.is-outlined:checked + label::after, .switch[type=checkbox].is-white.is-outlined:checked + label:after {
background: #fff
}
.switch[type=checkbox].is-white.is-thin.is-outlined + label::after, .switch[type=checkbox].is-white.is-thin.is-outlined + label:after {
box-shadow: none
}
.switch[type=checkbox].is-unchecked-white + label::before, .switch[type=checkbox].is-unchecked-white + label:before {
background: #fff
}
.switch[type=checkbox].is-unchecked-white.is-outlined + label::before, .switch[type=checkbox].is-unchecked-white.is-outlined + label:before {
background-color: transparent;
border-color: #fff !important
}
.switch[type=checkbox].is-unchecked-white.is-outlined + label::after, .switch[type=checkbox].is-unchecked-white.is-outlined + label:after {
background: #fff
}
.switch[type=checkbox].is-black + label .switch-active {
display: none
}
.switch[type=checkbox].is-black + label .switch-inactive {
display: inline-block
}
.switch[type=checkbox].is-black:checked + label::before, .switch[type=checkbox].is-black:checked + label:before {
background: #0a0a0a
}
.switch[type=checkbox].is-black:checked + label .switch-active {
display: inline-block
}
.switch[type=checkbox].is-black:checked + label .switch-inactive {
display: none
}
.switch[type=checkbox].is-black.is-outlined:checked + label::before, .switch[type=checkbox].is-black.is-outlined:checked + label:before {
background-color: transparent;
border-color: #0a0a0a !important
}
.switch[type=checkbox].is-black.is-outlined:checked + label::after, .switch[type=checkbox].is-black.is-outlined:checked + label:after {
background: #0a0a0a
}
.switch[type=checkbox].is-black.is-thin.is-outlined + label::after, .switch[type=checkbox].is-black.is-thin.is-outlined + label:after {
box-shadow: none
}
.switch[type=checkbox].is-unchecked-black + label::before, .switch[type=checkbox].is-unchecked-black + label:before {
background: #0a0a0a
}
.switch[type=checkbox].is-unchecked-black.is-outlined + label::before, .switch[type=checkbox].is-unchecked-black.is-outlined + label:before {
background-color: transparent;
border-color: #0a0a0a !important
}
.switch[type=checkbox].is-unchecked-black.is-outlined + label::after, .switch[type=checkbox].is-unchecked-black.is-outlined + label:after {
background: #0a0a0a
}
.switch[type=checkbox].is-light + label .switch-active {
display: none
}
.switch[type=checkbox].is-light + label .switch-inactive {
display: inline-block
}
.switch[type=checkbox].is-light:checked + label::before, .switch[type=checkbox].is-light:checked + label:before {
background: #f5f5f5
}
.switch[type=checkbox].is-light:checked + label .switch-active {
display: inline-block
}
.switch[type=checkbox].is-light:checked + label .switch-inactive {
display: none
}
.switch[type=checkbox].is-light.is-outlined:checked + label::before, .switch[type=checkbox].is-light.is-outlined:checked + label:before {
background-color: transparent;
border-color: #f5f5f5 !important
}
.switch[type=checkbox].is-light.is-outlined:checked + label::after, .switch[type=checkbox].is-light.is-outlined:checked + label:after {
background: #f5f5f5
}
.switch[type=checkbox].is-light.is-thin.is-outlined + label::after, .switch[type=checkbox].is-light.is-thin.is-outlined + label:after {
box-shadow: none
}
.switch[type=checkbox].is-unchecked-light + label::before, .switch[type=checkbox].is-unchecked-light + label:before {
background: #f5f5f5
}
.switch[type=checkbox].is-unchecked-light.is-outlined + label::before, .switch[type=checkbox].is-unchecked-light.is-outlined + label:before {
background-color: transparent;
border-color: #f5f5f5 !important
}
.switch[type=checkbox].is-unchecked-light.is-outlined + label::after, .switch[type=checkbox].is-unchecked-light.is-outlined + label:after {
background: #f5f5f5
}
.switch[type=checkbox].is-dark + label .switch-active {
display: none
}
.switch[type=checkbox].is-dark + label .switch-inactive {
display: inline-block
}
.switch[type=checkbox].is-dark:checked + label::before, .switch[type=checkbox].is-dark:checked + label:before {
background: #363636
}
.switch[type=checkbox].is-dark:checked + label .switch-active {
display: inline-block
}
.switch[type=checkbox].is-dark:checked + label .switch-inactive {
display: none
}
.switch[type=checkbox].is-dark.is-outlined:checked + label::before, .switch[type=checkbox].is-dark.is-outlined:checked + label:before {
background-color: transparent;
border-color: #363636 !important
}
.switch[type=checkbox].is-dark.is-outlined:checked + label::after, .switch[type=checkbox].is-dark.is-outlined:checked + label:after {
background: #363636
}
.switch[type=checkbox].is-dark.is-thin.is-outlined + label::after, .switch[type=checkbox].is-dark.is-thin.is-outlined + label:after {
box-shadow: none
}
.switch[type=checkbox].is-unchecked-dark + label::before, .switch[type=checkbox].is-unchecked-dark + label:before {
background: #363636
}
.switch[type=checkbox].is-unchecked-dark.is-outlined + label::before, .switch[type=checkbox].is-unchecked-dark.is-outlined + label:before {
background-color: transparent;
border-color: #363636 !important
}
.switch[type=checkbox].is-unchecked-dark.is-outlined + label::after, .switch[type=checkbox].is-unchecked-dark.is-outlined + label:after {
background: #363636
}
.switch[type=checkbox].is-primary + label .switch-active {
display: none
}
.switch[type=checkbox].is-primary + label .switch-inactive {
display: inline-block
}
.switch[type=checkbox].is-primary:checked + label::before, .switch[type=checkbox].is-primary:checked + label:before {
background: #00d1b2
}
.switch[type=checkbox].is-primary:checked + label .switch-active {
display: inline-block
}
.switch[type=checkbox].is-primary:checked + label .switch-inactive {
display: none
}
.switch[type=checkbox].is-primary.is-outlined:checked + label::before, .switch[type=checkbox].is-primary.is-outlined:checked + label:before {
background-color: transparent;
border-color: #00d1b2 !important
}
.switch[type=checkbox].is-primary.is-outlined:checked + label::after, .switch[type=checkbox].is-primary.is-outlined:checked + label:after {
background: #00d1b2
}
.switch[type=checkbox].is-primary.is-thin.is-outlined + label::after, .switch[type=checkbox].is-primary.is-thin.is-outlined + label:after {
box-shadow: none
}
.switch[type=checkbox].is-unchecked-primary + label::before, .switch[type=checkbox].is-unchecked-primary + label:before {
background: #00d1b2
}
.switch[type=checkbox].is-unchecked-primary.is-outlined + label::before, .switch[type=checkbox].is-unchecked-primary.is-outlined + label:before {
background-color: transparent;
border-color: #00d1b2 !important
}
.switch[type=checkbox].is-unchecked-primary.is-outlined + label::after, .switch[type=checkbox].is-unchecked-primary.is-outlined + label:after {
background: #00d1b2
}
.switch[type=checkbox].is-link + label .switch-active {
display: none
}
.switch[type=checkbox].is-link + label .switch-inactive {
display: inline-block
}
.switch[type=checkbox].is-link:checked + label::before, .switch[type=checkbox].is-link:checked + label:before {
background: #485fc7
}
.switch[type=checkbox].is-link:checked + label .switch-active {
display: inline-block
}
.switch[type=checkbox].is-link:checked + label .switch-inactive {
display: none
}
.switch[type=checkbox].is-link.is-outlined:checked + label::before, .switch[type=checkbox].is-link.is-outlined:checked + label:before {
background-color: transparent;
border-color: #485fc7 !important
}
.switch[type=checkbox].is-link.is-outlined:checked + label::after, .switch[type=checkbox].is-link.is-outlined:checked + label:after {
background: #485fc7
}
.switch[type=checkbox].is-link.is-thin.is-outlined + label::after, .switch[type=checkbox].is-link.is-thin.is-outlined + label:after {
box-shadow: none
}
.switch[type=checkbox].is-unchecked-link + label::before, .switch[type=checkbox].is-unchecked-link + label:before {
background: #485fc7
}
.switch[type=checkbox].is-unchecked-link.is-outlined + label::before, .switch[type=checkbox].is-unchecked-link.is-outlined + label:before {
background-color: transparent;
border-color: #485fc7 !important
}
.switch[type=checkbox].is-unchecked-link.is-outlined + label::after, .switch[type=checkbox].is-unchecked-link.is-outlined + label:after {
background: #485fc7
}
.switch[type=checkbox].is-info + label .switch-active {
display: none
}
.switch[type=checkbox].is-info + label .switch-inactive {
display: inline-block
}
.switch[type=checkbox].is-info:checked + label::before, .switch[type=checkbox].is-info:checked + label:before {
background: #3e8ed0
}
.switch[type=checkbox].is-info:checked + label .switch-active {
display: inline-block
}
.switch[type=checkbox].is-info:checked + label .switch-inactive {
display: none
}
.switch[type=checkbox].is-info.is-outlined:checked + label::before, .switch[type=checkbox].is-info.is-outlined:checked + label:before {
background-color: transparent;
border-color: #3e8ed0 !important
}
.switch[type=checkbox].is-info.is-outlined:checked + label::after, .switch[type=checkbox].is-info.is-outlined:checked + label:after {
background: #3e8ed0
}
.switch[type=checkbox].is-info.is-thin.is-outlined + label::after, .switch[type=checkbox].is-info.is-thin.is-outlined + label:after {
box-shadow: none
}
.switch[type=checkbox].is-unchecked-info + label::before, .switch[type=checkbox].is-unchecked-info + label:before {
background: #3e8ed0
}
.switch[type=checkbox].is-unchecked-info.is-outlined + label::before, .switch[type=checkbox].is-unchecked-info.is-outlined + label:before {
background-color: transparent;
border-color: #3e8ed0 !important
}
.switch[type=checkbox].is-unchecked-info.is-outlined + label::after, .switch[type=checkbox].is-unchecked-info.is-outlined + label:after {
background: #3e8ed0
}
.switch[type=checkbox].is-success + label .switch-active {
display: none
}
.switch[type=checkbox].is-success + label .switch-inactive {
display: inline-block
}
.switch[type=checkbox].is-success:checked + label::before, .switch[type=checkbox].is-success:checked + label:before {
background: #48c78e
}
.switch[type=checkbox].is-success:checked + label .switch-active {
display: inline-block
}
.switch[type=checkbox].is-success:checked + label .switch-inactive {
display: none
}
.switch[type=checkbox].is-success.is-outlined:checked + label::before, .switch[type=checkbox].is-success.is-outlined:checked + label:before {
background-color: transparent;
border-color: #48c78e !important
}
.switch[type=checkbox].is-success.is-outlined:checked + label::after, .switch[type=checkbox].is-success.is-outlined:checked + label:after {
background: #48c78e
}
.switch[type=checkbox].is-success.is-thin.is-outlined + label::after, .switch[type=checkbox].is-success.is-thin.is-outlined + label:after {
box-shadow: none
}
.switch[type=checkbox].is-unchecked-success + label::before, .switch[type=checkbox].is-unchecked-success + label:before {
background: #48c78e
}
.switch[type=checkbox].is-unchecked-success.is-outlined + label::before, .switch[type=checkbox].is-unchecked-success.is-outlined + label:before {
background-color: transparent;
border-color: #48c78e !important
}
.switch[type=checkbox].is-unchecked-success.is-outlined + label::after, .switch[type=checkbox].is-unchecked-success.is-outlined + label:after {
background: #48c78e
}
.switch[type=checkbox].is-warning + label .switch-active {
display: none
}
.switch[type=checkbox].is-warning + label .switch-inactive {
display: inline-block
}
.switch[type=checkbox].is-warning:checked + label::before, .switch[type=checkbox].is-warning:checked + label:before {
background: #ffe08a
}
.switch[type=checkbox].is-warning:checked + label .switch-active {
display: inline-block
}
.switch[type=checkbox].is-warning:checked + label .switch-inactive {
display: none
}
.switch[type=checkbox].is-warning.is-outlined:checked + label::before, .switch[type=checkbox].is-warning.is-outlined:checked + label:before {
background-color: transparent;
border-color: #ffe08a !important
}
.switch[type=checkbox].is-warning.is-outlined:checked + label::after, .switch[type=checkbox].is-warning.is-outlined:checked + label:after {
background: #ffe08a
}
.switch[type=checkbox].is-warning.is-thin.is-outlined + label::after, .switch[type=checkbox].is-warning.is-thin.is-outlined + label:after {
box-shadow: none
}
.switch[type=checkbox].is-unchecked-warning + label::before, .switch[type=checkbox].is-unchecked-warning + label:before {
background: #ffe08a
}
.switch[type=checkbox].is-unchecked-warning.is-outlined + label::before, .switch[type=checkbox].is-unchecked-warning.is-outlined + label:before {
background-color: transparent;
border-color: #ffe08a !important
}
.switch[type=checkbox].is-unchecked-warning.is-outlined + label::after, .switch[type=checkbox].is-unchecked-warning.is-outlined + label:after {
background: #ffe08a
}
.switch[type=checkbox].is-danger + label .switch-active {
display: none
}
.switch[type=checkbox].is-danger + label .switch-inactive {
display: inline-block
}
.switch[type=checkbox].is-danger:checked + label::before, .switch[type=checkbox].is-danger:checked + label:before {
background: #f14668
}
.switch[type=checkbox].is-danger:checked + label .switch-active {
display: inline-block
}
.switch[type=checkbox].is-danger:checked + label .switch-inactive {
display: none
}
.switch[type=checkbox].is-danger.is-outlined:checked + label::before, .switch[type=checkbox].is-danger.is-outlined:checked + label:before {
background-color: transparent;
border-color: #f14668 !important
}
.switch[type=checkbox].is-danger.is-outlined:checked + label::after, .switch[type=checkbox].is-danger.is-outlined:checked + label:after {
background: #f14668
}
.switch[type=checkbox].is-danger.is-thin.is-outlined + label::after, .switch[type=checkbox].is-danger.is-thin.is-outlined + label:after {
box-shadow: none
}
.switch[type=checkbox].is-unchecked-danger + label::before, .switch[type=checkbox].is-unchecked-danger + label:before {
background: #f14668
}
.switch[type=checkbox].is-unchecked-danger.is-outlined + label::before, .switch[type=checkbox].is-unchecked-danger.is-outlined + label:before {
background-color: transparent;
border-color: #f14668 !important
}
.switch[type=checkbox].is-unchecked-danger.is-outlined + label::after, .switch[type=checkbox].is-unchecked-danger.is-outlined + label:after {
background: #f14668
}
.field-body .switch[type=checkbox] + label {
margin-top: .375em
}

View File

@@ -18,24 +18,26 @@
<div class="navbar-menu" :class="{'is-active':showMenu}">
<div class="navbar-start">
<a class="navbar-item" href="/backends">
<NuxtLink class="navbar-item" href="/backends">
<span class="icon-text">
<span class="icon"><i class="fas fa-server"></i></span>
<span>Backends</span>
</span>
</a>
<a class="navbar-item" href="/history">
</NuxtLink>
<NuxtLink class="navbar-item" href="/history">
<span class="icon-text">
<span class="icon"><i class="fas fa-history"></i></span>
<span>History</span>
</span>
</a>
<a class="navbar-item" href="/logs">
</NuxtLink>
<NuxtLink class="navbar-item" href="/logs">
<span class="icon-text">
<span class="icon"><i class="fas fa-globe"></i></span>
<span>Logs</span>
</span>
</a>
</NuxtLink>
</div>
<div class="navbar-end pr-3">
<div class="navbar-item">
@@ -63,8 +65,8 @@
</div>
</nav>
<div class="columns is-multiline">
<div class="column is-12 mt-2" v-if="showConnection">
<div class="columns is-multiline" v-if="showConnection">
<div class="column is-12 mt-2">
<form class="box" @submit.prevent="testApi">
<div class="field">
<label class="label" for="api_url">
@@ -114,12 +116,15 @@
</div>
</form>
</div>
<div class="column is-12">
<slot/>
</div>
</div>
<template v-if="!api_url || !api_token">
<no-api/>
</template>
<template v-else>
<slot/>
</template>
<div class="columns mt-3 is-mobile">
<div class="column is-8-mobile">
<div class="has-text-left">

View File

@@ -17,12 +17,12 @@ export default defineNuxtConfig({
},
router: {
options: {
linkActiveClass: "is-active",
linkActiveClass: "is-selected",
}
},
modules: [
'@vueuse/nuxt',
'floating-vue/nuxt'
'floating-vue/nuxt',
],
nitro: {
output: {

View File

@@ -13,6 +13,7 @@
"@vueuse/core": "^10.9.0",
"@vueuse/nuxt": "^10.9.0",
"floating-vue": "^5.2.2",
"moment": "^2.30.1",
"nuxt": "^3.11.2",
"vue": "^3.4.21",
"vue-router": "^4.3.0"

View File

@@ -1,3 +1,101 @@
<template>
<p>NYI</p>
<div class="p-2">
<span class="title is-4">Backends</span>
<div class="is-pulled-right">
<div class="field is-grouped">
<p class="control">
<button class="button is-primary" @click.prevent="loadContent">
<span class="icon is-small">
<i class="fas fa-sync"></i>
</span>
</button>
</p>
</div>
</div>
</div>
<div class="columns is-multiline">
<div v-for="backend in backends" :key="backend.name" class="column is-6-tablet is-12-mobile">
<div class="card">
<header class="card-header">
<div class="card-header-title is-centered is-word-break">
<NuxtLink :href="'/backends/' + backend.name">
{{ backend.name }}
</NuxtLink>
</div>
</header>
<div class="card-content">
<div class="content">
<p>
<strong>Last Import:</strong> {{ moment(backend.import.lastSync).fromNow() }}
</p>
<p>
<strong>Last Export:</strong> {{ moment(backend.export.lastSync).fromNow() }}
</p>
</div>
</div>
<footer class="card-footer">
<div class="card-footer-item">
<div class="field">
<input :id="backend.name+'_export'" type="checkbox" class="switch is-success"
:checked="backend.export.enabled"
@change="updateValue(backend, 'export.enabled', !backend.export.enabled)">
<label :for="backend.name+'_export'">Export</label>
</div>
</div>
<div class="card-footer-item">
<div class="field">
<input :id="backend.name+'_import'" type="checkbox" class="switch is-success"
:checked="backend.import.enabled"
@change="updateValue(backend, 'import.enabled',!backend.import.enabled)">
<label :for="backend.name+'_import'">Import</label>
</div>
</div>
</footer>
</div>
</div>
</div>
</template>
<script setup>
import {useStorage} from '@vueuse/core';
import 'assets/css/bulma-switch.css'
import moment from "moment";
useHead({title: 'Backends'})
const api_url = useStorage('api_url', '')
const api_token = useStorage('api_token', '')
const backends = ref([])
const loadContent = async () => {
const response = await fetch(`${api_url.value}/v1/api/backends`, {
headers: {
'Authorization': `Bearer ${api_token.value}`
}
})
const json = await response.json();
backends.value = json.backends
}
onMounted(() => loadContent())
const updateValue = async (backend, key, newValue) => {
const response = await fetch(`${api_url.value}/v1/api/backends/${backend.name}`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${api_token.value}`
},
body: JSON.stringify([{
"key": key,
"value": newValue
}])
});
const json = await response.json();
backends.value[backends.value.findIndex(b => b.name === backend.name)] = json.backend
}
</script>

View File

@@ -1,19 +1,12 @@
<template>
<template v-if="!api_url || !api_token">
<no-api/>
</template>
<template v-else>
<p>foo</p>
</template>
<div class="columns">
<div class="column is-12">
<h1 class="title is-4">Index</h1>
<p>The WebUI still in very early stages.</p>
</div>
</div>
</template>
<script setup>
import {useStorage} from "@vueuse/core"
import NoApi from "~/components/NoApi.vue"
const api_url = useStorage('api_url', '')
const api_token = useStorage('api_token', '')
useHead({title: 'Index'})
</script>

39
frontend/utils/index.js Normal file
View File

@@ -0,0 +1,39 @@
const ag = (obj, path, defaultValue = null, separator = '.') => {
const keys = path.split(separator);
let at = obj;
for (let key of keys) {
if (typeof at === 'object' && at !== null && key in at) {
at = at[key];
} else {
return defaultValue;
}
}
return at;
}
const ag_set = (obj, path, value, separator = '.') => {
const keys = path.split(separator);
let at = obj;
while (keys.length > 0) {
if (keys.length === 1) {
if (typeof at === 'object' && at !== null) {
at[keys.shift()] = value;
} else {
throw new Error(`Cannot set value at this path (${path}) because it's not an object.`);
}
} else {
const key = keys.shift();
if (!at[key]) {
at[key] = {};
}
at = at[key];
}
}
return obj;
}
export {ag_set, ag}

View File

@@ -3576,6 +3576,11 @@ mlly@^1.3.0, mlly@^1.4.2, mlly@^1.6.1:
pkg-types "^1.1.0"
ufo "^1.5.3"
moment@^2.30.1:
version "2.30.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
mri@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"

View File

@@ -5,9 +5,13 @@ declare(strict_types=1);
namespace App\API\Backends;
use App\Libs\Attributes\Route\Get;
use App\Libs\Attributes\Route\Patch;
use App\Libs\Config;
use App\Libs\ConfigFile;
use App\Libs\HTTP_STATUS;
use App\Libs\Options;
use App\Libs\Traits\APITraits;
use JsonException;
use Psr\Http\Message\ResponseInterface as iResponse;
use Psr\Http\Message\ServerRequestInterface as iRequest;
@@ -52,4 +56,83 @@ final class Index
return api_response(HTTP_STATUS::HTTP_OK, $response);
}
#[Get(Index::URL . '/{name:backend}[/]', name: 'backends.view')]
public function backendsView(iRequest $request, array $args = []): iResponse
{
if (null === ($name = ag($args, 'name'))) {
return api_error('Invalid value for id path parameter.', HTTP_STATUS::HTTP_BAD_REQUEST);
}
$data = $this->getBackends(name: $name);
if (empty($data)) {
return api_error(r("Backend '{name}' not found.", ['name' => $name]), HTTP_STATUS::HTTP_NOT_FOUND);
}
$apiUrl = $request->getUri()->withHost('')->withPort(0)->withScheme('');
$data = array_pop($data);
$response = [
...$data,
'links' => [
'self' => (string)$apiUrl,
'list' => (string)$apiUrl->withPath(parseConfigValue(Index::URL)),
],
];
return api_response(HTTP_STATUS::HTTP_OK, ['backend' => $response]);
}
#[Patch(Index::URL . '/{name:backend}[/]', name: 'backends.view')]
public function backendsUpdatePartial(iRequest $request, array $args = []): iResponse
{
if (null === ($name = ag($args, 'name'))) {
return api_error('Invalid value for name path parameter.', HTTP_STATUS::HTTP_BAD_REQUEST);
}
$list = ConfigFile::open(Config::get('backends_file'), 'yaml', autoCreate: true);
if (false === $list->has($name)) {
return api_error(r("Backend '{name}' not found.", ['name' => $name]), HTTP_STATUS::HTTP_NOT_FOUND);
}
try {
$data = json_decode((string)$request->getBody(), true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
return api_error(r('Invalid JSON data. {error}', ['error' => $e->getMessage()]),
HTTP_STATUS::HTTP_BAD_REQUEST);
}
foreach ($data as $update) {
if (!ag_exists($update, 'key')) {
return api_error('No key to update was present.', HTTP_STATUS::HTTP_BAD_REQUEST);
}
$list->set($name . '.' . ag($update, 'key'), ag($update, 'value'));
}
$apiUrl = $request->getUri()->withHost('')->withPort(0)->withScheme('');
$list->persist();
$backend = $this->getBackends(name: $name);
if (empty($backend)) {
return api_error(r("Backend '{name}' not found.", ['name' => $name]), HTTP_STATUS::HTTP_NOT_FOUND);
}
$backend = array_pop($backend);
return api_response(HTTP_STATUS::HTTP_OK, [
'backend' => array_filter(
$backend,
fn($key) => false === in_array($key, ['options', 'webhook'], true),
ARRAY_FILTER_USE_KEY
),
'links' => [
'self' => (string)$apiUrl,
'list' => (string)$apiUrl->withPath(parseConfigValue(Index::URL)),
],
]);
}
}

View File

@@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
namespace App\API\Backends;
use App\Libs\Attributes\Route\Get;
use App\Libs\HTTP_STATUS;
use App\Libs\Traits\APITraits;
use Psr\Http\Message\ResponseInterface as iResponse;
use Psr\Http\Message\ServerRequestInterface as iRequest;
final class View
{
use APITraits;
#[Get(Index::URL . '/{name:backend}[/]', name: 'backends.view')]
public function backendsView(iRequest $request, array $args = []): iResponse
{
if (null === ($name = ag($args, 'name'))) {
return api_error('Invalid value for id path parameter.', HTTP_STATUS::HTTP_BAD_REQUEST);
}
$data = $this->getBackends(name: $name);
if (empty($data)) {
return api_error(r("Backend '{name}' not found.", ['name' => $name]), HTTP_STATUS::HTTP_NOT_FOUND);
}
$apiUrl = $request->getUri()->withHost('')->withPort(0)->withScheme('');
$data = array_pop($data);
$response = [
...$data,
'links' => [
'self' => (string)$apiUrl,
'list' => (string)$apiUrl->withPath(parseConfigValue(Index::URL)),
],
];
return api_response(HTTP_STATUS::HTTP_OK, ['backend' => $response]);
}
}