Added change email and logout

This commit is contained in:
xeovalyte 2024-10-02 11:19:05 +02:00
parent 5ca02189f4
commit 4d52d73275
Signed by: xeovalyte
SSH Key Fingerprint: SHA256:kSQDrQDmKzljJzfGYcd3m9RqHi4h8rSwkZ3sQ9kBURo
6 changed files with 729 additions and 12 deletions

View File

@ -943,6 +943,70 @@ html {
color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));
} }
.collapse:not(td):not(tr):not(colgroup) {
visibility: visible;
}
.collapse {
position: relative;
display: grid;
overflow: hidden;
grid-template-rows: auto 0fr;
transition: grid-template-rows 0.2s;
width: 100%;
border-radius: var(--rounded-box, 1rem);
}
.collapse-title,
.collapse > input[type="checkbox"],
.collapse > input[type="radio"],
.collapse-content {
grid-column-start: 1;
grid-row-start: 1;
}
.collapse > input[type="checkbox"],
.collapse > input[type="radio"] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
opacity: 0;
}
.collapse-content {
visibility: hidden;
grid-column-start: 1;
grid-row-start: 2;
min-height: 0px;
transition: visibility 0.2s;
transition: padding 0.2s ease-out,
background-color 0.2s ease-out;
padding-left: 1rem;
padding-right: 1rem;
cursor: unset;
}
.collapse[open],
.collapse-open,
.collapse:focus:not(.collapse-close) {
grid-template-rows: auto 1fr;
}
.collapse:not(.collapse-close):has(> input[type="checkbox"]:checked),
.collapse:not(.collapse-close):has(> input[type="radio"]:checked) {
grid-template-rows: auto 1fr;
}
.collapse[open] > .collapse-content,
.collapse-open > .collapse-content,
.collapse:focus:not(.collapse-close) > .collapse-content,
.collapse:not(.collapse-close) > input[type="checkbox"]:checked ~ .collapse-content,
.collapse:not(.collapse-close) > input[type="radio"]:checked ~ .collapse-content {
visibility: visible;
min-height: -moz-fit-content;
min-height: fit-content;
}
.dropdown { .dropdown {
position: relative; position: relative;
display: inline-block; display: inline-block;
@ -1039,6 +1103,15 @@ html {
} }
} }
.btn-outline:hover {
--tw-border-opacity: 1;
border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));
--tw-bg-opacity: 1;
background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));
--tw-text-opacity: 1;
color: var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity)));
}
.btn-outline.btn-primary:hover { .btn-outline.btn-primary:hover {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));
@ -1051,6 +1124,78 @@ html {
} }
} }
.btn-outline.btn-secondary:hover {
--tw-text-opacity: 1;
color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)));
}
@supports (color: color-mix(in oklab, black, black)) {
.btn-outline.btn-secondary:hover {
background-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black);
}
}
.btn-outline.btn-accent:hover {
--tw-text-opacity: 1;
color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)));
}
@supports (color: color-mix(in oklab, black, black)) {
.btn-outline.btn-accent:hover {
background-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black);
}
}
.btn-outline.btn-success:hover {
--tw-text-opacity: 1;
color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)));
}
@supports (color: color-mix(in oklab, black, black)) {
.btn-outline.btn-success:hover {
background-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black);
}
}
.btn-outline.btn-info:hover {
--tw-text-opacity: 1;
color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));
}
@supports (color: color-mix(in oklab, black, black)) {
.btn-outline.btn-info:hover {
background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black);
}
}
.btn-outline.btn-warning:hover {
--tw-text-opacity: 1;
color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)));
}
@supports (color: color-mix(in oklab, black, black)) {
.btn-outline.btn-warning:hover {
background-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black);
}
}
.btn-outline.btn-error:hover {
--tw-text-opacity: 1;
color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)));
}
@supports (color: color-mix(in oklab, black, black)) {
.btn-outline.btn-error:hover {
background-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black);
}
}
.btn-disabled:hover, .btn-disabled:hover,
.btn[disabled]:hover, .btn[disabled]:hover,
.btn:disabled:hover { .btn:disabled:hover {
@ -1395,6 +1540,30 @@ input.tab:checked + .tab-content,
background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));
} }
.toggle {
flex-shrink: 0;
--tglbg: var(--fallback-b1,oklch(var(--b1)/1));
--handleoffset: 1.5rem;
--handleoffsetcalculator: calc(var(--handleoffset) * -1);
--togglehandleborder: 0 0;
height: 1.5rem;
width: 3rem;
cursor: pointer;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
border-radius: var(--rounded-badge, 1.9rem);
border-width: 1px;
border-color: currentColor;
background-color: currentColor;
color: var(--fallback-bc,oklch(var(--bc)/0.5));
transition: background,
box-shadow var(--animation-input, 0.2s) ease-out;
box-shadow: var(--handleoffsetcalculator) 0 0 2px var(--tglbg) inset,
0 0 0 2px var(--tglbg) inset,
var(--togglehandleborder);
}
.badge-neutral { .badge-neutral {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: var(--fallback-n,oklch(var(--n)/var(--tw-border-opacity))); border-color: var(--fallback-n,oklch(var(--n)/var(--tw-border-opacity)));
@ -1449,6 +1618,10 @@ input.tab:checked + .tab-content,
.btn-neutral { .btn-neutral {
--btn-color: var(--fallback-n); --btn-color: var(--fallback-n);
} }
.btn-error {
--btn-color: var(--fallback-er);
}
} }
@supports (color: color-mix(in oklab, black, black)) { @supports (color: color-mix(in oklab, black, black)) {
@ -1456,6 +1629,36 @@ input.tab:checked + .tab-content,
background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black);
} }
.btn-outline.btn-secondary.btn-active {
background-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black);
}
.btn-outline.btn-accent.btn-active {
background-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black);
}
.btn-outline.btn-success.btn-active {
background-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black);
}
.btn-outline.btn-info.btn-active {
background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black);
}
.btn-outline.btn-warning.btn-active {
background-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black);
}
.btn-outline.btn-error.btn-active {
background-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black);
border-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black);
}
} }
.btn:focus-visible { .btn:focus-visible {
@ -1478,6 +1681,10 @@ input.tab:checked + .tab-content,
.btn-neutral { .btn-neutral {
--btn-color: var(--n); --btn-color: var(--n);
} }
.btn-error {
--btn-color: var(--er);
}
} }
.btn-neutral { .btn-neutral {
@ -1486,6 +1693,12 @@ input.tab:checked + .tab-content,
outline-color: var(--fallback-n,oklch(var(--n)/1)); outline-color: var(--fallback-n,oklch(var(--n)/1));
} }
.btn-error {
--tw-text-opacity: 1;
color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)));
outline-color: var(--fallback-er,oklch(var(--er)/1));
}
.btn.glass { .btn.glass {
--tw-shadow: 0 0 #0000; --tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000; --tw-shadow-colored: 0 0 #0000;
@ -1514,6 +1727,25 @@ input.tab:checked + .tab-content,
background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); background-color: var(--fallback-bc,oklch(var(--bc)/0.2));
} }
.btn-outline {
border-color: currentColor;
background-color: transparent;
--tw-text-opacity: 1;
color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.btn-outline.btn-active {
--tw-border-opacity: 1;
border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));
--tw-bg-opacity: 1;
background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));
--tw-text-opacity: 1;
color: var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity)));
}
.btn-outline.btn-primary { .btn-outline.btn-primary {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)));
@ -1524,6 +1756,66 @@ input.tab:checked + .tab-content,
color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));
} }
.btn-outline.btn-secondary {
--tw-text-opacity: 1;
color: var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity)));
}
.btn-outline.btn-secondary.btn-active {
--tw-text-opacity: 1;
color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)));
}
.btn-outline.btn-accent {
--tw-text-opacity: 1;
color: var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity)));
}
.btn-outline.btn-accent.btn-active {
--tw-text-opacity: 1;
color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)));
}
.btn-outline.btn-success {
--tw-text-opacity: 1;
color: var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity)));
}
.btn-outline.btn-success.btn-active {
--tw-text-opacity: 1;
color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)));
}
.btn-outline.btn-info {
--tw-text-opacity: 1;
color: var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity)));
}
.btn-outline.btn-info.btn-active {
--tw-text-opacity: 1;
color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));
}
.btn-outline.btn-warning {
--tw-text-opacity: 1;
color: var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity)));
}
.btn-outline.btn-warning.btn-active {
--tw-text-opacity: 1;
color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)));
}
.btn-outline.btn-error {
--tw-text-opacity: 1;
color: var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity)));
}
.btn-outline.btn-error.btn-active {
--tw-text-opacity: 1;
color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)));
}
.btn.btn-disabled, .btn.btn-disabled,
.btn[disabled], .btn[disabled],
.btn:disabled { .btn:disabled {
@ -1623,6 +1915,130 @@ input.tab:checked + .tab-content,
} }
} }
details.collapse {
width: 100%;
}
details.collapse summary {
position: relative;
display: block;
outline: 2px solid transparent;
outline-offset: 2px;
}
details.collapse summary::-webkit-details-marker {
display: none;
}
.collapse:focus-visible {
outline-style: solid;
outline-width: 2px;
outline-offset: 2px;
outline-color: var(--fallback-bc,oklch(var(--bc)/1));
}
.collapse:has(.collapse-title:focus-visible),
.collapse:has(> input[type="checkbox"]:focus-visible),
.collapse:has(> input[type="radio"]:focus-visible) {
outline-style: solid;
outline-width: 2px;
outline-offset: 2px;
outline-color: var(--fallback-bc,oklch(var(--bc)/1));
}
.collapse-arrow > .collapse-title:after {
position: absolute;
display: block;
height: 0.5rem;
width: 0.5rem;
--tw-translate-y: -100%;
--tw-rotate: 45deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
transition-duration: 150ms;
transition-duration: 0.2s;
top: 1.9rem;
inset-inline-end: 1.4rem;
content: "";
transform-origin: 75% 75%;
box-shadow: 2px 2px;
pointer-events: none;
}
.collapse-plus > .collapse-title:after {
position: absolute;
display: block;
height: 0.5rem;
width: 0.5rem;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
transition-duration: 300ms;
top: 0.9rem;
inset-inline-end: 1.4rem;
content: "+";
pointer-events: none;
}
.collapse:not(.collapse-open):not(.collapse-close) > input[type="checkbox"],
.collapse:not(.collapse-open):not(.collapse-close) > input[type="radio"]:not(:checked),
.collapse:not(.collapse-open):not(.collapse-close) > .collapse-title {
cursor: pointer;
}
.collapse:focus:not(.collapse-open):not(.collapse-close):not(.collapse[open]) > .collapse-title {
cursor: unset;
}
.collapse-title {
position: relative;
}
:where(.collapse > input[type="checkbox"]),
:where(.collapse > input[type="radio"]) {
z-index: 1;
}
.collapse-title,
:where(.collapse > input[type="checkbox"]),
:where(.collapse > input[type="radio"]) {
width: 100%;
padding: 1rem;
padding-inline-end: 3rem;
min-height: 3.75rem;
transition: background-color 0.2s ease-out;
}
.collapse[open] > :where(.collapse-content),
.collapse-open > :where(.collapse-content),
.collapse:focus:not(.collapse-close) > :where(.collapse-content),
.collapse:not(.collapse-close) > :where(input[type="checkbox"]:checked ~ .collapse-content),
.collapse:not(.collapse-close) > :where(input[type="radio"]:checked ~ .collapse-content) {
padding-bottom: 1rem;
transition: padding 0.2s ease-out,
background-color 0.2s ease-out;
}
.collapse[open].collapse-arrow > .collapse-title:after,
.collapse-open.collapse-arrow > .collapse-title:after,
.collapse-arrow:focus:not(.collapse-close) > .collapse-title:after,
.collapse-arrow:not(.collapse-close) > input[type="checkbox"]:checked ~ .collapse-title:after,
.collapse-arrow:not(.collapse-close) > input[type="radio"]:checked ~ .collapse-title:after {
--tw-translate-y: -50%;
--tw-rotate: 225deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.collapse[open].collapse-plus > .collapse-title:after,
.collapse-open.collapse-plus > .collapse-title:after,
.collapse-plus:focus:not(.collapse-close) > .collapse-title:after,
.collapse-plus:not(.collapse-close) > input[type="checkbox"]:checked ~ .collapse-title:after,
.collapse-plus:not(.collapse-close) > input[type="radio"]:checked ~ .collapse-title:after {
content: "";
}
.dropdown.dropdown-open .dropdown-content, .dropdown.dropdown-open .dropdown-content,
.dropdown:focus .dropdown-content, .dropdown:focus .dropdown-content,
.dropdown:focus-within .dropdown-content { .dropdown:focus-within .dropdown-content {
@ -2188,6 +2604,57 @@ input.tab:checked + .tab-content,
} }
} }
[dir="rtl"] .toggle {
--handleoffsetcalculator: calc(var(--handleoffset) * 1);
}
.toggle:focus-visible {
outline-style: solid;
outline-width: 2px;
outline-offset: 2px;
outline-color: var(--fallback-bc,oklch(var(--bc)/0.2));
}
.toggle:hover {
background-color: currentColor;
}
.toggle:checked,
.toggle[aria-checked="true"] {
background-image: none;
--handleoffsetcalculator: var(--handleoffset);
--tw-text-opacity: 1;
color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
}
[dir="rtl"] .toggle:checked, [dir="rtl"] .toggle[aria-checked="true"] {
--handleoffsetcalculator: calc(var(--handleoffset) * -1);
}
.toggle:indeterminate {
--tw-text-opacity: 1;
color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
box-shadow: calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset,
calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset,
0 0 0 2px var(--tglbg) inset;
}
[dir="rtl"] .toggle:indeterminate {
box-shadow: calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset,
calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset,
0 0 0 2px var(--tglbg) inset;
}
.toggle:disabled {
cursor: not-allowed;
--tw-border-opacity: 1;
border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));
background-color: transparent;
opacity: 0.3;
--togglehandleborder: 0 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset,
var(--handleoffsetcalculator) 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset;
}
.badge-md { .badge-md {
height: 1.25rem; height: 1.25rem;
font-size: 0.875rem; font-size: 0.875rem;
@ -2391,6 +2858,10 @@ input.tab:checked + .tab-content,
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
} }
.collapse {
visibility: collapse;
}
.static { .static {
position: static; position: static;
} }
@ -2412,6 +2883,10 @@ input.tab:checked + .tab-content,
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
.ml-auto {
margin-left: auto;
}
.mt-10 { .mt-10 {
margin-top: 2.5rem; margin-top: 2.5rem;
} }
@ -2491,6 +2966,10 @@ input.tab:checked + .tab-content,
width: 100%; width: 100%;
} }
.max-w-2xl {
max-width: 42rem;
}
.max-w-4xl { .max-w-4xl {
max-width: 56rem; max-width: 56rem;
} }
@ -2511,6 +2990,12 @@ input.tab:checked + .tab-content,
flex: none; flex: none;
} }
.select-none {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.flex-col { .flex-col {
flex-direction: column; flex-direction: column;
} }
@ -2543,10 +3028,20 @@ input.tab:checked + .tab-content,
gap: 0.5rem; gap: 0.5rem;
} }
.gap-3 {
gap: 0.75rem;
}
.gap-5 { .gap-5 {
gap: 1.25rem; gap: 1.25rem;
} }
.space-y-3 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(0.75rem * var(--tw-space-y-reverse));
}
.overflow-auto { .overflow-auto {
overflow: auto; overflow: auto;
} }
@ -2597,10 +3092,19 @@ input.tab:checked + .tab-content,
padding-bottom: 2.5rem; padding-bottom: 2.5rem;
} }
.py-5 {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
.pb-10 { .pb-10 {
padding-bottom: 2.5rem; padding-bottom: 2.5rem;
} }
.pl-2 {
padding-left: 0.5rem;
}
.pt-2 { .pt-2 {
padding-top: 0.5rem; padding-top: 0.5rem;
} }
@ -2628,6 +3132,10 @@ input.tab:checked + .tab-content,
font-weight: 700; font-weight: 700;
} }
.font-medium {
font-weight: 500;
}
.font-normal { .font-normal {
font-weight: 400; font-weight: 400;
} }

View File

@ -13,18 +13,27 @@ use dioxus::prelude::*;
#[component] #[component]
pub fn Global() -> Element { pub fn Global() -> Element {
let user = use_resource(get_user_from_cookie); let user = use_resource(get_user_from_cookie);
let mut user_state: Signal<Option<User>> = use_signal(|| None);
use_context_provider(|| user); use_context_provider(|| user_state);
use_effect(move || {
if let Some(Ok(u)) = &*user.read_unchecked() {
user_state.set(Some(u.clone()));
};
});
rsx! { rsx! {
match &*user.read_unchecked() { match &*user.read_unchecked() {
Some(Ok(_)) => rsx! { Some(Ok(_)) => rsx! {
crate::components::layout::topbar::Topbar {} if user_state().is_some() {
main { crate::components::layout::topbar::Topbar {}
class: "h-full overflow-y-auto", main {
Outlet::<Route> {} class: "h-full overflow-y-auto flex justify-center px-2 py-5",
Outlet::<Route> {}
}
crate::components::layout::navbar::Navbar {}
} }
crate::components::layout::navbar::Navbar {}
}, },
Some(Err(_)) => rsx! { Some(Err(_)) => rsx! {
Auth { } Auth { }
@ -38,8 +47,7 @@ pub fn Global() -> Element {
#[server] #[server]
async fn get_user_from_cookie() -> Result<User, ServerFnError> { async fn get_user_from_cookie() -> Result<User, ServerFnError> {
let token = Session::get_token_from_cookie().await?; let user = Session::fetch_current_user().await?;
let user = Session::fetch_user_from_token(token).await?;
Ok(user) Ok(user)
} }

View File

@ -1,10 +1,158 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use crate::util::model::{session::Session, user::User};
#[component] #[component]
pub fn Settings() -> Element { pub fn Settings() -> Element {
rsx! { rsx! {
div { div {
h1 { class: "text-xl font-bold text-primary", "Settings" } class: "w-full max-w-2xl space-y-3",
Account {},
Password {},
} }
} }
} }
fn Account() -> Element {
let user_state = use_context::<Signal<Option<User>>>();
let user = user_state().unwrap();
let mut is_open = use_signal(|| false);
let mut input_email = use_signal(|| user.email);
let submit = move |_| async move {
if let Ok(_) = change_email(input_email()).await {
tracing::info!("User email changed");
};
};
rsx! {
div {
class: "collapse collapse-arrow bg-base-200",
class: if is_open() { "collapse-open" },
div {
class: "collapse-title text-lg font-medium hover:cursor-pointer select-none",
onclick: move |_| is_open.toggle(),
"Account",
},
div {
class: "collapse-content",
div {
class: "pl-2 flex flex-col gap-3",
label {
class: "form-control w-full",
div {
class: "label",
span { class: "label-text", "Account ID" }
}
b { "{user.id}" },
}
label {
class: "form-control w-full",
div {
class: "label",
span { class: "label-text", "Email" }
}
input {
r#type: "text",
class: "input input-bordered w-full",
oninput: move |event| input_email.set(event.value()),
value: "{input_email}",
}
}
div {
class: "w-full flex mt-5",
button {
class: "btn btn-outline btn-error",
onclick: move |_| async move {
if let Ok(_) = logout().await {
let window = web_sys::window().expect("Could not find window");
window.location().reload().expect("Could not reload window");
}
},
"Uitloggen",
}
button {
class: "ml-auto btn btn-primary",
onclick: submit,
"Opslaan",
}
}
}
},
},
}
}
#[server]
async fn logout() -> Result<(), ServerFnError> {
let session_token = Session::get_token_from_cookie().await?;
Session::delete_session(session_token).await?;
Session::delete_cookie().await?;
Ok(())
}
#[server]
async fn change_email(new_email: String) -> Result<(), ServerFnError> {
let user = Session::fetch_current_user().await?;
user.change_email(new_email).await?;
Ok(())
}
fn Password() -> Element {
let mut is_open = use_signal(|| false);
rsx! {
div {
class: "collapse collapse-arrow bg-base-200",
class: if is_open() { "collapse-open" },
div {
class: "collapse-title text-lg font-medium hover:cursor-pointer select-none",
onclick: move |_| is_open.toggle(),
"Wachtwoord",
},
div {
class: "collapse-content",
div {
class: "pl-2 space-y-3",
label {
class: "form-control w-full",
div {
class: "label",
span { class: "label-text", "Huidig wachtwoord" }
}
input {
r#type: "text",
class: "input input-bordered w-full",
}
}
label {
class: "form-control w-full",
div {
class: "label",
span { class: "label-text", "Nieuw wachtwoord" }
}
input {
r#type: "text",
class: "input input-bordered w-full",
}
}
label {
class: "form-control w-full",
div {
class: "label",
span { class: "label-text", "Herhaal nieuw wachtwoord" }
}
input {
r#type: "text",
class: "input input-bordered w-full",
}
}
}
},
},
}
}

View File

@ -35,8 +35,6 @@ impl Member {
let members: Vec<Self> = res.take(0)?; let members: Vec<Self> = res.take(0)?;
tracing::info!("{:?}", members);
Ok(members) Ok(members)
} }

View File

@ -81,4 +81,44 @@ impl Session {
None => Err(crate::Error::NoSessionCookie), None => Err(crate::Error::NoSessionCookie),
} }
} }
pub async fn delete_cookie() -> Result<(), crate::Error> {
use axum::http::{header, HeaderValue};
use axum_extra::extract::cookie::{Cookie, SameSite};
use dioxus::prelude::server_context;
use time::Duration;
let mut cookie = Cookie::build(("session_token", ""))
.max_age(Duration::seconds(1))
.build();
cookie.set_same_site(SameSite::Strict);
server_context()
.response_parts_mut()
.unwrap()
.headers
.insert(
header::SET_COOKIE,
HeaderValue::from_str(&cookie.to_string())?,
);
Ok(())
}
pub async fn delete_session(user_id: String) -> Result<(), crate::Error> {
DB.query("DELETE ONLY session WHERE user = type::thing('user', $user_id)")
.bind(("user_id", user_id))
.await?;
Ok(())
}
// Fetches the current user from cookie headers
pub async fn fetch_current_user() -> Result<User, crate::Error> {
let session_token = Self::get_token_from_cookie().await?;
let user = Self::fetch_user_from_token(session_token).await?;
Ok(user)
}
} }

View File

@ -8,7 +8,7 @@ use surrealdb::sql::statements::{BeginStatement, CommitStatement};
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)] #[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
pub struct User { pub struct User {
pub id: String, pub id: String,
email: String, pub email: String,
password: Option<String>, password: Option<String>,
} }
@ -60,4 +60,19 @@ impl User {
None => Err(crate::Error::NoDocument), None => Err(crate::Error::NoDocument),
} }
} }
pub async fn change_email(&self, new_email: String) -> Result<String, crate::Error> {
let mut res = DB
.query("UPDATE type::thing('user', $user_id) SET email = $email RETURN VALUE email")
.bind(("user_id", self.id.to_string()))
.bind(("email", new_email))
.await?;
let email: Option<String> = res.take(0)?;
match email {
Some(e) => Ok(e),
None => Err(crate::Error::NoDocument),
}
}
} }