Added captcha to booking form. Deleted useless files.

main
Josef Rokos 1 year ago
parent 1b1b566267
commit ab3e85573d

84
Cargo.lock generated

@ -53,7 +53,7 @@ dependencies = [
"actix-service", "actix-service",
"actix-utils", "actix-utils",
"ahash 0.8.3", "ahash 0.8.3",
"base64 0.21.2", "base64 0.21.7",
"bitflags 2.4.0", "bitflags 2.4.0",
"brotli", "brotli",
"bytes", "bytes",
@ -470,9 +470,9 @@ checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.2" version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
@ -864,7 +864,7 @@ dependencies = [
"hmac 0.12.1", "hmac 0.12.1",
"percent-encoding", "percent-encoding",
"rand", "rand",
"sha2 0.10.7", "sha2 0.10.8",
"subtle", "subtle",
"time", "time",
"version_check", "version_check",
@ -1157,7 +1157,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75" checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75"
dependencies = [ dependencies = [
"base64 0.21.2", "base64 0.21.7",
"memchr", "memchr",
] ]
@ -1428,9 +1428,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.10" version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
@ -1899,6 +1899,16 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "leptos-captcha"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad333c9ec3968e6a00dbf227a9f4ecd08931b23d2d720ec0b3cfc1117000b5b"
dependencies = [
"leptos",
"spow",
]
[[package]] [[package]]
name = "leptos-use" name = "leptos-use"
version = "0.10.1" version = "0.10.1"
@ -2062,7 +2072,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b30c5bc7f3496d6ba399578171cf133c50d2172f9791fe292db4da2fd0d8cec4" checksum = "b30c5bc7f3496d6ba399578171cf133c50d2172f9791fe292db4da2fd0d8cec4"
dependencies = [ dependencies = [
"base64 0.21.2", "base64 0.21.7",
"cfg-if", "cfg-if",
"futures", "futures",
"indexmap 2.0.0", "indexmap 2.0.0",
@ -2138,7 +2148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a48c2e9831b370bc2d7233c2620298c45f3a158ed6b4b8d7416b2ada5a268fd8" checksum = "a48c2e9831b370bc2d7233c2620298c45f3a158ed6b4b8d7416b2ada5a268fd8"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"base64 0.21.2", "base64 0.21.7",
"chumsky", "chumsky",
"email-encoding", "email-encoding",
"email_address", "email_address",
@ -2162,9 +2172,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.147" version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]] [[package]]
name = "libm" name = "libm"
@ -2871,6 +2881,7 @@ dependencies = [
"getopts", "getopts",
"lazy_static", "lazy_static",
"leptos", "leptos",
"leptos-captcha",
"leptos-use", "leptos-use",
"leptos_actix", "leptos_actix",
"leptos_meta", "leptos_meta",
@ -3030,7 +3041,7 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
dependencies = [ dependencies = [
"base64 0.21.2", "base64 0.21.7",
] ]
[[package]] [[package]]
@ -3320,13 +3331,23 @@ dependencies = [
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.7" version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
"digest 0.10.7", "digest 0.10.7",
"sha2-asm",
]
[[package]]
name = "sha2-asm"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27ba7066011e3fb30d808b51affff34f0a66d3a03a58edd787c6e420e40e44e"
dependencies = [
"cc",
] ]
[[package]] [[package]]
@ -3424,6 +3445,21 @@ dependencies = [
"der", "der",
] ]
[[package]]
name = "spow"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33d19caa42297b681c88a844ace448357a2b107e723a9d26239338c8d37aa58"
dependencies = [
"base64 0.21.7",
"chrono",
"getrandom",
"nom",
"serde",
"sha2 0.10.8",
"thiserror",
]
[[package]] [[package]]
name = "sqlformat" name = "sqlformat"
version = "0.2.1" version = "0.2.1"
@ -3482,7 +3518,7 @@ dependencies = [
"rustls-pemfile", "rustls-pemfile",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.10.7", "sha2 0.10.8",
"smallvec", "smallvec",
"sqlformat", "sqlformat",
"thiserror", "thiserror",
@ -3522,7 +3558,7 @@ dependencies = [
"quote", "quote",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.10.7", "sha2 0.10.8",
"sqlx-core", "sqlx-core",
"sqlx-mysql", "sqlx-mysql",
"sqlx-postgres", "sqlx-postgres",
@ -3540,7 +3576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca69bf415b93b60b80dc8fda3cb4ef52b2336614d8da2de5456cc942a110482" checksum = "8ca69bf415b93b60b80dc8fda3cb4ef52b2336614d8da2de5456cc942a110482"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64 0.21.2", "base64 0.21.7",
"bitflags 2.4.0", "bitflags 2.4.0",
"byteorder", "byteorder",
"bytes", "bytes",
@ -3568,7 +3604,7 @@ dependencies = [
"rust_decimal", "rust_decimal",
"serde", "serde",
"sha1", "sha1",
"sha2 0.10.7", "sha2 0.10.8",
"smallvec", "smallvec",
"sqlx-core", "sqlx-core",
"stringprep", "stringprep",
@ -3585,7 +3621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0db2df1b8731c3651e204629dd55e52adbae0462fa1bdcbed56a2302c18181e" checksum = "a0db2df1b8731c3651e204629dd55e52adbae0462fa1bdcbed56a2302c18181e"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64 0.21.2", "base64 0.21.7",
"bitflags 2.4.0", "bitflags 2.4.0",
"byteorder", "byteorder",
"chrono", "chrono",
@ -3611,7 +3647,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sha1", "sha1",
"sha2 0.10.7", "sha2 0.10.8",
"smallvec", "smallvec",
"sqlx-core", "sqlx-core",
"stringprep", "stringprep",
@ -3735,18 +3771,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.40" version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.40" version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

@ -34,6 +34,7 @@ env_logger = "0.11"
getopts = "0.2.21" getopts = "0.2.21"
leptos-use = "0.10.1" leptos-use = "0.10.1"
lettre = {version = "0.11", features = ["tokio1-native-tls", "smtp-transport", "file-transport"], optional = true} lettre = {version = "0.11", features = ["tokio1-native-tls", "smtp-transport", "file-transport"], optional = true}
leptos-captcha = "0.2.0"
[features] [features]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"] csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 681 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

@ -1,27 +0,0 @@
/**
* Config
* -------------------------------------------------------------------------------------
* ! IMPORTANT: Make sure you clear the browser local storage In order to see the config changes in the template.
* ! To clear local storage: (https://www.leadshook.com/help/how-to-clear-local-storage-in-google-chrome-browser/).
*/
'use strict';
// JS global variables
let config = {
colors: {
primary: '#696cff',
secondary: '#8592a3',
success: '#71dd37',
info: '#03c3ec',
warning: '#ffab00',
danger: '#ff3e1d',
dark: '#233446',
black: '#000',
white: '#fff',
body: '#f4f5fb',
headingColor: '#566a7f',
axisColor: '#a1acb8',
borderColor: '#eceef1'
}
};

@ -1,662 +0,0 @@
/**
* Dashboard Analytics
*/
'use strict';
(function () {
let cardColor, headingColor, axisColor, shadeColor, borderColor;
cardColor = config.colors.white;
headingColor = config.colors.headingColor;
axisColor = config.colors.axisColor;
borderColor = config.colors.borderColor;
// Total Revenue Report Chart - Bar Chart
// --------------------------------------------------------------------
const totalRevenueChartEl = document.querySelector('#totalRevenueChart'),
totalRevenueChartOptions = {
series: [
{
name: '2021',
data: [18, 7, 15, 29, 18, 12, 9]
},
{
name: '2020',
data: [-13, -18, -9, -14, -5, -17, -15]
}
],
chart: {
height: 300,
stacked: true,
type: 'bar',
toolbar: { show: false }
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: '33%',
borderRadius: 12,
startingShape: 'rounded',
endingShape: 'rounded'
}
},
colors: [config.colors.primary, config.colors.info],
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth',
width: 6,
lineCap: 'round',
colors: [cardColor]
},
legend: {
show: true,
horizontalAlign: 'left',
position: 'top',
markers: {
height: 8,
width: 8,
radius: 12,
offsetX: -3
},
labels: {
colors: axisColor
},
itemMargin: {
horizontal: 10
}
},
grid: {
borderColor: borderColor,
padding: {
top: 0,
bottom: -8,
left: 20,
right: 20
}
},
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
labels: {
style: {
fontSize: '13px',
colors: axisColor
}
},
axisTicks: {
show: false
},
axisBorder: {
show: false
}
},
yaxis: {
labels: {
style: {
fontSize: '13px',
colors: axisColor
}
}
},
responsive: [
{
breakpoint: 1700,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '32%'
}
}
}
},
{
breakpoint: 1580,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '35%'
}
}
}
},
{
breakpoint: 1440,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '42%'
}
}
}
},
{
breakpoint: 1300,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '48%'
}
}
}
},
{
breakpoint: 1200,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '40%'
}
}
}
},
{
breakpoint: 1040,
options: {
plotOptions: {
bar: {
borderRadius: 11,
columnWidth: '48%'
}
}
}
},
{
breakpoint: 991,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '30%'
}
}
}
},
{
breakpoint: 840,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '35%'
}
}
}
},
{
breakpoint: 768,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '28%'
}
}
}
},
{
breakpoint: 640,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '32%'
}
}
}
},
{
breakpoint: 576,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '37%'
}
}
}
},
{
breakpoint: 480,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '45%'
}
}
}
},
{
breakpoint: 420,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '52%'
}
}
}
},
{
breakpoint: 380,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '60%'
}
}
}
}
],
states: {
hover: {
filter: {
type: 'none'
}
},
active: {
filter: {
type: 'none'
}
}
}
};
if (typeof totalRevenueChartEl !== undefined && totalRevenueChartEl !== null) {
const totalRevenueChart = new ApexCharts(totalRevenueChartEl, totalRevenueChartOptions);
totalRevenueChart.render();
}
// Growth Chart - Radial Bar Chart
// --------------------------------------------------------------------
const growthChartEl = document.querySelector('#growthChart'),
growthChartOptions = {
series: [78],
labels: ['Growth'],
chart: {
height: 240,
type: 'radialBar'
},
plotOptions: {
radialBar: {
size: 150,
offsetY: 10,
startAngle: -150,
endAngle: 150,
hollow: {
size: '55%'
},
track: {
background: cardColor,
strokeWidth: '100%'
},
dataLabels: {
name: {
offsetY: 15,
color: headingColor,
fontSize: '15px',
fontWeight: '600',
fontFamily: 'Public Sans'
},
value: {
offsetY: -25,
color: headingColor,
fontSize: '22px',
fontWeight: '500',
fontFamily: 'Public Sans'
}
}
}
},
colors: [config.colors.primary],
fill: {
type: 'gradient',
gradient: {
shade: 'dark',
shadeIntensity: 0.5,
gradientToColors: [config.colors.primary],
inverseColors: true,
opacityFrom: 1,
opacityTo: 0.6,
stops: [30, 70, 100]
}
},
stroke: {
dashArray: 5
},
grid: {
padding: {
top: -35,
bottom: -10
}
},
states: {
hover: {
filter: {
type: 'none'
}
},
active: {
filter: {
type: 'none'
}
}
}
};
if (typeof growthChartEl !== undefined && growthChartEl !== null) {
const growthChart = new ApexCharts(growthChartEl, growthChartOptions);
growthChart.render();
}
// Profit Report Line Chart
// --------------------------------------------------------------------
const profileReportChartEl = document.querySelector('#profileReportChart'),
profileReportChartConfig = {
chart: {
height: 80,
// width: 175,
type: 'line',
toolbar: {
show: false
},
dropShadow: {
enabled: true,
top: 10,
left: 5,
blur: 3,
color: config.colors.warning,
opacity: 0.15
},
sparkline: {
enabled: true
}
},
grid: {
show: false,
padding: {
right: 8
}
},
colors: [config.colors.warning],
dataLabels: {
enabled: false
},
stroke: {
width: 5,
curve: 'smooth'
},
series: [
{
data: [110, 270, 145, 245, 205, 285]
}
],
xaxis: {
show: false,
lines: {
show: false
},
labels: {
show: false
},
axisBorder: {
show: false
}
},
yaxis: {
show: false
}
};
if (typeof profileReportChartEl !== undefined && profileReportChartEl !== null) {
const profileReportChart = new ApexCharts(profileReportChartEl, profileReportChartConfig);
profileReportChart.render();
}
// Order Statistics Chart
// --------------------------------------------------------------------
const chartOrderStatistics = document.querySelector('#orderStatisticsChart'),
orderChartConfig = {
chart: {
height: 165,
width: 130,
type: 'donut'
},
labels: ['Electronic', 'Sports', 'Decor', 'Fashion'],
series: [85, 15, 50, 50],
colors: [config.colors.primary, config.colors.secondary, config.colors.info, config.colors.success],
stroke: {
width: 5,
colors: cardColor
},
dataLabels: {
enabled: false,
formatter: function (val, opt) {
return parseInt(val) + '%';
}
},
legend: {
show: false
},
grid: {
padding: {
top: 0,
bottom: 0,
right: 15
}
},
plotOptions: {
pie: {
donut: {
size: '75%',
labels: {
show: true,
value: {
fontSize: '1.5rem',
fontFamily: 'Public Sans',
color: headingColor,
offsetY: -15,
formatter: function (val) {
return parseInt(val) + '%';
}
},
name: {
offsetY: 20,
fontFamily: 'Public Sans'
},
total: {
show: true,
fontSize: '0.8125rem',
color: axisColor,
label: 'Weekly',
formatter: function (w) {
return '38%';
}
}
}
}
}
}
};
if (typeof chartOrderStatistics !== undefined && chartOrderStatistics !== null) {
const statisticsChart = new ApexCharts(chartOrderStatistics, orderChartConfig);
statisticsChart.render();
}
// Income Chart - Area chart
// --------------------------------------------------------------------
const incomeChartEl = document.querySelector('#incomeChart'),
incomeChartConfig = {
series: [
{
data: [24, 21, 30, 22, 42, 26, 35, 29]
}
],
chart: {
height: 215,
parentHeightOffset: 0,
parentWidthOffset: 0,
toolbar: {
show: false
},
type: 'area'
},
dataLabels: {
enabled: false
},
stroke: {
width: 2,
curve: 'smooth'
},
legend: {
show: false
},
markers: {
size: 6,
colors: 'transparent',
strokeColors: 'transparent',
strokeWidth: 4,
discrete: [
{
fillColor: config.colors.white,
seriesIndex: 0,
dataPointIndex: 7,
strokeColor: config.colors.primary,
strokeWidth: 2,
size: 6,
radius: 8
}
],
hover: {
size: 7
}
},
colors: [config.colors.primary],
fill: {
type: 'gradient',
gradient: {
shade: shadeColor,
shadeIntensity: 0.6,
opacityFrom: 0.5,
opacityTo: 0.25,
stops: [0, 95, 100]
}
},
grid: {
borderColor: borderColor,
strokeDashArray: 3,
padding: {
top: -20,
bottom: -8,
left: -10,
right: 8
}
},
xaxis: {
categories: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
axisBorder: {
show: false
},
axisTicks: {
show: false
},
labels: {
show: true,
style: {
fontSize: '13px',
colors: axisColor
}
}
},
yaxis: {
labels: {
show: false
},
min: 10,
max: 50,
tickAmount: 4
}
};
if (typeof incomeChartEl !== undefined && incomeChartEl !== null) {
const incomeChart = new ApexCharts(incomeChartEl, incomeChartConfig);
incomeChart.render();
}
// Expenses Mini Chart - Radial Chart
// --------------------------------------------------------------------
const weeklyExpensesEl = document.querySelector('#expensesOfWeek'),
weeklyExpensesConfig = {
series: [65],
chart: {
width: 60,
height: 60,
type: 'radialBar'
},
plotOptions: {
radialBar: {
startAngle: 0,
endAngle: 360,
strokeWidth: '8',
hollow: {
margin: 2,
size: '45%'
},
track: {
strokeWidth: '50%',
background: borderColor
},
dataLabels: {
show: true,
name: {
show: false
},
value: {
formatter: function (val) {
return '$' + parseInt(val);
},
offsetY: 5,
color: '#697a8d',
fontSize: '13px',
show: true
}
}
}
},
fill: {
type: 'solid',
colors: config.colors.primary
},
stroke: {
lineCap: 'round'
},
grid: {
padding: {
top: -10,
bottom: -15,
left: -10,
right: -10
}
},
states: {
hover: {
filter: {
type: 'none'
}
},
active: {
filter: {
type: 'none'
}
}
}
};
if (typeof weeklyExpensesEl !== undefined && weeklyExpensesEl !== null) {
const weeklyExpenses = new ApexCharts(weeklyExpensesEl, weeklyExpensesConfig);
weeklyExpenses.render();
}
})();

@ -1,37 +0,0 @@
/**
* Perfect Scrollbar
*/
'use strict';
document.addEventListener('DOMContentLoaded', function () {
(function () {
const verticalExample = document.getElementById('vertical-example'),
horizontalExample = document.getElementById('horizontal-example'),
horizVertExample = document.getElementById('both-scrollbars-example');
// Vertical Example
// --------------------------------------------------------------------
if (verticalExample) {
new PerfectScrollbar(verticalExample, {
wheelPropagation: false
});
}
// Horizontal Example
// --------------------------------------------------------------------
if (horizontalExample) {
new PerfectScrollbar(horizontalExample, {
wheelPropagation: false,
suppressScrollY: true
});
}
// Both vertical and Horizontal Example
// --------------------------------------------------------------------
if (horizVertExample) {
new PerfectScrollbar(horizVertExample, {
wheelPropagation: false
});
}
})();
});

@ -1,11 +0,0 @@
/**
* Form Basic Inputs
*/
'use strict';
(function () {
// Indeterminate checkbox
const checkbox = document.getElementById('defaultCheck2');
checkbox.indeterminate = true;
})();

@ -1,118 +0,0 @@
/**
* Main
*/
'use strict';
let menu, animate;
(function () {
// Initialize menu
//-----------------
let layoutMenuEl = document.querySelectorAll('#layout-menu');
layoutMenuEl.forEach(function (element) {
menu = new Menu(element, {
orientation: 'vertical',
closeChildren: false
});
// Change parameter to true if you want scroll animation
window.Helpers.scrollToActive((animate = false));
window.Helpers.mainMenu = menu;
});
// Initialize menu togglers and bind click on each
let menuToggler = document.querySelectorAll('.layout-menu-toggle');
menuToggler.forEach(item => {
item.addEventListener('click', event => {
event.preventDefault();
window.Helpers.toggleCollapsed();
});
});
// Display menu toggle (layout-menu-toggle) on hover with delay
let delay = function (elem, callback) {
let timeout = null;
elem.onmouseenter = function () {
// Set timeout to be a timer which will invoke callback after 300ms (not for small screen)
if (!Helpers.isSmallScreen()) {
timeout = setTimeout(callback, 300);
} else {
timeout = setTimeout(callback, 0);
}
};
elem.onmouseleave = function () {
// Clear any timers set to timeout
document.querySelector('.layout-menu-toggle').classList.remove('d-block');
clearTimeout(timeout);
};
};
if (document.getElementById('layout-menu')) {
delay(document.getElementById('layout-menu'), function () {
// not for small screen
if (!Helpers.isSmallScreen()) {
document.querySelector('.layout-menu-toggle').classList.add('d-block');
}
});
}
// Display in main menu when menu scrolls
let menuInnerContainer = document.getElementsByClassName('menu-inner'),
menuInnerShadow = document.getElementsByClassName('menu-inner-shadow')[0];
if (menuInnerContainer.length > 0 && menuInnerShadow) {
menuInnerContainer[0].addEventListener('ps-scroll-y', function () {
if (this.querySelector('.ps__thumb-y').offsetTop) {
menuInnerShadow.style.display = 'block';
} else {
menuInnerShadow.style.display = 'none';
}
});
}
// Init helpers & misc
// --------------------
// Init BS Tooltip
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// Accordion active class
const accordionActiveFunction = function (e) {
if (e.type == 'show.bs.collapse' || e.type == 'show.bs.collapse') {
e.target.closest('.accordion-item').classList.add('active');
} else {
e.target.closest('.accordion-item').classList.remove('active');
}
};
const accordionTriggerList = [].slice.call(document.querySelectorAll('.accordion'));
const accordionList = accordionTriggerList.map(function (accordionTriggerEl) {
accordionTriggerEl.addEventListener('show.bs.collapse', accordionActiveFunction);
accordionTriggerEl.addEventListener('hide.bs.collapse', accordionActiveFunction);
});
// Auto update layout based on screen size
window.Helpers.setAutoUpdate(true);
// Toggle Password Visibility
window.Helpers.initPasswordToggle();
// Speech To Text
window.Helpers.initSpeechToText();
// Manage menu expanded/collapsed with templateCustomizer & local storage
//------------------------------------------------------------------
// If current layout is horizontal OR current window screen is small (overlay menu) than return from here
if (window.Helpers.isSmallScreen()) {
return;
}
// If current layout is vertical and current window screen is > small
// Auto update menu collapsed/expanded based on the themeConfig
window.Helpers.setCollapsed(true, false);
})();

@ -1,29 +0,0 @@
/**
* Account Settings - Account
*/
'use strict';
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
const deactivateAcc = document.querySelector('#formAccountDeactivation');
// Update/reset user image of account page
let accountUserImage = document.getElementById('uploadedAvatar');
const fileInput = document.querySelector('.account-file-input'),
resetFileInput = document.querySelector('.account-image-reset');
if (accountUserImage) {
const resetImage = accountUserImage.src;
fileInput.onchange = () => {
if (fileInput.files[0]) {
accountUserImage.src = window.URL.createObjectURL(fileInput.files[0]);
}
};
resetFileInput.onclick = () => {
fileInput.value = '';
accountUserImage.src = resetImage;
};
}
})();
});

@ -1,33 +0,0 @@
/**
* UI Modals
*/
'use strict';
(function () {
// On hiding modal, remove iframe video/audio to stop playing
const youTubeModal = document.querySelector('#youTubeModal'),
youTubeModalVideo = youTubeModal.querySelector('iframe');
youTubeModal.addEventListener('hidden.bs.modal', function () {
youTubeModalVideo.setAttribute('src', '');
});
// Function to get and auto play youTube video
const autoPlayYouTubeModal = function () {
const modalTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="modal"]'));
modalTriggerList.map(function (modalTriggerEl) {
modalTriggerEl.onclick = function () {
const theModal = this.getAttribute('data-bs-target'),
videoSRC = this.getAttribute('data-theVideo'),
videoSRCauto = `${videoSRC}?autoplay=1`,
modalVideo = document.querySelector(`${theModal} iframe`);
if (modalVideo) {
modalVideo.setAttribute('src', videoSRCauto);
}
};
});
};
// Calling function on load
autoPlayYouTubeModal();
})();

@ -1,13 +0,0 @@
// /**
// * UI Tooltips & Popovers
// */
'use strict';
(function () {
const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
const popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
// added { html: true, sanitize: false } option to render button in content area of popover
return new bootstrap.Popover(popoverTriggerEl, { html: true, sanitize: false });
});
})();

@ -1,39 +0,0 @@
/**
* UI Toasts
*/
'use strict';
(function () {
// Bootstrap toasts example
// --------------------------------------------------------------------
const toastPlacementExample = document.querySelector('.toast-placement-ex'),
toastPlacementBtn = document.querySelector('#showToastPlacement');
let selectedType, selectedPlacement, toastPlacement;
// Dispose toast when open another
function toastDispose(toast) {
if (toast && toast._element !== null) {
if (toastPlacementExample) {
toastPlacementExample.classList.remove(selectedType);
DOMTokenList.prototype.remove.apply(toastPlacementExample.classList, selectedPlacement);
}
toast.dispose();
}
}
// Placement Button click
if (toastPlacementBtn) {
toastPlacementBtn.onclick = function () {
if (toastPlacement) {
toastDispose(toastPlacement);
}
selectedType = document.querySelector('#selectTypeOpt').value;
selectedPlacement = document.querySelector('#selectPlacement').value.split(' ');
toastPlacementExample.classList.add(selectedType);
DOMTokenList.prototype.add.apply(toastPlacementExample.classList, selectedPlacement);
toastPlacement = new bootstrap.Toast(toastPlacementExample);
toastPlacement.show();
};
}
})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,746 +0,0 @@
/**
* Apex Charts
*/
.apexcharts-canvas {
position: relative;
user-select: none;
/* cannot give overflow: hidden as it will crop tooltips which overflow outside chart area */
}
/* scrollbar is not visible by default for legend, hence forcing the visibility */
.apexcharts-canvas ::-webkit-scrollbar {
-webkit-appearance: none;
width: 6px;
}
.apexcharts-canvas ::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.5);
box-shadow: 0 0 1px rgba(255, 255, 255, 0.5);
-webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 0.5);
}
.apexcharts-inner {
position: relative;
}
.apexcharts-text tspan {
font-family: inherit;
}
.legend-mouseover-inactive {
transition: 0.15s ease all;
opacity: 0.2;
}
.apexcharts-series-collapsed {
opacity: 0;
}
.apexcharts-tooltip {
border-radius: 5px;
box-shadow: 2px 2px 6px -4px #999;
cursor: default;
font-size: 14px;
left: 62px;
opacity: 0;
pointer-events: none;
position: absolute;
top: 20px;
display: flex;
flex-direction: column;
overflow: hidden;
white-space: nowrap;
z-index: 12;
transition: 0.15s ease all;
}
.apexcharts-tooltip.apexcharts-active {
opacity: 1;
transition: 0.15s ease all;
}
.apexcharts-tooltip.apexcharts-theme-light {
border: 1px solid #e3e3e3;
background: rgba(255, 255, 255, 0.96);
}
.apexcharts-tooltip.apexcharts-theme-dark {
color: #fff;
background: rgba(30, 30, 30, 0.8);
}
.apexcharts-tooltip * {
font-family: inherit;
}
.apexcharts-tooltip-title {
padding: 6px;
font-size: 15px;
margin-bottom: 4px;
}
.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {
background: #ECEFF1;
border-bottom: 1px solid #ddd;
}
.apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {
background: rgba(0, 0, 0, 0.7);
border-bottom: 1px solid #333;
}
.apexcharts-tooltip-text-y-value,
.apexcharts-tooltip-text-goals-value,
.apexcharts-tooltip-text-z-value {
display: inline-block;
font-weight: 600;
margin-left: 5px;
}
.apexcharts-tooltip-text-y-label:empty,
.apexcharts-tooltip-text-y-value:empty,
.apexcharts-tooltip-text-goals-label:empty,
.apexcharts-tooltip-text-goals-value:empty,
.apexcharts-tooltip-text-z-value:empty {
display: none;
}
.apexcharts-tooltip-text-y-value,
.apexcharts-tooltip-text-goals-value,
.apexcharts-tooltip-text-z-value {
font-weight: 600;
}
.apexcharts-tooltip-text-goals-label,
.apexcharts-tooltip-text-goals-value {
padding: 6px 0 5px;
}
.apexcharts-tooltip-goals-group,
.apexcharts-tooltip-text-goals-label,
.apexcharts-tooltip-text-goals-value {
display: flex;
}
.apexcharts-tooltip-text-goals-label:not(:empty),
.apexcharts-tooltip-text-goals-value:not(:empty) {
margin-top: -6px;
}
.apexcharts-tooltip-marker {
width: 12px;
height: 12px;
position: relative;
top: 0px;
margin-right: 10px;
border-radius: 50%;
}
.apexcharts-tooltip-series-group {
padding: 0 10px;
display: none;
text-align: left;
justify-content: left;
align-items: center;
}
.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker {
opacity: 1;
}
.apexcharts-tooltip-series-group.apexcharts-active,
.apexcharts-tooltip-series-group:last-child {
padding-bottom: 4px;
}
.apexcharts-tooltip-series-group-hidden {
opacity: 0;
height: 0;
line-height: 0;
padding: 0 !important;
}
.apexcharts-tooltip-y-group {
padding: 6px 0 5px;
}
.apexcharts-tooltip-box, .apexcharts-custom-tooltip {
padding: 4px 8px;
}
.apexcharts-tooltip-boxPlot {
display: flex;
flex-direction: column-reverse;
}
.apexcharts-tooltip-box > div {
margin: 4px 0;
}
.apexcharts-tooltip-box span.value {
font-weight: bold;
}
.apexcharts-tooltip-rangebar {
padding: 5px 8px;
}
.apexcharts-tooltip-rangebar .category {
font-weight: 600;
color: #777;
}
.apexcharts-tooltip-rangebar .series-name {
font-weight: bold;
display: block;
margin-bottom: 5px;
}
.apexcharts-xaxistooltip {
opacity: 0;
padding: 9px 10px;
pointer-events: none;
color: #373d3f;
font-size: 13px;
text-align: center;
border-radius: 2px;
position: absolute;
z-index: 10;
background: #ECEFF1;
border: 1px solid #90A4AE;
transition: 0.15s ease all;
}
.apexcharts-xaxistooltip.apexcharts-theme-dark {
background: rgba(0, 0, 0, 0.7);
border: 1px solid rgba(0, 0, 0, 0.5);
color: #fff;
}
.apexcharts-xaxistooltip:after,
.apexcharts-xaxistooltip:before {
left: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.apexcharts-xaxistooltip:after {
border-color: rgba(236, 239, 241, 0);
border-width: 6px;
margin-left: -6px;
}
.apexcharts-xaxistooltip:before {
border-color: rgba(144, 164, 174, 0);
border-width: 7px;
margin-left: -7px;
}
.apexcharts-xaxistooltip-bottom:after,
.apexcharts-xaxistooltip-bottom:before {
bottom: 100%;
}
.apexcharts-xaxistooltip-top:after,
.apexcharts-xaxistooltip-top:before {
top: 100%;
}
.apexcharts-xaxistooltip-bottom:after {
border-bottom-color: #ECEFF1;
}
.apexcharts-xaxistooltip-bottom:before {
border-bottom-color: #90A4AE;
}
.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:after {
border-bottom-color: rgba(0, 0, 0, 0.5);
}
.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:before {
border-bottom-color: rgba(0, 0, 0, 0.5);
}
.apexcharts-xaxistooltip-top:after {
border-top-color: #ECEFF1;
}
.apexcharts-xaxistooltip-top:before {
border-top-color: #90A4AE;
}
.apexcharts-xaxistooltip-top.apexcharts-theme-dark:after {
border-top-color: rgba(0, 0, 0, 0.5);
}
.apexcharts-xaxistooltip-top.apexcharts-theme-dark:before {
border-top-color: rgba(0, 0, 0, 0.5);
}
.apexcharts-xaxistooltip.apexcharts-active {
opacity: 1;
transition: 0.15s ease all;
}
.apexcharts-yaxistooltip {
opacity: 0;
padding: 4px 10px;
pointer-events: none;
color: #373d3f;
font-size: 13px;
text-align: center;
border-radius: 2px;
position: absolute;
z-index: 10;
background: #ECEFF1;
border: 1px solid #90A4AE;
}
.apexcharts-yaxistooltip.apexcharts-theme-dark {
background: rgba(0, 0, 0, 0.7);
border: 1px solid rgba(0, 0, 0, 0.5);
color: #fff;
}
.apexcharts-yaxistooltip:after,
.apexcharts-yaxistooltip:before {
top: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.apexcharts-yaxistooltip:after {
border-color: rgba(236, 239, 241, 0);
border-width: 6px;
margin-top: -6px;
}
.apexcharts-yaxistooltip:before {
border-color: rgba(144, 164, 174, 0);
border-width: 7px;
margin-top: -7px;
}
.apexcharts-yaxistooltip-left:after,
.apexcharts-yaxistooltip-left:before {
left: 100%;
}
.apexcharts-yaxistooltip-right:after,
.apexcharts-yaxistooltip-right:before {
right: 100%;
}
.apexcharts-yaxistooltip-left:after {
border-left-color: #ECEFF1;
}
.apexcharts-yaxistooltip-left:before {
border-left-color: #90A4AE;
}
.apexcharts-yaxistooltip-left.apexcharts-theme-dark:after {
border-left-color: rgba(0, 0, 0, 0.5);
}
.apexcharts-yaxistooltip-left.apexcharts-theme-dark:before {
border-left-color: rgba(0, 0, 0, 0.5);
}
.apexcharts-yaxistooltip-right:after {
border-right-color: #ECEFF1;
}
.apexcharts-yaxistooltip-right:before {
border-right-color: #90A4AE;
}
.apexcharts-yaxistooltip-right.apexcharts-theme-dark:after {
border-right-color: rgba(0, 0, 0, 0.5);
}
.apexcharts-yaxistooltip-right.apexcharts-theme-dark:before {
border-right-color: rgba(0, 0, 0, 0.5);
}
.apexcharts-yaxistooltip.apexcharts-active {
opacity: 1;
}
.apexcharts-yaxistooltip-hidden {
display: none;
}
.apexcharts-xcrosshairs,
.apexcharts-ycrosshairs {
pointer-events: none;
opacity: 0;
transition: 0.15s ease all;
}
.apexcharts-xcrosshairs.apexcharts-active,
.apexcharts-ycrosshairs.apexcharts-active {
opacity: 1;
transition: 0.15s ease all;
}
.apexcharts-ycrosshairs-hidden {
opacity: 0;
}
.apexcharts-selection-rect {
cursor: move;
}
.svg_select_boundingRect, .svg_select_points_rot {
pointer-events: none;
opacity: 0;
visibility: hidden;
}
.apexcharts-selection-rect + g .svg_select_boundingRect,
.apexcharts-selection-rect + g .svg_select_points_rot {
opacity: 0;
visibility: hidden;
}
.apexcharts-selection-rect + g .svg_select_points_l,
.apexcharts-selection-rect + g .svg_select_points_r {
cursor: ew-resize;
opacity: 1;
visibility: visible;
}
.svg_select_points {
fill: #efefef;
stroke: #333;
rx: 2;
}
.apexcharts-svg.apexcharts-zoomable.hovering-zoom {
cursor: crosshair;
}
.apexcharts-svg.apexcharts-zoomable.hovering-pan {
cursor: move;
}
.apexcharts-zoom-icon,
.apexcharts-zoomin-icon,
.apexcharts-zoomout-icon,
.apexcharts-reset-icon,
.apexcharts-pan-icon,
.apexcharts-selection-icon,
.apexcharts-menu-icon,
.apexcharts-toolbar-custom-icon {
cursor: pointer;
width: 20px;
height: 20px;
line-height: 24px;
color: #6E8192;
text-align: center;
}
.apexcharts-zoom-icon svg,
.apexcharts-zoomin-icon svg,
.apexcharts-zoomout-icon svg,
.apexcharts-reset-icon svg,
.apexcharts-menu-icon svg {
fill: #6E8192;
}
.apexcharts-selection-icon svg {
fill: #444;
transform: scale(0.76);
}
.apexcharts-theme-dark .apexcharts-zoom-icon svg,
.apexcharts-theme-dark .apexcharts-zoomin-icon svg,
.apexcharts-theme-dark .apexcharts-zoomout-icon svg,
.apexcharts-theme-dark .apexcharts-reset-icon svg,
.apexcharts-theme-dark .apexcharts-pan-icon svg,
.apexcharts-theme-dark .apexcharts-selection-icon svg,
.apexcharts-theme-dark .apexcharts-menu-icon svg,
.apexcharts-theme-dark .apexcharts-toolbar-custom-icon svg {
fill: #f3f4f5;
}
.apexcharts-canvas .apexcharts-zoom-icon.apexcharts-selected svg,
.apexcharts-canvas .apexcharts-selection-icon.apexcharts-selected svg,
.apexcharts-canvas .apexcharts-reset-zoom-icon.apexcharts-selected svg {
fill: #008FFB;
}
.apexcharts-theme-light .apexcharts-selection-icon:not(.apexcharts-selected):hover svg,
.apexcharts-theme-light .apexcharts-zoom-icon:not(.apexcharts-selected):hover svg,
.apexcharts-theme-light .apexcharts-zoomin-icon:hover svg,
.apexcharts-theme-light .apexcharts-zoomout-icon:hover svg,
.apexcharts-theme-light .apexcharts-reset-icon:hover svg,
.apexcharts-theme-light .apexcharts-menu-icon:hover svg {
fill: #333;
}
.apexcharts-selection-icon,
.apexcharts-menu-icon {
position: relative;
}
.apexcharts-reset-icon {
margin-left: 5px;
}
.apexcharts-zoom-icon,
.apexcharts-reset-icon,
.apexcharts-menu-icon {
transform: scale(0.85);
}
.apexcharts-zoomin-icon,
.apexcharts-zoomout-icon {
transform: scale(0.7);
}
.apexcharts-zoomout-icon {
margin-right: 3px;
}
.apexcharts-pan-icon {
transform: scale(0.62);
position: relative;
left: 1px;
top: 0px;
}
.apexcharts-pan-icon svg {
fill: #fff;
stroke: #6E8192;
stroke-width: 2;
}
.apexcharts-pan-icon.apexcharts-selected svg {
stroke: #008FFB;
}
.apexcharts-pan-icon:not(.apexcharts-selected):hover svg {
stroke: #333;
}
.apexcharts-toolbar {
position: absolute;
z-index: 11;
max-width: 176px;
text-align: right;
border-radius: 3px;
padding: 0px 6px 2px 6px;
display: flex;
justify-content: space-between;
align-items: center;
}
.apexcharts-menu {
background: #fff;
position: absolute;
top: 100%;
border: 1px solid #ddd;
border-radius: 3px;
padding: 3px;
right: 10px;
opacity: 0;
min-width: 110px;
transition: 0.15s ease all;
pointer-events: none;
}
.apexcharts-menu.apexcharts-menu-open {
opacity: 1;
pointer-events: all;
transition: 0.15s ease all;
}
.apexcharts-menu-item {
padding: 6px 7px;
font-size: 12px;
cursor: pointer;
}
.apexcharts-theme-light .apexcharts-menu-item:hover {
background: #eee;
}
.apexcharts-theme-dark .apexcharts-menu {
background: rgba(0, 0, 0, 0.7);
color: #fff;
}
@media screen and (min-width: 768px) {
.apexcharts-canvas:hover .apexcharts-toolbar {
opacity: 1;
}
}
.apexcharts-datalabel.apexcharts-element-hidden {
opacity: 0;
}
.apexcharts-pie-label,
.apexcharts-datalabels,
.apexcharts-datalabel,
.apexcharts-datalabel-label,
.apexcharts-datalabel-value {
cursor: default;
pointer-events: none;
}
.apexcharts-pie-label-delay {
opacity: 0;
animation-name: opaque;
animation-duration: 0.3s;
animation-fill-mode: forwards;
animation-timing-function: ease;
}
.apexcharts-canvas .apexcharts-element-hidden {
opacity: 0;
}
.apexcharts-hide .apexcharts-series-points {
opacity: 0;
}
.apexcharts-gridline,
.apexcharts-annotation-rect,
.apexcharts-tooltip .apexcharts-marker,
.apexcharts-area-series .apexcharts-area,
.apexcharts-line,
.apexcharts-zoom-rect,
.apexcharts-toolbar svg,
.apexcharts-area-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,
.apexcharts-line-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,
.apexcharts-radar-series path,
.apexcharts-radar-series polygon {
pointer-events: none;
}
/* markers */
.apexcharts-marker {
transition: 0.15s ease all;
}
@keyframes opaque {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* Resize generated styles */
@keyframes resizeanim {
from {
opacity: 0;
}
to {
opacity: 0;
}
}
.resize-triggers {
animation: 1ms resizeanim;
visibility: hidden;
opacity: 0;
}
.resize-triggers,
.resize-triggers > div,
.contract-trigger:before {
content: " ";
display: block;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
.resize-triggers > div {
background: #eee;
overflow: auto;
}
.contract-trigger:before {
width: 200%;
height: 200%;
}
.light-style .apexcharts-canvas .apexcharts-tooltip {
background: #fff;
border-color: #d9dee3;
box-shadow: 0 0.25rem 1rem rgba(161, 172, 184, 0.45);
}
.light-style .apexcharts-canvas .apexcharts-tooltip .apexcharts-tooltip-title {
background: #fff;
border-color: #d9dee3;
font-family: var(--bs-font-sans-serif) !important;
font-weight: 600;
}
.light-style .apexcharts-canvas .apexcharts-xaxistooltip,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip {
background: #f5f5f9;
border-color: #d9dee3;
}
.light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-xaxistooltip-bottom:after, .light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-yaxistooltip-bottom:after,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-xaxistooltip-bottom:after,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-yaxistooltip-bottom:after {
border-bottom-color: #f5f5f9;
}
.light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-xaxistooltip-bottom:before, .light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-yaxistooltip-bottom:before,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-xaxistooltip-bottom:before,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-yaxistooltip-bottom:before {
border-bottom-color: #d9dee3;
}
.light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-xaxistooltip-left:after, .light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-yaxistooltip-left:after,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-xaxistooltip-left:after,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-yaxistooltip-left:after {
border-left-color: #f5f5f9;
}
.light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-xaxistooltip-left:before, .light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-yaxistooltip-left:before,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-xaxistooltip-left:before,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-yaxistooltip-left:before {
border-left-color: #d9dee3;
}
.light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-xaxistooltip-right:after, .light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-yaxistooltip-right:after,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-xaxistooltip-right:after,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-yaxistooltip-right:after {
border-right-color: #f5f5f9;
}
.light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-xaxistooltip-right:before, .light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-yaxistooltip-right:before,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-xaxistooltip-right:before,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-yaxistooltip-right:before {
border-right-color: #d9dee3;
}
.light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-xaxistooltip-top:after, .light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-yaxistooltip-top:after,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-xaxistooltip-top:after,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-yaxistooltip-top:after {
border-top-color: #f5f5f9;
}
.light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-xaxistooltip-top:before, .light-style .apexcharts-canvas .apexcharts-xaxistooltip.apexcharts-yaxistooltip-top:before,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-xaxistooltip-top:before,
.light-style .apexcharts-canvas .apexcharts-yaxistooltip.apexcharts-yaxistooltip-top:before {
border-top-color: #d9dee3;
}
.light-style .apexcharts-canvas .apexcharts-tooltip-text {
font-family: var(--bs-font-sans-serif) !important;
filter: none;
}
.light-style .apexcharts-canvas .apexcharts-marker {
filter: drop-shadow(0 2px 4px #a1acb866);
}

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
@import '../../node_modules/highlight.js/styles/github.css';

@ -1,95 +0,0 @@
/*
Atom One Light by Daniel Gamage
Original One Light Syntax theme from https://github.com/atom/one-light-syntax
base: #fafafa
mono-1: #383a42
mono-2: #686b77
mono-3: #a0a1a7
hue-1: #0184bb
hue-2: #4078f2
hue-3: #a626a4
hue-4: #50a14f
hue-5: #e45649
hue-5-2: #c91243
hue-6: #986801
hue-6-2: #c18401
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #383a42;
background: #fafafa;
}
.hljs-comment,
.hljs-quote {
color: #a0a1a7;
font-style: italic;
}
.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #a626a4;
}
.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #e45649;
}
.hljs-literal {
color: #0184bb;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attribute,
.hljs-meta-string {
color: #50a14f;
}
.hljs-built_in,
.hljs-class .hljs-title {
color: #c18401;
}
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-type,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #986801;
}
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-title {
color: #4078f2;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,211 +0,0 @@
/*
* Container style
*/
.ps {
overflow: hidden !important;
overflow-anchor: none;
-ms-overflow-style: none;
touch-action: auto;
-ms-touch-action: auto;
}
/*
* Scrollbar rail styles
*/
.ps__rail-x {
display: none;
opacity: 0;
transition: background-color 0.2s linear, opacity 0.2s linear;
-webkit-transition: background-color 0.2s linear, opacity 0.2s linear;
height: 15px;
/* there must be 'bottom' or 'top' for ps__rail-x */
bottom: 0px;
/* please don't change 'position' */
position: absolute;
}
.ps__rail-y {
display: none;
opacity: 0;
transition: background-color 0.2s linear, opacity 0.2s linear;
-webkit-transition: background-color 0.2s linear, opacity 0.2s linear;
width: 15px;
/* there must be 'right' or 'left' for ps__rail-y */
right: 0;
/* please don't change 'position' */
position: absolute;
}
.ps--active-x > .ps__rail-x,
.ps--active-y > .ps__rail-y {
display: block;
background-color: transparent;
}
.ps:hover > .ps__rail-x,
.ps:hover > .ps__rail-y,
.ps--focus > .ps__rail-x,
.ps--focus > .ps__rail-y,
.ps--scrolling-x > .ps__rail-x,
.ps--scrolling-y > .ps__rail-y {
opacity: 0.6;
}
.ps .ps__rail-x:hover,
.ps .ps__rail-y:hover,
.ps .ps__rail-x:focus,
.ps .ps__rail-y:focus,
.ps .ps__rail-x.ps--clicking,
.ps .ps__rail-y.ps--clicking {
background-color: #eee;
opacity: 0.9;
}
/*
* Scrollbar thumb styles
*/
.ps__thumb-x {
background-color: #aaa;
border-radius: 6px;
transition: background-color 0.2s linear, height 0.2s ease-in-out;
-webkit-transition: background-color 0.2s linear, height 0.2s ease-in-out;
height: 6px;
/* there must be 'bottom' for ps__thumb-x */
bottom: 2px;
/* please don't change 'position' */
position: absolute;
}
.ps__thumb-y {
background-color: #aaa;
border-radius: 6px;
transition: background-color 0.2s linear, width 0.2s ease-in-out;
-webkit-transition: background-color 0.2s linear, width 0.2s ease-in-out;
width: 6px;
/* there must be 'right' for ps__thumb-y */
right: 2px;
/* please don't change 'position' */
position: absolute;
}
.ps__rail-x:hover > .ps__thumb-x,
.ps__rail-x:focus > .ps__thumb-x,
.ps__rail-x.ps--clicking .ps__thumb-x {
background-color: #999;
height: 11px;
}
.ps__rail-y:hover > .ps__thumb-y,
.ps__rail-y:focus > .ps__thumb-y,
.ps__rail-y.ps--clicking .ps__thumb-y {
background-color: #999;
width: 11px;
}
/* MS supports */
@supports (-ms-overflow-style: none) {
.ps {
overflow: auto !important;
}
}
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
.ps {
overflow: auto !important;
}
}
.ps {
position: relative;
}
.ps__rail-x {
height: 0.25rem;
}
.ps__rail-y {
width: 0.25rem;
}
.ps__rail-x,
.ps__rail-y,
.ps__thumb-x,
.ps__thumb-y {
border-radius: 10rem;
}
.ps__rail-x:hover,
.ps__rail-x:focus,
.ps__rail-x.ps--clicking,
.ps__rail-x:hover > .ps__thumb-x,
.ps__rail-x:focus > .ps__thumb-x,
.ps__rail-x.ps--clicking > .ps__thumb-x {
height: 0.375rem;
}
.ps__rail-y:hover,
.ps__rail-y:focus,
.ps__rail-y.ps--clicking,
.ps__rail-y:hover > .ps__thumb-y,
.ps__rail-y:focus > .ps__thumb-y,
.ps__rail-y.ps--clicking > .ps__thumb-y {
width: 0.375rem;
}
.ps__thumb-x {
height: 0.25rem;
bottom: 0;
}
.ps__thumb-y {
width: 0.25rem;
right: 0;
}
.light-style .ps__thumb-x,
.light-style .ps__thumb-y {
background-color: rgba(67, 89, 113, 0.4);
}
.light-style .ps__rail-x:hover,
.light-style .ps__rail-y:hover,
.light-style .ps__rail-x:focus,
.light-style .ps__rail-y:focus,
.light-style .ps__rail-x.ps--clicking,
.light-style .ps__rail-y.ps--clicking {
background-color: rgba(67, 89, 113, 0.2);
}
.light-style .ps__rail-x:hover > .ps__thumb-x,
.light-style .ps__rail-y:hover > .ps__thumb-y,
.light-style .ps__rail-x:focus > .ps__thumb-x,
.light-style .ps__rail-y:focus > .ps__thumb-y,
.light-style .ps__rail-x.ps--clicking > .ps__thumb-x,
.light-style .ps__rail-y.ps--clicking > .ps__thumb-y {
background-color: rgba(67, 89, 113, 0.7);
}
.light-style .ps-inverted .ps__rail-x:hover,
.light-style .ps-inverted .ps__rail-y:hover,
.light-style .ps-inverted .ps__rail-x:focus,
.light-style .ps-inverted .ps__rail-y:focus,
.light-style .ps-inverted .ps__rail-x.ps--clicking,
.light-style .ps-inverted .ps__rail-y.ps--clicking {
background-color: rgba(255, 255, 255, 0.5);
}
.light-style .ps-inverted .ps__thumb-x,
.light-style .ps-inverted .ps__thumb-y {
background-color: rgba(255, 255, 255, 0.7);
}
.light-style .ps-inverted .ps__rail-x:hover > .ps__thumb-x,
.light-style .ps-inverted .ps__rail-y:hover > .ps__thumb-y,
.light-style .ps-inverted .ps__rail-x:focus > .ps__thumb-x,
.light-style .ps-inverted .ps__rail-y:focus > .ps__thumb-y,
.light-style .ps-inverted .ps__rail-x.ps--clicking > .ps__thumb-x,
.light-style .ps-inverted .ps__rail-y.ps--clicking > .ps__thumb-y {
background-color: #fff;
}
@supports (-moz-appearance: none) {
#both-scrollbars-example {
max-width: 1080px;
margin: 0 auto;
padding-left: 0;
padding-right: 0;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -214,7 +214,7 @@ pub fn is_reserved(reservations: &Vec<Reservation>, time: &NaiveTime, property:
} }
#[server] #[server]
pub async fn create_reservation(reservation: CrReservation) -> Result<ApiResponse<NaiveDate>, ServerFnError> { pub async fn create_reservation(reservation: CrReservation, pow: String) -> Result<ApiResponse<NaiveDate>, ServerFnError> {
use crate::backend::get_pool; use crate::backend::get_pool;
use crate::backend::customer::find_customer_by_email; use crate::backend::customer::find_customer_by_email;
use crate::backend::customer::sync_customer_data; use crate::backend::customer::sync_customer_data;
@ -226,6 +226,9 @@ pub async fn create_reservation(reservation: CrReservation) -> Result<ApiRespons
use chrono::Local; use chrono::Local;
use sqlx::query; use sqlx::query;
use rust_decimal::Decimal; use rust_decimal::Decimal;
use leptos_captcha::spow::pow::Pow;
Pow::validate(&pow)?;
let slots = reservation.slots().iter().fold(HashMap::new(), |mut map, s| { let slots = reservation.slots().iter().fold(HashMap::new(), |mut map, s| {
let slot_str = s.split("|").collect::<Vec<_>>(); let slot_str = s.split("|").collect::<Vec<_>>();

@ -307,3 +307,10 @@ pub async fn delete_user(id: i32) -> Result<ApiResponse<()>, ServerFnError> {
Ok(ApiResponse::Data(())) Ok(ApiResponse::Data(()))
} }
#[server]
pub async fn get_pow() -> Result<String, ServerFnError> {
use leptos_captcha::spow::pow::Pow;
Ok(Pow::new(10)?.to_string())
}

@ -1,3 +1,4 @@
use leptos_captcha::spow::pow::Pow;
use log::error; use log::error;
use rezervator::backend::company::check_company; use rezervator::backend::company::check_company;
use rezervator::backend::mail::check_messages; use rezervator::backend::mail::check_messages;
@ -37,6 +38,8 @@ async fn main() -> std::io::Result<()> {
return Ok(()); return Ok(());
} }
Pow::init_random().expect("Cannot init captcha");
let cfg_path = matches.opt_str("c").unwrap_or("config.toml".to_string()); let cfg_path = matches.opt_str("c").unwrap_or("config.toml".to_string());
env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init(); env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init();

@ -1,9 +1,11 @@
use chrono::{Duration, Local, NaiveDate, NaiveTime, Timelike}; use chrono::{Duration, Local, NaiveDate, NaiveTime, Timelike};
use leptos::*; use leptos::*;
use leptos_captcha::{Captcha, pow_dispatch};
use leptos_router::*; use leptos_router::*;
use rust_decimal::Decimal; use rust_decimal::Decimal;
use crate::backend::data::{ApiResponse, DayHour, Reservation, ResProperty, SlotType, TmCheck}; use crate::backend::data::{ApiResponse, DayHour, Reservation, ResProperty, SlotType, TmCheck};
use crate::backend::reservation::{CreateReservation, get_public_form_data, is_reserved}; use crate::backend::reservation::{CreateReservation, get_public_form_data, is_reserved};
use crate::backend::user::get_pow;
use crate::components::data_form::ForValidation; use crate::components::data_form::ForValidation;
use crate::components::modal_box::DialogOpener; use crate::components::modal_box::DialogOpener;
use crate::locales::trl; use crate::locales::trl;
@ -97,6 +99,7 @@ pub fn Public() -> impl IntoView {
let invalid_dlg = DialogOpener::new(); let invalid_dlg = DialogOpener::new();
let result_dlg = DialogOpener::new(); let result_dlg = DialogOpener::new();
let result = cr_reservation.value(); let result = cr_reservation.value();
let is_pending = create_rw_signal(None);
create_effect(move |_| { create_effect(move |_| {
day.set(Local::now().date_naive()); day.set(Local::now().date_naive());
@ -104,20 +107,31 @@ pub fn Public() -> impl IntoView {
view! { view! {
<ResError opener=invalid_dlg validator=validator/> <ResError opener=invalid_dlg validator=validator/>
<ResSaved opener=result_dlg save_result=result day=day price=price.write_only() slots=slots.write_only()/> <ResSaved opener=result_dlg
save_result=result
day=day
price=price.write_only()
slots=slots.write_only()
captcha_state=is_pending.read_only()/>
<div class="card-body"> <div class="card-body">
<ActionForm <ActionForm
on:submit=move |ev| { on:submit=move |ev| {
let act = CreateReservation::from_event(&ev); cr_reservation.set_pending(true);
if !act.is_err() { if let Ok(mut act) = CreateReservation::from_event(&ev) {
validator.check(act.unwrap().entity(), &ev); validator.check(act.entity(), &ev);
}
if !validator.is_valid() { if !validator.is_valid() {
invalid_dlg.show(); invalid_dlg.show();
} else { } else {
ev.prevent_default();
pow_dispatch(get_pow, is_pending, move |pow| {
act.pow = pow.unwrap();
cr_reservation.dispatch(act);
});
result_dlg.show(); result_dlg.show();
} }
} }
}
action=cr_reservation> action=cr_reservation>
<div class="row mb-5"> <div class="row mb-5">
<div class="col-md"> <div class="col-md">
@ -246,6 +260,7 @@ pub fn Public() -> impl IntoView {
/> />
</div> </div>
</div> </div>
<Captcha is_pending />
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">
{trl("Book")} {trl("Book")}

@ -32,7 +32,8 @@ pub fn res_saved(
save_result: RwSignal<Option<Result<ApiResponse<NaiveDate>, ServerFnError>>>, save_result: RwSignal<Option<Result<ApiResponse<NaiveDate>, ServerFnError>>>,
day: RwSignal<NaiveDate>, day: RwSignal<NaiveDate>,
price: WriteSignal<Decimal>, price: WriteSignal<Decimal>,
slots: WriteSignal<Vec<String>>) -> impl IntoView { slots: WriteSignal<Vec<String>>,
captcha_state: ReadSignal<Option<bool>>) -> impl IntoView {
view! {{move ||{ view! {{move ||{
if let Some(r) = save_result.get() { if let Some(r) = save_result.get() {
match r { match r {
@ -40,7 +41,9 @@ pub fn res_saved(
match ar { match ar {
ApiResponse::Data(d) => { ApiResponse::Data(d) => {
view! { view! {
<div> <div style={ move || if let Some(c) = captcha_state.get() {
if c {"display: none;"} else {"display: block;"}
} else {"display: block;"}}>
<ModalDialog opener=opener title="Reservation saved"> <ModalDialog opener=opener title="Reservation saved">
<ModalBody> <ModalBody>
<p> <p>

@ -116,3 +116,106 @@ div.content {
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
} }
.leptos-captcha {
margin: .5rem 0;
border-left: 5px solid hsl(0, 60%, 44%);
line-height: 1;
padding-left: .25rem;
transition: border-left 250ms;
color: #3f3f3f;
}
/* The outer captcha container in pending state */
.leptos-captcha[data-state="pending"] {
border-left: 5px solid hsl(57, 60%, 44%);
}
/* The outer captcha container in verified state */
.leptos-captcha[data-state="verified"] {
border-left: 5px solid hsl(113, 60%, 44%);
}
/* The label for the hidden input */
.leptos-captcha > label {
display: flex;
align-items: center;
gap: .25rem;
margin: 0;
padding: 0;
}
/* The icons in front and back */
.leptos-captcha .icon-front,
.leptos-captcha .icon-back {
width: 0.5rem;
opacity: .8;
transition: color 250ms;
}
/* The icon in the back */
.leptos-captcha .icon-back {
margin-bottom: -.25rem;
}
/* The icon in the back in verified state */
.leptos-captcha[data-state="verified"] .icon-back {
color: hsl(113, 60%, 44%);
}
/* The inner text content */
.leptos-captcha .text {
font-size: 0.5rem;
font-weight: bold;
}
/* The inner text content in pending state */
.leptos-captcha .pending {
color: hsl(57, 60%, 44%);
}
/* The inner text content in verified state */
.leptos-captcha .verified {
color: hsl(113, 60%, 44%);
}
.spinner {
display: inline-block;
position: relative;
width: 1.5rem;
height: 1.5rem;
}
.spinner div {
box-sizing: border-box;
display: block;
position: absolute;
width: 1.25rem;
height: 1.25rem;
margin: .125rem;
border: .125rem solid #3f3f3f;
border-radius: 50%;
animation: spin 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: #3f3f3f transparent transparent transparent;
}
.spinner div:nth-child(1) {
animation-delay: -0.45s;
}
.spinner div:nth-child(2) {
animation-delay: -0.3s;
}
.spinner div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
Loading…
Cancel
Save