Compare commits
238 Commits
Digital-ID
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2dd48be65 | ||
|
|
bd8268d848 | ||
|
|
4ca691608a | ||
|
|
5939bb4e13 | ||
|
|
68e665804c | ||
|
|
2efc3acd94 | ||
|
|
428153a9d9 | ||
|
|
78f48b240b | ||
|
|
8bf83aaa5b | ||
|
|
e0753a0779 | ||
|
|
9c331789b3 | ||
|
|
3e8a0e2233 | ||
|
|
a8bb75b2f5 | ||
|
|
8e4b6c832e | ||
|
|
0927c0dca5 | ||
|
|
a555d18417 | ||
|
|
ea4d8a4049 | ||
|
|
1448d3e7da | ||
|
|
e07353cd87 | ||
|
|
2210b4ab91 | ||
|
|
8e5c0a3edc | ||
|
|
4f73075cf9 | ||
|
|
b68ef748ca | ||
|
|
200f4f58a7 | ||
|
|
86a6bf013d | ||
|
|
6afa08e5f5 | ||
|
|
3173ec9342 | ||
|
|
c8352e6537 | ||
|
|
9fbc6423d1 | ||
|
|
e4f691ab78 | ||
|
|
446276fadf | ||
|
|
1b574ac347 | ||
|
|
0c7db9cd30 | ||
|
|
fc977cc43a | ||
|
|
0a0151d1dd | ||
|
|
369f0e375c | ||
|
|
79ca03823c | ||
|
|
49f3ee17a0 | ||
|
|
841e82e233 | ||
|
|
2212ea7433 | ||
|
|
f257066d5a | ||
|
|
027bf0d405 | ||
|
|
663cfacf63 | ||
|
|
27ee899402 | ||
|
|
59dd4c9554 | ||
|
|
c659e17344 | ||
|
|
49d86ed74b | ||
|
|
b2d5df9533 | ||
|
|
0eea3d1ea3 | ||
|
|
9686e5245f | ||
|
|
1f306b2dce | ||
|
|
a86dd13e0e | ||
|
|
0e5d7477fc | ||
|
|
f395661480 | ||
|
|
4c334648cb | ||
|
|
344b2c7862 | ||
|
|
826f340e9a | ||
|
|
b4bccf2cba | ||
|
|
7bb1832d4d | ||
|
|
17ac1b04eb | ||
|
|
e4dc1e961e | ||
|
|
fa198054dd | ||
|
|
f31c895b8d | ||
|
|
b7be78cf76 | ||
|
|
6d92fde547 | ||
|
|
03bc841894 | ||
|
|
c24a1fa09d | ||
|
|
57e5dee442 | ||
|
|
188e79caf9 | ||
|
|
e9b21c5c02 | ||
|
|
75464d81f0 | ||
|
|
fcb7d40ca5 | ||
|
|
f27edb97bb | ||
|
|
b75a2349c2 | ||
|
|
37c9feffe1 | ||
|
|
fe6bcf8d0f | ||
|
|
d7488f8ca9 | ||
|
|
ffa92ded10 | ||
|
|
68e94201e0 | ||
|
|
020e45ddce | ||
|
|
8955d846e4 | ||
|
|
d5bc90c818 | ||
|
|
16a2ffca2d | ||
|
|
f932c0bc33 | ||
|
|
da268d7ecc | ||
|
|
736e8fc16b | ||
|
|
774955f131 | ||
|
|
5b20d71a0c | ||
|
|
9714b0add8 | ||
|
|
52eb2a75c1 | ||
|
|
3c77264fae | ||
|
|
429f36b6a6 | ||
|
|
11be69a79b | ||
|
|
911c13cb87 | ||
|
|
1e9830c8d4 | ||
|
|
a8a6355daa | ||
|
|
444c94690b | ||
|
|
67133c5414 | ||
|
|
f58a94bf24 | ||
|
|
9820692cd9 | ||
|
|
2eeb39abc9 | ||
|
|
5eb4ee16c0 | ||
|
|
bc364d1b85 | ||
|
|
8489bc375c | ||
|
|
b9f795c0e4 | ||
|
|
47dfcf347c | ||
|
|
e837f7cc89 | ||
|
|
7ad29e8fe0 | ||
|
|
0fb5764a70 | ||
|
|
5ebc965a2a | ||
|
|
138047fa10 | ||
|
|
a271da7bf9 | ||
|
|
79784cbcee | ||
|
|
596c44aac3 | ||
|
|
5d17137815 | ||
|
|
18bf373508 | ||
|
|
944c9989e9 | ||
|
|
202989f374 | ||
|
|
f21d10f830 | ||
|
|
c8604a56ac | ||
|
|
2d0ae35cc8 | ||
|
|
76f942b90f | ||
|
|
f0e0d9479d | ||
|
|
3403289e0a | ||
|
|
25fddec6e8 | ||
|
|
1d0cb45024 | ||
|
|
222850dfd9 | ||
|
|
8f225752cf | ||
|
|
038657751a | ||
|
|
29d7d6fdb8 | ||
|
|
e44dfee284 | ||
|
|
0f455f126d | ||
|
|
0d4f27660a | ||
|
|
9703371f28 | ||
|
|
16ca8a1df2 | ||
|
|
e883b190a7 | ||
|
|
85897a8efe | ||
|
|
5f85d1696a | ||
|
|
1c539e5255 | ||
|
|
bcfc018560 | ||
|
|
e175ca0295 | ||
|
|
bac246eac2 | ||
|
|
f65c99e86a | ||
|
|
63439b2da1 | ||
|
|
1d9368d200 | ||
|
|
3a4ed9daf9 | ||
|
|
54ccd3ed9f | ||
|
|
fe1105e9d6 | ||
|
|
95cc2ac1e0 | ||
|
|
b4516ef18e | ||
|
|
d617de75b5 | ||
|
|
83e5e39d3c | ||
|
|
f89d59e7df | ||
|
|
c944644db0 | ||
|
|
bfdddee46a | ||
|
|
0da68ddd63 | ||
|
|
f209ee5f5f | ||
|
|
9bef3d9311 | ||
|
|
632b98a81a | ||
|
|
eeaabeebf3 | ||
|
|
81f5ec2741 | ||
|
|
3fcda54070 | ||
|
|
49aff14a80 | ||
|
|
da04efbf80 | ||
|
|
541c5988e9 | ||
|
|
cfa5af78c0 | ||
|
|
fc4d9fb897 | ||
|
|
ed5565eb17 | ||
|
|
99512491fa | ||
|
|
24a66478d6 | ||
|
|
9484b0bda9 | ||
|
|
885963be45 | ||
|
|
e144e50a40 | ||
|
|
16f9a52a41 | ||
|
|
5e74f2bc0b | ||
|
|
fa5ddbf64a | ||
|
|
c45b7c5e4d | ||
|
|
606223216f | ||
|
|
c41bed05fd | ||
|
|
b71dccbd0e | ||
|
|
264a908352 | ||
|
|
36eb756be0 | ||
|
|
f74293b1f4 | ||
|
|
095c5e236e | ||
|
|
f3dcbdab49 | ||
|
|
3236f2ae94 | ||
|
|
0d127aa5fa | ||
|
|
ac18be77a7 | ||
|
|
568543bba5 | ||
|
|
3b62d188c1 | ||
|
|
de11c6be0f | ||
|
|
5b1ff31e01 | ||
|
|
03bb781b5a | ||
|
|
981d62b849 | ||
|
|
883f173c12 | ||
|
|
709f668697 | ||
|
|
3e5ef4f128 | ||
|
|
d86c118b21 | ||
|
|
4179cd703d | ||
|
|
20221b497b | ||
|
|
9bdbd396e8 | ||
|
|
c7d797b2af | ||
|
|
bc71ea6cb9 | ||
|
|
8dd57625fd | ||
|
|
ec700318ae | ||
|
|
bcd7f89dee | ||
|
|
0de76771ac | ||
|
|
3cd337d589 | ||
|
|
3f3a56c305 | ||
|
|
b40b59af3f | ||
|
|
b98c4db02d | ||
|
|
a5a046f911 | ||
|
|
d31a2f09d5 | ||
|
|
5fca3bdc4e | ||
|
|
e6142b1402 | ||
|
|
df6419bfff | ||
|
|
99d235f5e7 | ||
|
|
180961ec20 | ||
|
|
c4c7b10b99 | ||
|
|
79bed4e69d | ||
|
|
56e2691af5 | ||
|
|
13136d2712 | ||
|
|
5f1e8c55ff | ||
|
|
b4ad092228 | ||
|
|
13839cf67c | ||
|
|
fbafb94b2b | ||
|
|
60194da89b | ||
|
|
6580c211ff | ||
|
|
c44516a2f7 | ||
|
|
1ab53990a1 | ||
|
|
7ff6d08fc8 | ||
|
|
4bdaa28fff | ||
|
|
59d96222d6 | ||
|
|
3730bbee86 | ||
|
|
fa3ee41fbd | ||
|
|
b85163cda8 | ||
|
|
234c68ec53 | ||
|
|
6d47aa7411 |
64
.github/workflows/docker-image.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
name: Build and deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/**'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
logLevel:
|
||||||
|
description: 'Log level'
|
||||||
|
required: true
|
||||||
|
default: 'warning'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- info
|
||||||
|
- warning
|
||||||
|
- debug
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: Get branch data
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: kandrusyak/blockchain-ui:master
|
||||||
|
deploy:
|
||||||
|
needs: build
|
||||||
|
name: Deploy to host
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: Get branch data
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Deploying changes
|
||||||
|
uses: appleboy/ssh-action@master
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SSH_HOST }}
|
||||||
|
username: ${{ secrets.SSH_USERNAME }}
|
||||||
|
password: ${{ secrets.SSH_PASSWORD }}
|
||||||
|
script: |
|
||||||
|
docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
./update.sh
|
||||||
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
/node_modules
|
||||||
|
.idea
|
||||||
19
.prettierrc
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"arrowParens": "always",
|
||||||
|
"useTabs": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": true,
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"jsxBracketSameLine": false,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"printWidth": 100,
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.json",
|
||||||
|
"options": {
|
||||||
|
"singleQuote": false,
|
||||||
|
"parser": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
FROM node:16-alpine AS builder
|
||||||
|
|
||||||
|
RUN apk --update add --no-cache curl git python3 alpine-sdk bash autoconf libtool automake
|
||||||
|
|
||||||
|
WORKDIR /front
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
RUN npm run build:prod
|
||||||
|
|
||||||
|
FROM nginx:1.21.6-alpine as runner
|
||||||
|
|
||||||
|
COPY ./deploy/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY --from=builder /front/build /var/www/html
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
26
README.md
@@ -1,26 +0,0 @@
|
|||||||
# Mosaic React
|
|
||||||
|
|
||||||
React landing page template designed by Cruip.
|
|
||||||
|
|
||||||
## Project setup
|
|
||||||
```
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compiles and hot-reloads for development
|
|
||||||
```
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compiles and minifies for production
|
|
||||||
```
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Customize configuration
|
|
||||||
See [Configuration Reference](https://vitejs.dev/guide/).
|
|
||||||
|
|
||||||
## Support notes
|
|
||||||
We are shipping our templates with a very basic React configuration to let you quickly get into the development process, but we don't discourage you from using any other configuration or framework built on the top of React. So, please note that any request dealing with React (e.g. extra features, customisations, et cetera) is to be considered out of the support scope.
|
|
||||||
|
|
||||||
For more information about what support covers, please see our (FAQs)[https://cruip.com/faq/].
|
|
||||||
33
deploy/nginx.conf
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
worker_processes 1;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
pid /var/run/nginx.pid;
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
http {
|
||||||
|
include mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
|
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
gzip_static on;
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name localhost;
|
||||||
|
charset utf-8;
|
||||||
|
location / {
|
||||||
|
root /var/www/html;
|
||||||
|
try_files $uri /index.html;
|
||||||
|
etag on;
|
||||||
|
expires max;
|
||||||
|
add_header Cache-Control public;
|
||||||
|
location = /index.html {
|
||||||
|
expires 0;
|
||||||
|
add_header Cache-Control "no-cache, public, must-revalidate, proxy-revalidate";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
dist/assets/00aab12feba6fab9c8e5cb94b7bab85c.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
dist/assets/0d7b13bc31f623e46ce6d318d72b8fa0.jpg
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
dist/assets/337193482a6fc165c3e101c4cd45864b.png
vendored
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
dist/assets/5bdc905a0d21817d9e9cf280f7464f6d.png
vendored
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
dist/assets/6e225e2cab53b1738e4fe13130e1ad4b.jpg
vendored
Normal file
|
After Width: | Height: | Size: 916 B |
BIN
dist/assets/9668c24210a4bf2fcbd7bd5570e95795.jpg
vendored
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
dist/assets/9c6b3ebd646e88131f55cd0e4de66a67.png
vendored
Normal file
|
After Width: | Height: | Size: 912 B |
BIN
dist/assets/a7c6e3bab1dca3610a3f01816c09b150.png
vendored
Normal file
|
After Width: | Height: | Size: 623 B |
4
dist/assets/adcfdc4f4455c26d9153b599b1475b09.svg
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle fill="#0EA5E9" cx="18" cy="18" r="18" />
|
||||||
|
<path d="M18 26a8 8 0 1 1 8-8 8.009 8.009 0 0 1-8 8Zm0-14a6 6 0 1 0 0 12 6 6 0 0 0 0-12Z" fill="#F0F9FF" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 258 B |
BIN
dist/assets/d33927970705d24be4be69220dbc3ad0.jpg
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
4
dist/assets/d53e2b56cd0cdf928c678711f37177ab.svg
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle fill="#6366F1" cx="18" cy="18" r="18" />
|
||||||
|
<path d="M24.446 19.335a2.5 2.5 0 00-3.522 3.194c-.845.63-1.87.97-2.924.971a4.979 4.979 0 01-1.113-.135 4.436 4.436 0 01-1.343 1.682 6.91 6.91 0 006.9-1.165 2.5 2.5 0 002-4.547h.002zM20.431 11.938a2.5 2.5 0 10-.4 2.014 5.027 5.027 0 012.723 3.078c.148-.018.297-.028.446-.03a4.5 4.5 0 011.7.334 7.023 7.023 0 00-4.469-5.396zM14.969 20.25a2.49 2.49 0 00-1.932-1.234A4.624 4.624 0 0113 18.5a4.97 4.97 0 011.348-3.391 4.456 4.456 0 01-.788-2.016A6.989 6.989 0 0011 18.5c.003.391.04.781.11 1.166a2.5 2.5 0 103.859.584z" fill="#EEF2FF" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 683 B |
10
dist/assets/e9b097e399651f55a5ea32dac11e8b48.svg
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<circle cx="18" cy="18" r="18" fill="#E1E1F7"/>
|
||||||
|
<rect x="6" y="6" width="24" height="24" fill="url(#pattern0)"/>
|
||||||
|
<defs>
|
||||||
|
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||||
|
<use xlink:href="#image0_21_1525" transform="scale(0.0125)"/>
|
||||||
|
</pattern>
|
||||||
|
<image id="image0_21_1525" width="80" height="80" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAUKADAAQAAAABAAAAUAAAAAAx4ExPAAAHHUlEQVR4Ae1bWYwVRRRlwBGXaBRxRQaMGvDDiAG/lCigjHEDE/VHPox8uGCi4o8mij/G6Iea6JcJ0TCSEDVKYhC3EOPglhgMGoMxKswQBRWXuG8snjNTd7xTr+u9qq7q9/pp3eRQ1bfvPffW7br9+r0eJkzIkiuQK5ArkCuQK5ArkCuQK5ArkCuQK5ArkCvw/6pAT5uWOw1xLgQWAWcCUw3+xrgL2ApsAJ4B/gJccixOLAb6gdnACQB1oTxw6Q45G2k+DewFDnhgGDaXA7acAcU6YB8Qw2Pz1vZ4MjJ7TC32d8yfB24B5gLckbQ5AmBxbgC2ACzOfuA2gNILPAroC8CdeitwDjAdeAt4FyDPjYDmoV3XyYnIWBbBwj0AsNVaCW8nKwEWkFgGvAGwqH8C24BBwJbNUGg9eW4HyMEd2w90jXBHvQ9w0Z8Ac4BQuQcOuk3/wPF5oSSwF54fMX8N4MU5Eqi1vIDsuHjuliklM50EvyFAF/ErHJ8LiLBtufNs0XofHtu/o8eXIDoXvQfoS5DJ4eBYAGwCyPsLMBug2G07qi3Wc9dtBHYDNo/4dXzkfecjk+CKxNlwJw0Y7rcjuMmzNgFPRApu13kmsSGMB7nNSp/hbmQbcwfpViahblseixTpuRv5wVbEI35B48Qga7fxEnNqPUY+cqSWX0G42pAutcj5acuC2FKk/wlG7xlDm8f2b+uxPG7wW0JVsgjELFRMGzO3VDzkSiafgomLOy0ZYyPRSSbGFxiL2pMePnrNQ58oSdXC8qDM+1RV8oMh5uNRUXvytI/+e8Vjpp0f5MZ8aIWpHAZu7nLeD2PkYDin4BnJIdUObMdVlQdz2YlSRJ+2FVuOfCak2Dyj2sB/UxVQkjkmMH6IuXDLxRJfn7YVW47ylGDzaJu2zzcjItviggojy6fn65Exzoc/c43lGUkj1Q78zixKdok5TDoIN3dOaNtq+2kmqyQ7MFUBd5qkTk5asvFk080hY4W2rbaXHCXn8VECj1J97Ro2cWcExg8xF+4hOPH3viKZX6SETuvlQgw5bIPUqXbgDhN1ZlD0MOM+Yy4XS7x1e4qOo0svxbR5tK/3PFUBvzQRZ3pHDjeUHbjLctXtqU+59EcZI5tH+7Z9/iwiHgA+rzDydhODb+5iJBVPTA4Nvj9DwwLyfUhVQm7GYCxXe/roNU90rqlaWD7RTo/OyE0g3Izlak8fveZxR2vzmUcQj7tjVYVxyc0YD0fGSMUTmcZ4d3kfwgUKxluUPxI+GS+2qHzali7iL6PNY9H6HaZqYX5Bvx+Q78SMzl89YkVzkPs+4GWL1Kdte5WPi0eZdHb6McLzKp+SII1TDRdfk8bIDDgzp1iehhxS7UBNLEnO0sqSc+EQTqHxbVuxf9xMbB45X3qsooDvmGyuKZ3Vv47CIZxyxqdtxZZjnzmwebRNbeb8eZ9/osY3YMdFZHU8fPnMRy7Oywpz4Ev5WJ6y8Uv5DcCL95znSnmPOq03HGsMR2jbiv06i8fQ1XvgHz3uMYnfXSLVe43vNxinGn/+aDto5npopudFJDSP9q31vB/Z8S+ruICHAjLlgzJ96HtRgJ9t+iAUKXhs3rYeX2YWwYX4Cm2JSwFpQ9vXR695bP9kx1V8CuvkNuiDwPmLsA/9tC2yJ09Xi+wE30WE2rt4U/G4+Ef0Ve/ApsEDT/q0bSBlvHmVBeTD66qIFOlLDpH9mHBX2eLSi53NI/pajj3Iir/MvALsA6SNQt4/0Fb8yPESUObT2OZhTsyNOdZO+HbvWuADQBbPv2FZAywAQnY6bekzAJBD+LZivgzoBXzamTwLAfLILiUXc2Suqd5Igqq8TIbrCmAHIAv9DPOVgLy8wbS0kOMOgO9ahH8Y873Aq4AtzR6qt8B4JyA82zFn7lxDR+RKRNUL4w65CuCVTy3kvBpgDF0AxgsR/r54HbANEB6ugWtpm8xCpE2AJPAh5lcAPUDVwhiMxZgSfzfm3Hm2NGtznlsKaB6uiWurVHgPkjdw32J+EzCp0ojF5Ix5M8AcWEje45YDWljUQa0wc623ebg23h+TC+8TqwG56k9hfnTyKOGEU+CyFpC8nsD8kHCakf8UpHm4Vq45iTAhPkowSX4qXg/UTZYjIfnEfhNz/S6FuTZrZ+5IEc2zEcoyF0O4xkZeVRbva+CsMW39JsyNOTJX7iAtLFKrdhb7Zjxi4z32w5IJ/QbUuXiyoDkmV+a8UJQlRs2zuIT/mAtfITKZO8c09Z/cZXLmT/i6PSVz33YWHt6+nDLReWb0xFxz/skWdnU6Lbn2IilefFv4ie2jF555NkHIMQMVBQvh6IRtqrxb8tTi+2AbKsy25c6bb8VqpbfMGw9bfXvoxt3XuMp4jbNOre6B8aEzQ65ArkCuQK5ArkCuQK5ArkCuQK5ArkCuQK5ArsB/qgL/AIxZGoBhTSTCAAAAAElFTkSuQmCC"/>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
BIN
dist/assets/f2a7edc970be4aa58ba65c495ab16ed0.png
vendored
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
dist/favicon.ico
vendored
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
1
dist/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="UTF-8"><title>React App</title><link rel="icon" href="favicon.ico"><script defer="defer" src="main.3ab1a28d.js"></script></head><body><div id="root"></div><script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-M6E6PDB6Y4")</script><script async src="https://www.googletagmanager.com/gtag/js?id=G-M6E6PDB6Y4"></script></body></html>
|
||||||
8
dist/main.3ab1a28d.js
vendored
Normal file
96
dist/main.3ab1a28d.js.LICENSE.txt
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
object-assign
|
||||||
|
(c) Sindre Sorhus
|
||||||
|
@license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Copyright (c) 2018 Jed Watson.
|
||||||
|
Licensed under the MIT License (MIT), see
|
||||||
|
http://jedwatson.github.io/classnames
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The buffer module from node.js, for the browser.
|
||||||
|
*
|
||||||
|
* @author Feross Aboukhadijeh <https://feross.org>
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! https://mths.be/cssesc v3.0.0 by @mathias */
|
||||||
|
|
||||||
|
/*! https://mths.be/punycode v1.3.2 by @mathias */
|
||||||
|
|
||||||
|
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||||
|
|
||||||
|
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license qrcode.react
|
||||||
|
* Copyright (c) Paul O'Shannessy
|
||||||
|
* SPDX-License-Identifier: ISC
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React Router DOM v6.3.0
|
||||||
|
*
|
||||||
|
* Copyright (c) Remix Software Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE.md file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React Router v6.3.0
|
||||||
|
*
|
||||||
|
* Copyright (c) Remix Software Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE.md file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v0.20.2
|
||||||
|
* scheduler.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v16.13.1
|
||||||
|
* react-is.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v17.0.2
|
||||||
|
* react-dom.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v17.0.2
|
||||||
|
* react.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license URI.js v4.4.1 (c) 2011 Gary Court. License: http://github.com/garycourt/uri-js */
|
||||||
|
|
||||||
|
//! moment.js
|
||||||
|
|
||||||
|
//! moment.js locale configuration
|
||||||
3
dist/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"API_URL": "https://tn-alpha.idntty.org:8080/api/"
|
||||||
|
}
|
||||||
21
index.html
@@ -1,21 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Vite App</title>
|
|
||||||
</head>
|
|
||||||
<body class="font-inter antialiased bg-slate-100 text-slate-600">
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<script>
|
|
||||||
if (localStorage.getItem('sidebar-expanded') == 'true') {
|
|
||||||
document.querySelector('body').classList.add('sidebar-expanded');
|
|
||||||
} else {
|
|
||||||
document.querySelector('body').classList.remove('sidebar-expanded');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/main.jsx"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
14110
package-lock.json
generated
56
package.json
@@ -2,27 +2,67 @@
|
|||||||
"name": "mosaic-react",
|
"name": "mosaic-react",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "webpack-dev-server --https",
|
||||||
"build": "vite build",
|
"build:dev": "webpack --mode development --progress",
|
||||||
"preview": "vite preview"
|
"build:prod": "webpack --mode production --progress"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@liskhq/lisk-client": "^5.2.2",
|
||||||
"@tailwindcss/forms": "^0.4.0",
|
"@tailwindcss/forms": "^0.4.0",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
"chart.js": "^3.5.0",
|
"chart.js": "^3.5.0",
|
||||||
"chartjs-adapter-moment": "^1.0.0",
|
"chartjs-adapter-moment": "^1.0.0",
|
||||||
|
"classnames": "^2.3.2",
|
||||||
|
"crypto-browserify": "^3.12.0",
|
||||||
|
"mobx": "^6.6.0",
|
||||||
|
"mobx-react-lite": "^3.4.0",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
|
"net": "^1.0.2",
|
||||||
|
"os-browserify": "^0.3.0",
|
||||||
|
"path-browserify": "^1.0.1",
|
||||||
|
"prettier": "^2.8.0",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"qrcode.react": "^3.1.0",
|
||||||
|
"querystring-es3": "^0.2.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
"react-country-flag": "^3.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-flatpickr": "^3.10.7",
|
"react-flatpickr": "^3.10.7",
|
||||||
|
"react-loader-spinner": "^5.2.0",
|
||||||
"react-router-dom": "^6.2.1",
|
"react-router-dom": "^6.2.1",
|
||||||
"react-transition-group": "^4.4.1"
|
"react-transition-group": "^4.4.1",
|
||||||
|
"sodium-universal": "^3.1.0",
|
||||||
|
"stream-browserify": "^3.0.0",
|
||||||
|
"url": "^0.11.0",
|
||||||
|
"util": "^0.12.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-react": "^1.0.7",
|
"@babel/core": "^7.18.5",
|
||||||
"autoprefixer": "^10.4.2",
|
"@babel/preset-env": "^7.18.2",
|
||||||
|
"@babel/preset-react": "^7.17.12",
|
||||||
|
"autoprefixer": "^10.4.5",
|
||||||
|
"babel-loader": "^8.2.5",
|
||||||
|
"css-loader": "^6.7.1",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"html-loader": "^3.1.2",
|
||||||
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.5",
|
||||||
"sass": "^1.49.0",
|
"postcss-loader": "^7.0.0",
|
||||||
|
"sass": "^1.52.3",
|
||||||
|
"sass-loader": "^13.0.0",
|
||||||
|
"style-loader": "^3.3.1",
|
||||||
"tailwindcss": "^3.0.18",
|
"tailwindcss": "^3.0.18",
|
||||||
"vite": "^2.7.2"
|
"webpack": "^5.73.0",
|
||||||
|
"webpack-cli": "^4.10.0",
|
||||||
|
"webpack-dev-server": "^4.9.2"
|
||||||
|
},
|
||||||
|
"babel": {
|
||||||
|
"presets": [
|
||||||
|
"@babel/env",
|
||||||
|
"@babel/react"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browser": {
|
||||||
|
"fs": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import tailwind from 'tailwindcss'
|
const tailwind = require('tailwindcss')
|
||||||
import autoprefixer from 'autoprefixer'
|
const autoprefixer = require('autoprefixer')
|
||||||
import tailwindConfig from './src/css/tailwind.config.js'
|
const tailwindConfig = require('./src/tailwind.config.js')
|
||||||
|
|
||||||
export default {
|
module.exports = {
|
||||||
plugins: [tailwind(tailwindConfig), autoprefixer],
|
plugins: [tailwind(tailwindConfig), autoprefixer],
|
||||||
}
|
}
|
||||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
19
public/index.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-M6E6PDB6Y4');
|
||||||
|
</script>
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-M6E6PDB6Y4"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
public/settings.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"API_URL": "https://tn-alpha.idntty.org:8080/api/"
|
||||||
|
}
|
||||||
184
src/App.jsx
@@ -1,171 +1,63 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import {
|
import { Routes, Route, useLocation } from 'react-router-dom';
|
||||||
Routes,
|
|
||||||
Route,
|
|
||||||
useLocation
|
|
||||||
} from 'react-router-dom';
|
|
||||||
|
|
||||||
import './css/style.scss';
|
import './css/style.scss';
|
||||||
|
|
||||||
import './charts/ChartjsConfig';
|
|
||||||
|
|
||||||
// Import pages
|
// Import pages
|
||||||
import Dashboard from './pages/Dashboard';
|
|
||||||
import Analytics from './pages/Analytics';
|
|
||||||
import Fintech from './pages/Fintech';
|
|
||||||
import ProfileId from './pages/digitalId/ProfileId';
|
import ProfileId from './pages/digitalId/ProfileId';
|
||||||
import Validate from './pages/digitalId/Validate';
|
|
||||||
import ValidationLog from './pages/digitalId/ValidationLog';
|
import ValidationLog from './pages/digitalId/ValidationLog';
|
||||||
import Verify from './pages/digitalId/Verify';
|
import Verify from './pages/digitalId/Verify';
|
||||||
import Customers from './pages/ecommerce/Customers';
|
import Onboarding1 from './pages/Onboarding1';
|
||||||
import Orders from './pages/ecommerce/Orders';
|
import Onboarding2 from './pages/Onboarding2';
|
||||||
import Invoices from './pages/ecommerce/Invoices';
|
import Onboarding3 from './pages/Onboarding3';
|
||||||
import Shop from './pages/ecommerce/Shop';
|
import Onboarding4 from './pages/Onboarding4';
|
||||||
import Shop2 from './pages/ecommerce/Shop2';
|
import SharedData from './pages/SharedData';
|
||||||
import Product from './pages/ecommerce/Product';
|
import SignIn from './pages/SignIn';
|
||||||
import Cart from './pages/ecommerce/Cart';
|
import { LoadingOverlay } from './components/LoadingOverlay';
|
||||||
import Cart2 from './pages/ecommerce/Cart2';
|
import { store } from './store/store';
|
||||||
import Cart3 from './pages/ecommerce/Cart3';
|
import { observer } from 'mobx-react-lite';
|
||||||
import Pay from './pages/ecommerce/Pay';
|
import Delegates from './pages/services/Delegates';
|
||||||
import Campaigns from './pages/Campaigns';
|
import VPNServers from './pages/services/VPNServers';
|
||||||
import UsersTabs from './pages/community/UsersTabs';
|
import VPNPrepare from './pages/services/VPNPrepare';
|
||||||
import UsersTiles from './pages/community/UsersTiles';
|
import { NotificationContainer } from './components/NotificationContainer';
|
||||||
import Profile from './pages/community/Profile';
|
|
||||||
import Feed from './pages/community/Feed';
|
|
||||||
import Forum from './pages/community/Forum';
|
|
||||||
import ForumPost from './pages/community/ForumPost';
|
|
||||||
import Meetups from './pages/community/Meetups';
|
|
||||||
import MeetupsPost from './pages/community/MeetupsPost';
|
|
||||||
import CreditCards from './pages/finance/CreditCards';
|
|
||||||
import Transactions from './pages/finance/Transactions';
|
|
||||||
import TransactionDetails from './pages/finance/TransactionDetails';
|
|
||||||
import JobListing from './pages/job/JobListing';
|
|
||||||
import JobPost from './pages/job/JobPost';
|
|
||||||
import CompanyProfile from './pages/job/CompanyProfile';
|
|
||||||
import Messages from './pages/Messages';
|
|
||||||
import TasksKanban from './pages/tasks/TasksKanban';
|
|
||||||
import TasksList from './pages/tasks/TasksList';
|
|
||||||
import Inbox from './pages/Inbox';
|
|
||||||
import Calendar from './pages/Calendar';
|
|
||||||
import Account from './pages/settings/Account';
|
|
||||||
import Notifications from './pages/settings/Notifications';
|
|
||||||
import Apps from './pages/settings/Apps';
|
|
||||||
import Plans from './pages/settings/Plans';
|
|
||||||
import Billing from './pages/settings/Billing';
|
|
||||||
import Feedback from './pages/settings/Feedback';
|
|
||||||
import Changelog from './pages/utility/Changelog';
|
|
||||||
import Roadmap from './pages/utility/Roadmap';
|
|
||||||
import Faqs from './pages/utility/Faqs';
|
|
||||||
import EmptyState from './pages/utility/EmptyState';
|
|
||||||
import PageNotFound from './pages/utility/PageNotFound';
|
|
||||||
import KnowledgeBase from './pages/utility/KnowledgeBase';
|
|
||||||
import Signin from './pages/Signin';
|
|
||||||
import Signup from './pages/Signup';
|
|
||||||
import ResetPassword from './pages/ResetPassword';
|
|
||||||
import Onboarding01 from './pages/Onboarding01';
|
|
||||||
import Onboarding02 from './pages/Onboarding02';
|
|
||||||
import Onboarding03 from './pages/Onboarding03';
|
|
||||||
import Onboarding04 from './pages/Onboarding04';
|
|
||||||
import ButtonPage from './pages/component/ButtonPage';
|
|
||||||
import FormPage from './pages/component/FormPage';
|
|
||||||
import DropdownPage from './pages/component/DropdownPage';
|
|
||||||
import AlertPage from './pages/component/AlertPage';
|
|
||||||
import ModalPage from './pages/component/ModalPage';
|
|
||||||
import PaginationPage from './pages/component/PaginationPage';
|
|
||||||
import TabsPage from './pages/component/TabsPage';
|
|
||||||
import BreadcrumbPage from './pages/component/BreadcrumbPage';
|
|
||||||
import BadgePage from './pages/component/BadgePage';
|
|
||||||
import AvatarPage from './pages/component/AvatarPage';
|
|
||||||
import TooltipPage from './pages/component/TooltipPage';
|
|
||||||
import AccordionPage from './pages/component/AccordionPage';
|
|
||||||
import IconsPage from './pages/component/IconsPage';
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
|
|
||||||
|
const App = observer(() => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem('svgAvatar', '');
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.querySelector('html').style.scrollBehavior = 'auto'
|
document.querySelector('html').style.scrollBehavior = 'auto';
|
||||||
window.scroll({ top: 0 })
|
window.scroll({ top: 0 });
|
||||||
document.querySelector('html').style.scrollBehavior = ''
|
document.querySelector('html').style.scrollBehavior = '';
|
||||||
}, [location.pathname]); // triggered on route change
|
}, [location.pathname]); // triggered on route change
|
||||||
|
|
||||||
|
if (!store.nodeInfo.networkIdentifier) {
|
||||||
|
return <LoadingOverlay />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{store.loading && <LoadingOverlay />}
|
||||||
|
<NotificationContainer />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route exact path="/" element={<Dashboard />} />
|
<Route exact path="/" element={<Onboarding1 />} />
|
||||||
<Route path="/dashboard/analytics" element={<Analytics />} />
|
|
||||||
<Route path="/dashboard/fintech" element={<Fintech />} />
|
|
||||||
<Route path="/digitalId/profile-id" element={<ProfileId />} />
|
<Route path="/digitalId/profile-id" element={<ProfileId />} />
|
||||||
<Route path="/digitalId/validate" element={<Validate />} />
|
{/*<Route path="/digitalId/validate" element={<Validate />} />*/}
|
||||||
<Route path="/digitalId/validation-log" element={<ValidationLog />} />
|
<Route path="/digitalId/validation-log" element={<ValidationLog />} />
|
||||||
<Route path="/digitalId/verify" element={<Verify />} />
|
<Route path="/digitalId/verify" element={<Verify />} />
|
||||||
<Route path="/ecommerce/customers" element={<Customers />} />
|
<Route path="/onboarding-2" element={<Onboarding2 />} />
|
||||||
<Route path="/ecommerce/orders" element={<Orders />} />
|
<Route path="/onboarding-3" element={<Onboarding3 />} />
|
||||||
<Route path="/ecommerce/invoices" element={<Invoices />} />
|
<Route path="/onboarding-4" element={<Onboarding4 />} />
|
||||||
<Route path="/ecommerce/shop" element={<Shop />} />
|
<Route path="/services/delegates" element={<Delegates />} />
|
||||||
<Route path="/ecommerce/shop-2" element={<Shop2 />} />
|
<Route path="/services/vpn" element={<VPNPrepare />} />
|
||||||
<Route path="/ecommerce/product" element={<Product />} />
|
<Route path="/services/vpn/servers" element={<VPNServers />} />
|
||||||
<Route path="/ecommerce/cart" element={<Cart />} />
|
<Route path="/shared-data/:id" element={<SharedData />} />
|
||||||
<Route path="/ecommerce/cart-2" element={<Cart2 />} />
|
<Route path="/signIn" element={<SignIn />} />
|
||||||
<Route path="/ecommerce/cart-3" element={<Cart3 />} />
|
|
||||||
<Route path="/ecommerce/pay" element={<Pay />} />
|
|
||||||
<Route path="/campaigns" element={<Campaigns />} />
|
|
||||||
<Route path="/community/users-tabs" element={<UsersTabs />} />
|
|
||||||
<Route path="/community/users-tiles" element={<UsersTiles />} />
|
|
||||||
<Route path="/community/profile" element={<Profile />} />
|
|
||||||
<Route path="/community/feed" element={<Feed />} />
|
|
||||||
<Route path="/community/forum" element={<Forum />} />
|
|
||||||
<Route path="/community/forum-post" element={<ForumPost />} />
|
|
||||||
<Route path="/community/meetups" element={<Meetups />} />
|
|
||||||
<Route path="/community/meetups-post" element={<MeetupsPost />} />
|
|
||||||
<Route path="/finance/cards" element={<CreditCards />} />
|
|
||||||
<Route path="/finance/transactions" element={<Transactions />} />
|
|
||||||
<Route path="/finance/transaction-details" element={<TransactionDetails />} />
|
|
||||||
<Route path="/job/job-listing" element={<JobListing />} />
|
|
||||||
<Route path="/job/job-post" element={<JobPost />} />
|
|
||||||
<Route path="/job/company-profile" element={<CompanyProfile />} />
|
|
||||||
<Route path="/messages" element={<Messages />} />
|
|
||||||
<Route path="/tasks/kanban" element={<TasksKanban />} />
|
|
||||||
<Route path="/tasks/list" element={<TasksList />} />
|
|
||||||
<Route path="/inbox" element={<Inbox />} />
|
|
||||||
<Route path="/calendar" element={<Calendar />} />
|
|
||||||
<Route path="/settings/account" element={<Account />} />
|
|
||||||
<Route path="/settings/notifications" element={<Notifications />} />
|
|
||||||
<Route path="/settings/apps" element={<Apps />} />
|
|
||||||
<Route path="/settings/plans" element={<Plans />} />
|
|
||||||
<Route path="/settings/billing" element={<Billing />} />
|
|
||||||
<Route path="/settings/feedback" element={<Feedback />} />
|
|
||||||
<Route path="/utility/changelog" element={<Changelog />} />
|
|
||||||
<Route path="/utility/roadmap" element={<Roadmap />} />
|
|
||||||
<Route path="/utility/faqs" element={<Faqs />} />
|
|
||||||
<Route path="/utility/empty-state" element={<EmptyState />} />
|
|
||||||
<Route path="/utility/404" element={<PageNotFound />} />
|
|
||||||
<Route path="/utility/knowledge-base" element={<KnowledgeBase />} />
|
|
||||||
<Route path="/signin" element={<Signin />} />
|
|
||||||
<Route path="/signup" element={<Signup />} />
|
|
||||||
<Route path="/reset-password" element={<ResetPassword />} />
|
|
||||||
<Route path="/onboarding-01" element={<Onboarding01 />} />
|
|
||||||
<Route path="/onboarding-02" element={<Onboarding02 />} />
|
|
||||||
<Route path="/onboarding-03" element={<Onboarding03 />} />
|
|
||||||
<Route path="/onboarding-04" element={<Onboarding04 />} />
|
|
||||||
<Route path="/component/button" element={<ButtonPage />} />
|
|
||||||
<Route path="/component/form" element={<FormPage />} />
|
|
||||||
<Route path="/component/dropdown" element={<DropdownPage />} />
|
|
||||||
<Route path="/component/alert" element={<AlertPage />} />
|
|
||||||
<Route path="/component/modal" element={<ModalPage />} />
|
|
||||||
<Route path="/component/pagination" element={<PaginationPage />} />
|
|
||||||
<Route path="/component/tabs" element={<TabsPage />} />
|
|
||||||
<Route path="/component/breadcrumb" element={<BreadcrumbPage />} />
|
|
||||||
<Route path="/component/badge" element={<BadgePage />} />
|
|
||||||
<Route path="/component/avatar" element={<AvatarPage />} />
|
|
||||||
<Route path="/component/tooltip" element={<TooltipPage />} />
|
|
||||||
<Route path="/component/accordion" element={<AccordionPage />} />
|
|
||||||
<Route path="/component/icons" element={<IconsPage />} />
|
|
||||||
<Route path="*" element={<PageNotFound />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
3
src/api/node.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { fetchWrapper } from '../shared/fetchWrapper';
|
||||||
|
|
||||||
|
export const getNodeInfo = (headers) => fetchWrapper.get('node/info/', headers);
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig, formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
|
|
||||||
|
|
||||||
function BarChart01({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const legend = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
top: 12,
|
|
||||||
bottom: 16,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 5,
|
|
||||||
callback: (value) => formatValue(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
displayFormats: {
|
|
||||||
month: 'MMM YY',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatValue(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 500,
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
plugins: [{
|
|
||||||
id: 'htmlLegend',
|
|
||||||
afterUpdate(c, args, options) {
|
|
||||||
const ul = legend.current;
|
|
||||||
if (!ul) return;
|
|
||||||
// Remove old legend items
|
|
||||||
while (ul.firstChild) {
|
|
||||||
ul.firstChild.remove();
|
|
||||||
}
|
|
||||||
// Reuse the built-in legendItems generator
|
|
||||||
const items = c.options.plugins.legend.labels.generateLabels(c);
|
|
||||||
items.forEach((item) => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.style.marginRight = tailwindConfig().theme.margin[4];
|
|
||||||
// Button element
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.style.display = 'inline-flex';
|
|
||||||
button.style.alignItems = 'center';
|
|
||||||
button.style.opacity = item.hidden ? '.3' : '';
|
|
||||||
button.onclick = () => {
|
|
||||||
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex));
|
|
||||||
c.update();
|
|
||||||
};
|
|
||||||
// Color box
|
|
||||||
const box = document.createElement('span');
|
|
||||||
box.style.display = 'block';
|
|
||||||
box.style.width = tailwindConfig().theme.width[3];
|
|
||||||
box.style.height = tailwindConfig().theme.height[3];
|
|
||||||
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
|
|
||||||
box.style.marginRight = tailwindConfig().theme.margin[2];
|
|
||||||
box.style.borderWidth = '3px';
|
|
||||||
box.style.borderColor = item.fillStyle;
|
|
||||||
box.style.pointerEvents = 'none';
|
|
||||||
// Label
|
|
||||||
const labelContainer = document.createElement('span');
|
|
||||||
labelContainer.style.display = 'flex';
|
|
||||||
labelContainer.style.alignItems = 'center';
|
|
||||||
const value = document.createElement('span');
|
|
||||||
value.style.color = tailwindConfig().theme.colors.slate[800];
|
|
||||||
value.style.fontSize = tailwindConfig().theme.fontSize['3xl'][0];
|
|
||||||
value.style.lineHeight = tailwindConfig().theme.fontSize['3xl'][1].lineHeight;
|
|
||||||
value.style.fontWeight = tailwindConfig().theme.fontWeight.bold;
|
|
||||||
value.style.marginRight = tailwindConfig().theme.margin[2];
|
|
||||||
value.style.pointerEvents = 'none';
|
|
||||||
const label = document.createElement('span');
|
|
||||||
label.style.color = tailwindConfig().theme.colors.slate[500];
|
|
||||||
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
|
|
||||||
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
|
|
||||||
const theValue = c.data.datasets[item.datasetIndex].data.reduce((a, b) => a + b, 0);
|
|
||||||
const valueText = document.createTextNode(formatValue(theValue));
|
|
||||||
const labelText = document.createTextNode(item.text);
|
|
||||||
value.appendChild(valueText);
|
|
||||||
label.appendChild(labelText);
|
|
||||||
li.appendChild(button);
|
|
||||||
button.appendChild(box);
|
|
||||||
button.appendChild(labelContainer);
|
|
||||||
labelContainer.appendChild(value);
|
|
||||||
labelContainer.appendChild(label);
|
|
||||||
ul.appendChild(li);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="px-5 py-3">
|
|
||||||
<ul ref={legend} className="flex flex-wrap"></ul>
|
|
||||||
</div>
|
|
||||||
<div className="grow">
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BarChart01;
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
|
|
||||||
|
|
||||||
function BarChart02({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
top: 12,
|
|
||||||
bottom: 16,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
stacked: true,
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
beginAtZero: true,
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 5,
|
|
||||||
callback: (value) => formatValue(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
stacked: true,
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
displayFormats: {
|
|
||||||
month: 'MMM YY',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
autoSkipPadding: 48,
|
|
||||||
maxRotation: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatValue(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 200,
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BarChart02;
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig, formatThousands } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
|
|
||||||
|
|
||||||
function BarChart03({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const legend = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
top: 12,
|
|
||||||
bottom: 16,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
stacked: true,
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
beginAtZero: true,
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 5,
|
|
||||||
callback: (value) => formatThousands(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
stacked: true,
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
displayFormats: {
|
|
||||||
month: 'MMM',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
autoSkipPadding: 48,
|
|
||||||
maxRotation: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatThousands(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 500,
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
plugins: [{
|
|
||||||
id: 'htmlLegend',
|
|
||||||
afterUpdate(c, args, options) {
|
|
||||||
const ul = legend.current
|
|
||||||
if (!ul) return
|
|
||||||
// Remove old legend items
|
|
||||||
while (ul.firstChild) {
|
|
||||||
ul.firstChild.remove()
|
|
||||||
}
|
|
||||||
// Reuse the built-in legendItems generator
|
|
||||||
const items = c.options.plugins.legend.labels.generateLabels(c)
|
|
||||||
items.forEach((item) => {
|
|
||||||
const li = document.createElement('li')
|
|
||||||
li.style.marginRight = tailwindConfig().theme.margin[3];
|
|
||||||
// Button element
|
|
||||||
const button = document.createElement('button')
|
|
||||||
button.style.display = 'inline-flex';
|
|
||||||
button.style.alignItems = 'center';
|
|
||||||
button.style.opacity = item.hidden ? '.3' : '';
|
|
||||||
button.onclick = () => {
|
|
||||||
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex))
|
|
||||||
c.update()
|
|
||||||
};
|
|
||||||
// Color box
|
|
||||||
const box = document.createElement('span')
|
|
||||||
box.style.display = 'block';
|
|
||||||
box.style.width = tailwindConfig().theme.width[3];
|
|
||||||
box.style.height = tailwindConfig().theme.height[3];
|
|
||||||
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
|
|
||||||
box.style.marginRight = tailwindConfig().theme.margin[2];
|
|
||||||
box.style.borderWidth = '3px';
|
|
||||||
box.style.borderColor = item.fillStyle;
|
|
||||||
box.style.pointerEvents = 'none';
|
|
||||||
// Label
|
|
||||||
const label = document.createElement('span')
|
|
||||||
label.style.color = tailwindConfig().theme.colors.slate[500];
|
|
||||||
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
|
|
||||||
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
|
|
||||||
const labelText = document.createTextNode(item.text)
|
|
||||||
label.appendChild(labelText)
|
|
||||||
li.appendChild(button)
|
|
||||||
button.appendChild(box)
|
|
||||||
button.appendChild(label)
|
|
||||||
ul.appendChild(li)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="px-5 py-4">
|
|
||||||
<div className="grow mb-1">
|
|
||||||
<ul ref={legend} className="flex flex-wrap"></ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="grow">
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BarChart03;
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig, formatThousands } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
|
|
||||||
|
|
||||||
function BarChart04({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const legend = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
indexAxis: 'y',
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
top: 12,
|
|
||||||
bottom: 16,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
displayFormats: {
|
|
||||||
month: 'MMM',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 3,
|
|
||||||
align: 'end',
|
|
||||||
callback: (value) => formatThousands(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatThousands(context.parsed.x),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 500,
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
plugins: [{
|
|
||||||
id: 'htmlLegend',
|
|
||||||
afterUpdate(c, args, options) {
|
|
||||||
const ul = legend.current
|
|
||||||
if (!ul) return
|
|
||||||
// Remove old legend items
|
|
||||||
while (ul.firstChild) {
|
|
||||||
ul.firstChild.remove()
|
|
||||||
}
|
|
||||||
// Reuse the built-in legendItems generator
|
|
||||||
const items = c.options.plugins.legend.labels.generateLabels(c)
|
|
||||||
items.forEach((item) => {
|
|
||||||
const li = document.createElement('li')
|
|
||||||
li.style.marginRight = tailwindConfig().theme.margin[4]
|
|
||||||
// Button element
|
|
||||||
const button = document.createElement('button')
|
|
||||||
button.style.display = 'inline-flex'
|
|
||||||
button.style.alignItems = 'center'
|
|
||||||
button.style.opacity = item.hidden ? '.3' : ''
|
|
||||||
button.onclick = () => {
|
|
||||||
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex))
|
|
||||||
c.update()
|
|
||||||
}
|
|
||||||
// Color box
|
|
||||||
const box = document.createElement('span')
|
|
||||||
box.style.display = 'block'
|
|
||||||
box.style.width = tailwindConfig().theme.width[3]
|
|
||||||
box.style.height = tailwindConfig().theme.height[3]
|
|
||||||
box.style.borderRadius = tailwindConfig().theme.borderRadius.full
|
|
||||||
box.style.marginRight = tailwindConfig().theme.margin[2]
|
|
||||||
box.style.borderWidth = '3px'
|
|
||||||
box.style.borderColor = item.fillStyle
|
|
||||||
box.style.pointerEvents = 'none'
|
|
||||||
// Label
|
|
||||||
const label = document.createElement('span')
|
|
||||||
label.style.color = tailwindConfig().theme.colors.slate[500]
|
|
||||||
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0]
|
|
||||||
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight
|
|
||||||
const labelText = document.createTextNode(item.text)
|
|
||||||
label.appendChild(labelText)
|
|
||||||
li.appendChild(button)
|
|
||||||
button.appendChild(box)
|
|
||||||
button.appendChild(label)
|
|
||||||
ul.appendChild(li)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="px-5 py-4">
|
|
||||||
<ul ref={legend} className="flex flex-wrap"></ul>
|
|
||||||
</div>
|
|
||||||
<div className="grow">
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BarChart04;
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig, formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
|
|
||||||
|
|
||||||
function BarChart05({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const legend = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
top: 12,
|
|
||||||
bottom: 16,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 5,
|
|
||||||
callback: (value) => formatValue(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
displayFormats: {
|
|
||||||
month: 'MMM YY',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatValue(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 500,
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
id: 'htmlLegend',
|
|
||||||
afterUpdate(c, args, options) {
|
|
||||||
const ul = legend.current;
|
|
||||||
if (!ul) return;
|
|
||||||
// Remove old legend items
|
|
||||||
while (ul.firstChild) {
|
|
||||||
ul.firstChild.remove();
|
|
||||||
}
|
|
||||||
// Reuse the built-in legendItems generator
|
|
||||||
const items = c.options.plugins.legend.labels.generateLabels(c);
|
|
||||||
items.forEach((item) => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.style.marginRight = tailwindConfig().theme.margin[4];
|
|
||||||
// Button element
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.style.display = 'inline-flex';
|
|
||||||
button.style.alignItems = 'center';
|
|
||||||
button.style.opacity = item.hidden ? '.3' : '';
|
|
||||||
button.onclick = () => {
|
|
||||||
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex));
|
|
||||||
c.update();
|
|
||||||
};
|
|
||||||
// Color box
|
|
||||||
const box = document.createElement('span');
|
|
||||||
box.style.display = 'block';
|
|
||||||
box.style.width = tailwindConfig().theme.width[3];
|
|
||||||
box.style.height = tailwindConfig().theme.height[3];
|
|
||||||
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
|
|
||||||
box.style.marginRight = tailwindConfig().theme.margin[2];
|
|
||||||
box.style.borderWidth = '3px';
|
|
||||||
box.style.borderColor = item.fillStyle;
|
|
||||||
box.style.pointerEvents = 'none';
|
|
||||||
// Label
|
|
||||||
const label = document.createElement('span');
|
|
||||||
label.style.color = tailwindConfig().theme.colors.slate[500];
|
|
||||||
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
|
|
||||||
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
|
|
||||||
const labelText = document.createTextNode(item.text);
|
|
||||||
label.appendChild(labelText);
|
|
||||||
li.appendChild(button);
|
|
||||||
button.appendChild(box);
|
|
||||||
button.appendChild(label);
|
|
||||||
ul.appendChild(li);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="px-5 py-3">
|
|
||||||
<div className="flex flex-wrap justify-between items-center">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="text-3xl font-bold text-slate-800 mr-2">$1,347.09</div>
|
|
||||||
<div className="text-sm">Net</div>
|
|
||||||
</div>
|
|
||||||
<div className="grow ml-2">
|
|
||||||
<ul ref={legend} className="flex flex-wrap justify-end"></ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="grow">
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BarChart05;
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, BarController, BarElement, LinearScale, CategoryScale, Tooltip, Legend,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig, formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
// Import images
|
|
||||||
import revolutIcon from '../images/company-icon-01.svg';
|
|
||||||
import hsbcIcon from '../images/company-icon-02.svg';
|
|
||||||
import qontoIcon from '../images/company-icon-03.svg';
|
|
||||||
import n26Icon from '../images/company-icon-04.svg';
|
|
||||||
|
|
||||||
Chart.register(BarController, BarElement, LinearScale, CategoryScale, Tooltip, Legend);
|
|
||||||
const images = [revolutIcon, hsbcIcon, qontoIcon, n26Icon];
|
|
||||||
|
|
||||||
function BarChart06({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const legend = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
indexAxis: 'y',
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
top: 12,
|
|
||||||
bottom: 16,
|
|
||||||
left: 72,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
drawTicks: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 3,
|
|
||||||
align: 'end',
|
|
||||||
callback: (value) => formatValue(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatValue(context.parsed.x),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 500,
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
id: 'htmlLegend',
|
|
||||||
afterUpdate(c, args, options) {
|
|
||||||
const ul = legend.current;
|
|
||||||
if (!ul) return;
|
|
||||||
// Remove old legend items
|
|
||||||
while (ul.firstChild) {
|
|
||||||
ul.firstChild.remove();
|
|
||||||
}
|
|
||||||
// Reuse the built-in legendItems generator
|
|
||||||
const items = c.options.plugins.legend.labels.generateLabels(c);
|
|
||||||
items.forEach((item) => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.style.marginRight = tailwindConfig().theme.margin[4];
|
|
||||||
// Button element
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.style.display = 'inline-flex';
|
|
||||||
button.style.alignItems = 'center';
|
|
||||||
button.style.opacity = item.hidden ? '.3' : '';
|
|
||||||
button.onclick = () => {
|
|
||||||
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex));
|
|
||||||
c.update();
|
|
||||||
};
|
|
||||||
// Color box
|
|
||||||
const box = document.createElement('span');
|
|
||||||
box.style.display = 'block';
|
|
||||||
box.style.width = tailwindConfig().theme.width[3];
|
|
||||||
box.style.height = tailwindConfig().theme.height[3];
|
|
||||||
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
|
|
||||||
box.style.marginRight = tailwindConfig().theme.margin[2];
|
|
||||||
box.style.borderWidth = '3px';
|
|
||||||
box.style.borderColor = item.fillStyle;
|
|
||||||
box.style.pointerEvents = 'none';
|
|
||||||
// Label
|
|
||||||
const label = document.createElement('span');
|
|
||||||
label.style.color = tailwindConfig().theme.colors.slate[500];
|
|
||||||
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
|
|
||||||
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
|
|
||||||
const labelText = document.createTextNode(item.text);
|
|
||||||
label.appendChild(labelText);
|
|
||||||
li.appendChild(button);
|
|
||||||
button.appendChild(box);
|
|
||||||
button.appendChild(label);
|
|
||||||
ul.appendChild(li);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
afterDraw(c) {
|
|
||||||
const xAxis = c.scales.x;
|
|
||||||
const yAxis = c.scales.y;
|
|
||||||
yAxis.ticks.forEach((value, index) => {
|
|
||||||
const y = yAxis.getPixelForTick(index);
|
|
||||||
const image = new Image();
|
|
||||||
image.src = images[index];
|
|
||||||
c.ctx.drawImage(image, xAxis.left - 52, y - 18);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="px-5 py-4">
|
|
||||||
<ul ref={legend} className="flex flex-wrap"></ul>
|
|
||||||
</div>
|
|
||||||
<div className="grow">
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BarChart06;
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
// Import Chart.js
|
|
||||||
import { Chart, Tooltip } from 'chart.js';
|
|
||||||
// Import Tailwind config
|
|
||||||
import { tailwindConfig } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(Tooltip);
|
|
||||||
|
|
||||||
// Define Chart.js default settings
|
|
||||||
Chart.defaults.font.family = '"Inter", sans-serif';
|
|
||||||
Chart.defaults.font.weight = '500';
|
|
||||||
Chart.defaults.color = tailwindConfig().theme.colors.slate[400];
|
|
||||||
Chart.defaults.scale.grid.color = tailwindConfig().theme.colors.slate[100];
|
|
||||||
Chart.defaults.plugins.tooltip.titleColor = tailwindConfig().theme.colors.slate[800];
|
|
||||||
Chart.defaults.plugins.tooltip.bodyColor = tailwindConfig().theme.colors.slate[800];
|
|
||||||
Chart.defaults.plugins.tooltip.backgroundColor = tailwindConfig().theme.colors.white;
|
|
||||||
Chart.defaults.plugins.tooltip.borderWidth = 1;
|
|
||||||
Chart.defaults.plugins.tooltip.borderColor = tailwindConfig().theme.colors.slate[200];
|
|
||||||
Chart.defaults.plugins.tooltip.displayColors = false;
|
|
||||||
Chart.defaults.plugins.tooltip.mode = 'nearest';
|
|
||||||
Chart.defaults.plugins.tooltip.intersect = false;
|
|
||||||
Chart.defaults.plugins.tooltip.position = 'nearest';
|
|
||||||
Chart.defaults.plugins.tooltip.caretSize = 0;
|
|
||||||
Chart.defaults.plugins.tooltip.caretPadding = 20;
|
|
||||||
Chart.defaults.plugins.tooltip.cornerRadius = 4;
|
|
||||||
Chart.defaults.plugins.tooltip.padding = 8;
|
|
||||||
|
|
||||||
// Register Chart.js plugin to add a bg option for chart area
|
|
||||||
Chart.register({
|
|
||||||
id: 'chartAreaPlugin',
|
|
||||||
// eslint-disable-next-line object-shorthand
|
|
||||||
beforeDraw: (chart) => {
|
|
||||||
if (chart.config.options.chartArea && chart.config.options.chartArea.backgroundColor) {
|
|
||||||
const ctx = chart.canvas.getContext('2d');
|
|
||||||
const { chartArea } = chart;
|
|
||||||
ctx.save();
|
|
||||||
ctx.fillStyle = chart.config.options.chartArea.backgroundColor;
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
ctx.fillRect(chartArea.left, chartArea.top, chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
|
|
||||||
ctx.restore();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, DoughnutController, ArcElement, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(DoughnutController, ArcElement, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function DoughnutChart({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const legend = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'doughnut',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
cutout: '80%',
|
|
||||||
layout: {
|
|
||||||
padding: 24,
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 500,
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
plugins: [{
|
|
||||||
id: 'htmlLegend',
|
|
||||||
afterUpdate(c, args, options) {
|
|
||||||
const ul = legend.current;
|
|
||||||
if (!ul) return;
|
|
||||||
// Remove old legend items
|
|
||||||
while (ul.firstChild) {
|
|
||||||
ul.firstChild.remove();
|
|
||||||
}
|
|
||||||
// Reuse the built-in legendItems generator
|
|
||||||
const items = c.options.plugins.legend.labels.generateLabels(c);
|
|
||||||
items.forEach((item) => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.style.margin = tailwindConfig().theme.margin[1];
|
|
||||||
// Button element
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.classList.add('btn-xs');
|
|
||||||
button.style.backgroundColor = tailwindConfig().theme.colors.white;
|
|
||||||
button.style.borderWidth = tailwindConfig().theme.borderWidth[1];
|
|
||||||
button.style.borderColor = tailwindConfig().theme.colors.slate[200];
|
|
||||||
button.style.color = tailwindConfig().theme.colors.slate[500];
|
|
||||||
button.style.boxShadow = tailwindConfig().theme.boxShadow.md;
|
|
||||||
button.style.opacity = item.hidden ? '.3' : '';
|
|
||||||
button.onclick = () => {
|
|
||||||
c.toggleDataVisibility(item.index, !item.index);
|
|
||||||
c.update();
|
|
||||||
};
|
|
||||||
// Color box
|
|
||||||
const box = document.createElement('span');
|
|
||||||
box.style.display = 'block';
|
|
||||||
box.style.width = tailwindConfig().theme.width[2];
|
|
||||||
box.style.height = tailwindConfig().theme.height[2];
|
|
||||||
box.style.backgroundColor = item.fillStyle;
|
|
||||||
box.style.borderRadius = tailwindConfig().theme.borderRadius.sm;
|
|
||||||
box.style.marginRight = tailwindConfig().theme.margin[1];
|
|
||||||
box.style.pointerEvents = 'none';
|
|
||||||
// Label
|
|
||||||
const label = document.createElement('span');
|
|
||||||
label.style.display = 'flex';
|
|
||||||
label.style.alignItems = 'center';
|
|
||||||
const labelText = document.createTextNode(item.text);
|
|
||||||
label.appendChild(labelText);
|
|
||||||
li.appendChild(button);
|
|
||||||
button.appendChild(box);
|
|
||||||
button.appendChild(label);
|
|
||||||
ul.appendChild(li);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grow flex flex-col justify-center">
|
|
||||||
<div>
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
<div className="px-5 pt-2 pb-6">
|
|
||||||
<ul ref={legend} className="flex flex-wrap justify-center -m-1"></ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DoughnutChart;
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig, formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function LineChart01({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
chartArea: {
|
|
||||||
backgroundColor: tailwindConfig().theme.colors.slate[50],
|
|
||||||
},
|
|
||||||
layout: {
|
|
||||||
padding: 20,
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
display: false,
|
|
||||||
beginAtZero: true,
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
},
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatValue(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LineChart01;
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig, formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function LineChart02({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const legend = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: 20,
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
beginAtZero: true,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 5,
|
|
||||||
callback: (value) => formatValue(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
displayFormats: {
|
|
||||||
month: 'MMM YY',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
autoSkipPadding: 48,
|
|
||||||
maxRotation: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatValue(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
plugins: [{
|
|
||||||
id: 'htmlLegend',
|
|
||||||
afterUpdate(c, args, options) {
|
|
||||||
const ul = legend.current;
|
|
||||||
if (!ul) return;
|
|
||||||
// Remove old legend items
|
|
||||||
while (ul.firstChild) {
|
|
||||||
ul.firstChild.remove();
|
|
||||||
}
|
|
||||||
// Reuse the built-in legendItems generator
|
|
||||||
const items = c.options.plugins.legend.labels.generateLabels(c);
|
|
||||||
items.slice(0, 2).forEach((item) => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.style.marginLeft = tailwindConfig().theme.margin[3];
|
|
||||||
// Button element
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.style.display = 'inline-flex';
|
|
||||||
button.style.alignItems = 'center';
|
|
||||||
button.style.opacity = item.hidden ? '.3' : '';
|
|
||||||
button.onclick = () => {
|
|
||||||
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex));
|
|
||||||
c.update();
|
|
||||||
};
|
|
||||||
// Color box
|
|
||||||
const box = document.createElement('span');
|
|
||||||
box.style.display = 'block';
|
|
||||||
box.style.width = tailwindConfig().theme.width[3];
|
|
||||||
box.style.height = tailwindConfig().theme.height[3];
|
|
||||||
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
|
|
||||||
box.style.marginRight = tailwindConfig().theme.margin[2];
|
|
||||||
box.style.borderWidth = '3px';
|
|
||||||
box.style.borderColor = c.data.datasets[item.datasetIndex].borderColor;
|
|
||||||
box.style.pointerEvents = 'none';
|
|
||||||
// Label
|
|
||||||
const label = document.createElement('span');
|
|
||||||
label.style.color = tailwindConfig().theme.colors.slate[500];
|
|
||||||
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
|
|
||||||
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
|
|
||||||
const labelText = document.createTextNode(item.text);
|
|
||||||
label.appendChild(labelText);
|
|
||||||
li.appendChild(button);
|
|
||||||
button.appendChild(box);
|
|
||||||
button.appendChild(label);
|
|
||||||
ul.appendChild(li);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="px-5 py-3">
|
|
||||||
<div className="flex flex-wrap justify-between items-end">
|
|
||||||
<div className="flex items-start">
|
|
||||||
<div className="text-3xl font-bold text-slate-800 mr-2">$1,482</div>
|
|
||||||
<div className="text-sm font-semibold text-white px-1.5 bg-amber-500 rounded-full">-22%</div>
|
|
||||||
</div>
|
|
||||||
<div className="grow ml-2 mb-1">
|
|
||||||
<ul ref={legend} className="flex flex-wrap justify-end"></ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* Chart built with Chart.js 3 */}
|
|
||||||
<div className="grow">
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LineChart02;
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { formatThousands } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function LineChart03({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: 20,
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
callback: (value) => formatThousands(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
displayFormats: {
|
|
||||||
month: 'MMM YY',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
autoSkipPadding: 48,
|
|
||||||
maxRotation: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatThousands(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LineChart03;
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig, formatThousands } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function LineChart04({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
chartArea: {
|
|
||||||
backgroundColor: tailwindConfig().theme.colors.slate[50],
|
|
||||||
},
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
display: false,
|
|
||||||
beginAtZero: true,
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
},
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatThousands(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LineChart04;
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function LineChart05({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const legend = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: 20,
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 7,
|
|
||||||
callback: (value) => `${value}%`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
displayFormats: {
|
|
||||||
month: 'MMM YY',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
autoSkipPadding: 48,
|
|
||||||
maxRotation: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => `${context.parsed.y}%`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
id: 'htmlLegend',
|
|
||||||
afterUpdate(c, args, options) {
|
|
||||||
const ul = legend.current;
|
|
||||||
if (!ul) return;
|
|
||||||
// Remove old legend items
|
|
||||||
while (ul.firstChild) {
|
|
||||||
ul.firstChild.remove();
|
|
||||||
}
|
|
||||||
// Reuse the built-in legendItems generator
|
|
||||||
const items = c.options.plugins.legend.labels.generateLabels(c);
|
|
||||||
items.forEach((item) => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.style.marginLeft = tailwindConfig().theme.margin[3];
|
|
||||||
// Button element
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.style.display = 'inline-flex';
|
|
||||||
button.style.alignItems = 'center';
|
|
||||||
button.style.opacity = item.hidden ? '.3' : '';
|
|
||||||
button.onclick = () => {
|
|
||||||
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex));
|
|
||||||
c.update();
|
|
||||||
};
|
|
||||||
// Color box
|
|
||||||
const box = document.createElement('span');
|
|
||||||
box.style.display = 'block';
|
|
||||||
box.style.width = tailwindConfig().theme.width[3];
|
|
||||||
box.style.height = tailwindConfig().theme.height[3];
|
|
||||||
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
|
|
||||||
box.style.marginRight = tailwindConfig().theme.margin[2];
|
|
||||||
box.style.borderWidth = '3px';
|
|
||||||
box.style.borderColor = c.data.datasets[item.datasetIndex].borderColor;
|
|
||||||
box.style.pointerEvents = 'none';
|
|
||||||
// Label
|
|
||||||
const label = document.createElement('span');
|
|
||||||
label.style.color = tailwindConfig().theme.colors.slate[500];
|
|
||||||
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
|
|
||||||
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
|
|
||||||
const labelText = document.createTextNode(item.text);
|
|
||||||
label.appendChild(labelText);
|
|
||||||
li.appendChild(button);
|
|
||||||
button.appendChild(box);
|
|
||||||
button.appendChild(label);
|
|
||||||
ul.appendChild(li);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="px-5 py-3">
|
|
||||||
<div className="flex flex-wrap justify-between items-end">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="text-3xl font-bold text-slate-800 mr-2">244.7%</div>
|
|
||||||
<div className="text-sm">
|
|
||||||
<span className="font-medium text-slate-800">17.4%</span> AVG
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="grow ml-2 mb-1">
|
|
||||||
<ul ref={legend} className="flex flex-wrap justify-end" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* Chart built with Chart.js 3 */}
|
|
||||||
<div className="grow">
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LineChart05;
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function LineChart06({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
top: 12,
|
|
||||||
bottom: 16,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 7,
|
|
||||||
callback: (value) => formatValue(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
displayFormats: {
|
|
||||||
month: 'MMM YY',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
autoSkipPadding: 48,
|
|
||||||
maxRotation: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatValue(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LineChart06;
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, LineController, LineElement, Filler, PointElement, LinearScale, CategoryScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, CategoryScale, Tooltip);
|
|
||||||
|
|
||||||
function LineChart07({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
top: 12,
|
|
||||||
bottom: 16,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 7,
|
|
||||||
callback: (value) => formatValue(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
autoSkipPadding: 48,
|
|
||||||
maxRotation: 0,
|
|
||||||
align: 'end',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatValue(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LineChart07;
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function LineChart08({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
top: 16,
|
|
||||||
bottom: 16,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
drawTicks: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 2,
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
},
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatValue(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LineChart08;
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function LineChart09({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
display: false,
|
|
||||||
beginAtZero: true,
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'MM-DD-YYYY',
|
|
||||||
unit: 'month',
|
|
||||||
},
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
title: () => false, // Disable tooltip title
|
|
||||||
label: (context) => formatValue(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LineChart09;
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, PieController, ArcElement, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(PieController, ArcElement, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function PieChart({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const legend = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'pie',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
top: 4,
|
|
||||||
bottom: 4,
|
|
||||||
left: 24,
|
|
||||||
right: 24,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 200,
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
id: 'htmlLegend',
|
|
||||||
afterUpdate(c, args, options) {
|
|
||||||
const ul = legend.current;
|
|
||||||
if (!ul) return;
|
|
||||||
// Remove old legend items
|
|
||||||
while (ul.firstChild) {
|
|
||||||
ul.firstChild.remove();
|
|
||||||
}
|
|
||||||
// Reuse the built-in legendItems generator
|
|
||||||
const items = c.options.plugins.legend.labels.generateLabels(c);
|
|
||||||
items.forEach((item) => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.style.margin = tailwindConfig().theme.margin[1.5];
|
|
||||||
// Button element
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.style.display = 'inline-flex';
|
|
||||||
button.style.alignItems = 'center';
|
|
||||||
button.style.opacity = item.hidden ? '.3' : '';
|
|
||||||
button.onclick = () => {
|
|
||||||
c.toggleDataVisibility(item.index, !item.index);
|
|
||||||
c.update();
|
|
||||||
};
|
|
||||||
// Color box
|
|
||||||
const box = document.createElement('span');
|
|
||||||
box.style.display = 'block';
|
|
||||||
box.style.width = tailwindConfig().theme.width[3];
|
|
||||||
box.style.height = tailwindConfig().theme.height[3];
|
|
||||||
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
|
|
||||||
box.style.marginRight = tailwindConfig().theme.margin[1.5];
|
|
||||||
box.style.borderWidth = '3px';
|
|
||||||
box.style.borderColor = item.fillStyle;
|
|
||||||
box.style.pointerEvents = 'none';
|
|
||||||
// Label
|
|
||||||
const label = document.createElement('span');
|
|
||||||
label.style.color = tailwindConfig().theme.colors.slate[500];
|
|
||||||
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
|
|
||||||
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
|
|
||||||
const labelText = document.createTextNode(item.text);
|
|
||||||
label.appendChild(labelText);
|
|
||||||
li.appendChild(button);
|
|
||||||
button.appendChild(box);
|
|
||||||
button.appendChild(label);
|
|
||||||
ul.appendChild(li);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grow flex flex-col justify-center">
|
|
||||||
<div>
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
<div className="px-5 py-4">
|
|
||||||
<ul ref={legend} className="flex flex-wrap justify-center -m-1" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PieChart;
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, PolarAreaController, RadialLinearScale, Tooltip, Legend,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(PolarAreaController, RadialLinearScale, Tooltip, Legend);
|
|
||||||
|
|
||||||
function PolarChart({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const legend = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'polarArea',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: 24,
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
duration: 500,
|
|
||||||
},
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
plugins: [{
|
|
||||||
id: 'htmlLegend',
|
|
||||||
afterUpdate(c, args, options) {
|
|
||||||
const ul = legend.current
|
|
||||||
if (!ul) return
|
|
||||||
// Remove old legend items
|
|
||||||
while (ul.firstChild) {
|
|
||||||
ul.firstChild.remove()
|
|
||||||
}
|
|
||||||
// Reuse the built-in legendItems generator
|
|
||||||
const items = c.options.plugins.legend.labels.generateLabels(c)
|
|
||||||
items.forEach((item) => {
|
|
||||||
const li = document.createElement('li')
|
|
||||||
li.style.margin = tailwindConfig().theme.margin[1]
|
|
||||||
// Button element
|
|
||||||
const button = document.createElement('button')
|
|
||||||
button.classList.add('btn-xs')
|
|
||||||
button.style.backgroundColor = tailwindConfig().theme.colors.white
|
|
||||||
button.style.borderWidth = tailwindConfig().theme.borderWidth[1]
|
|
||||||
button.style.borderColor = tailwindConfig().theme.colors.slate[200]
|
|
||||||
button.style.color = tailwindConfig().theme.colors.slate[500]
|
|
||||||
button.style.boxShadow = tailwindConfig().theme.boxShadow.md
|
|
||||||
button.style.opacity = item.hidden ? '.3' : ''
|
|
||||||
button.onclick = () => {
|
|
||||||
c.toggleDataVisibility(item.index, !item.index)
|
|
||||||
c.update()
|
|
||||||
}
|
|
||||||
// Color box
|
|
||||||
const box = document.createElement('span')
|
|
||||||
box.style.display = 'block'
|
|
||||||
box.style.width = tailwindConfig().theme.width[2]
|
|
||||||
box.style.height = tailwindConfig().theme.height[2]
|
|
||||||
box.style.backgroundColor = item.fillStyle
|
|
||||||
box.style.borderRadius = tailwindConfig().theme.borderRadius.sm
|
|
||||||
box.style.marginRight = tailwindConfig().theme.margin[1]
|
|
||||||
box.style.pointerEvents = 'none'
|
|
||||||
// Label
|
|
||||||
const label = document.createElement('span')
|
|
||||||
label.style.display = 'flex'
|
|
||||||
label.style.alignItems = 'center'
|
|
||||||
const labelText = document.createTextNode(item.text)
|
|
||||||
label.appendChild(labelText)
|
|
||||||
li.appendChild(button)
|
|
||||||
button.appendChild(box)
|
|
||||||
button.appendChild(label)
|
|
||||||
ul.appendChild(li)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grow flex flex-col justify-center">
|
|
||||||
<div>
|
|
||||||
<canvas ref={canvas} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
<div className="px-5 pt-2 pb-6">
|
|
||||||
<ul ref={legend} className="flex flex-wrap justify-center -m-1"></ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PolarChart;
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
|
|
||||||
} from 'chart.js';
|
|
||||||
import 'chartjs-adapter-moment';
|
|
||||||
|
|
||||||
// Import utilities
|
|
||||||
import { tailwindConfig, formatValue } from '../utils/Utils';
|
|
||||||
|
|
||||||
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
|
|
||||||
|
|
||||||
function RealtimeChart({
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const canvas = useRef(null);
|
|
||||||
const chartValue = useRef(null);
|
|
||||||
const chartDeviation = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ctx = canvas.current;
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: data,
|
|
||||||
options: {
|
|
||||||
layout: {
|
|
||||||
padding: 20,
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
grid: {
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
suggestedMin: 30,
|
|
||||||
suggestedMax: 80,
|
|
||||||
ticks: {
|
|
||||||
maxTicksLimit: 5,
|
|
||||||
callback: (value) => formatValue(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
parser: 'hh:mm:ss',
|
|
||||||
unit: 'second',
|
|
||||||
tooltipFormat: 'MMM DD, H:mm:ss a',
|
|
||||||
displayFormats: {
|
|
||||||
second: 'H:mm:ss',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
drawBorder: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
autoSkipPadding: 48,
|
|
||||||
maxRotation: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
titleFont: {
|
|
||||||
weight: '600',
|
|
||||||
},
|
|
||||||
callbacks: {
|
|
||||||
label: (context) => formatValue(context.parsed.y),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
intersect: false,
|
|
||||||
mode: 'nearest',
|
|
||||||
},
|
|
||||||
animation: false,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
resizeDelay: 200,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return () => chart.destroy();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
// Update header values
|
|
||||||
useEffect(() => {
|
|
||||||
const currentValue = data.datasets[0].data[data.datasets[0].data.length - 1];
|
|
||||||
const previousValue = data.datasets[0].data[data.datasets[0].data.length - 2];
|
|
||||||
const diff = ((currentValue - previousValue) / previousValue) * 100;
|
|
||||||
chartValue.current.innerHTML = data.datasets[0].data[data.datasets[0].data.length - 1];
|
|
||||||
if (diff < 0) {
|
|
||||||
chartDeviation.current.style.backgroundColor = tailwindConfig().theme.colors.yellow[500];
|
|
||||||
} else {
|
|
||||||
chartDeviation.current.style.backgroundColor = tailwindConfig().theme.colors.emerald[500];
|
|
||||||
}
|
|
||||||
chartDeviation.current.innerHTML = `${diff > 0 ? '+' : ''}${diff.toFixed(2)}%`;
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="px-5 py-3">
|
|
||||||
<div className="flex items-start">
|
|
||||||
<div className="text-3xl font-bold text-slate-800 mr-2 tabular-nums">$<span ref={chartValue}>57.81</span></div>
|
|
||||||
<div ref={chartDeviation} className="text-sm font-semibold text-white px-1.5 rounded-full"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="grow">
|
|
||||||
<canvas ref={canvas} data={data} width={width} height={height}></canvas>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RealtimeChart;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
function AccordionBasic(props) {
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="px-5 py-4 rounded-sm border border-slate-200">
|
|
||||||
<button
|
|
||||||
className="flex items-center justify-between w-full group mb-1"
|
|
||||||
aria-expanded={open}
|
|
||||||
onClick={() => setOpen(!open)}
|
|
||||||
>
|
|
||||||
<div className="text-sm text-slate-800 font-medium">{props.title}</div>
|
|
||||||
<svg className={`w-8 h-8 shrink-0 fill-current text-slate-400 group-hover:text-slate-500 ml-3 ${open && 'rotate-180'}`} viewBox="0 0 32 32">
|
|
||||||
<path d="M16 20l-5.4-5.4 1.4-1.4 4 4 4-4 1.4 1.4z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<div className={`text-sm ${!open && 'hidden'}`}>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AccordionBasic;
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
function AccordionTableItem(props) {
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<tbody className="text-sm">
|
|
||||||
<tr>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="flex items-center text-slate-800">
|
|
||||||
<div className="w-10 h-10 shrink-0 flex items-center justify-center bg-slate-100 rounded-full mr-2 sm:mr-3">
|
|
||||||
<img className="rounded-full ml-1" src={props.image} width="40" height="40" alt={props.customer} />
|
|
||||||
</div>
|
|
||||||
<div className="font-medium text-slate-800">{props.customer}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="text-left font-medium text-emerald-500">{props.total}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="inline-flex font-medium bg-amber-100 text-amber-600 rounded-full text-center px-2.5 py-0.5">{props.status}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="text-center">{props.items}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="text-left">{props.location}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-400 shrink-0 mr-2" viewBox="0 0 16 16">
|
|
||||||
<path d="M4.3 4.5c1.9-1.9 5.1-1.9 7 0 .7.7 1.2 1.7 1.4 2.7l2-.3c-.2-1.5-.9-2.8-1.9-3.8C10.1.4 5.7.4 2.9 3.1L.7.9 0 7.3l6.4-.7-2.1-2.1zM15.6 8.7l-6.4.7 2.1 2.1c-1.9 1.9-5.1 1.9-7 0-.7-.7-1.2-1.7-1.4-2.7l-2 .3c.2 1.5.9 2.8 1.9 3.8 1.4 1.4 3.1 2 4.9 2 1.8 0 3.6-.7 4.9-2l2.2 2.2.8-6.4z" />
|
|
||||||
</svg>
|
|
||||||
<div>{props.type}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap w-px">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<button
|
|
||||||
className={`text-slate-400 hover:text-slate-500 transform ${open && 'rotate-180'}`}
|
|
||||||
aria-expanded={open}
|
|
||||||
onClick={() => setOpen(!open)}
|
|
||||||
aria-controls={`description-${props.id}`}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Menu</span>
|
|
||||||
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32">
|
|
||||||
<path d="M16 20l-5.4-5.4 1.4-1.4 4 4 4-4 1.4 1.4z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/*
|
|
||||||
Example of content revealing when clicking the button on the right side:
|
|
||||||
Note that you must set a "colSpan" attribute on the <td> element,
|
|
||||||
and it should match the number of columns in your table
|
|
||||||
*/}
|
|
||||||
<tr id={`description-${props.id}`} role="region" className={`${!open && 'hidden'}`}>
|
|
||||||
<td colSpan="10" className="px-2 first:pl-5 last:pr-5 py-3">
|
|
||||||
<div className="flex items-center bg-slate-50 p-3 -mt-3">
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current text-slate-400 mr-2">
|
|
||||||
<path d="M1 16h3c.3 0 .5-.1.7-.3l11-11c.4-.4.4-1 0-1.4l-3-3c-.4-.4-1-.4-1.4 0l-11 11c-.2.2-.3.4-.3.7v3c0 .6.4 1 1 1zm1-3.6l10-10L13.6 4l-10 10H2v-1.6z" />
|
|
||||||
</svg>
|
|
||||||
<div className="italic">{props.description}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AccordionTableItem;
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
function AccordionTableRichItem(props) {
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<tbody className="text-sm">
|
|
||||||
<tr>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="flex items-center text-slate-800">
|
|
||||||
<div className="w-10 h-10 shrink-0 flex items-center justify-center bg-slate-100 rounded-full mr-2 sm:mr-3">
|
|
||||||
<img className="rounded-full ml-1" src={props.image} width="40" height="40" alt={props.customer} />
|
|
||||||
</div>
|
|
||||||
<div className="font-medium text-slate-800">{props.customer}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="text-left">{props.email}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="text-left">{props.location}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="text-left">{props.date}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
|
|
||||||
<div className="text-left text-emerald-500 font-medium">{props.amount}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap w-px">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<button
|
|
||||||
className={`text-slate-400 hover:text-slate-500 transform ${open && 'rotate-180'}`}
|
|
||||||
aria-expanded={open}
|
|
||||||
onClick={() => setOpen(!open)}
|
|
||||||
aria-controls={`description-${props.id}`}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Menu</span>
|
|
||||||
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32">
|
|
||||||
<path d="M16 20l-5.4-5.4 1.4-1.4 4 4 4-4 1.4 1.4z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/*
|
|
||||||
Example of content revealing when clicking the button on the right side:
|
|
||||||
Note that you must set a "colSpan" attribute on the <td> element,
|
|
||||||
and it should match the number of columns in your table
|
|
||||||
*/}
|
|
||||||
<tr id={`description-${props.id}`} role="region" className={`${!open && 'hidden'}`}>
|
|
||||||
<td colSpan="10" className="px-2 first:pl-5 last:pr-5 py-3">
|
|
||||||
<div className="bg-slate-50 p-3 -mt-3">
|
|
||||||
<div className="text-sm mb-3">
|
|
||||||
<div className="font-medium text-slate-800 mb-1">{props.descriptionTitle}</div>
|
|
||||||
<div>{props.descriptionBody}</div>
|
|
||||||
</div>
|
|
||||||
<button className="btn-xs bg-indigo-500 hover:bg-indigo-600 text-white">Approve</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AccordionTableRichItem;
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function Banner({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
type,
|
|
||||||
open,
|
|
||||||
setOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const typeIcon = (type) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'warning':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
case 'error':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3.5 10.1l-1.4 1.4L8 9.4l-2.1 2.1-1.4-1.4L6.6 8 4.5 5.9l1.4-1.4L8 6.6l2.1-2.1 1.4 1.4L9.4 8l2.1 2.1z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
case 'success':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zM7 11.4L3.6 8 5 6.6l2 2 4-4L12.4 6 7 11.4z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm1 12H7V7h2v5zM8 6c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const typeColor = (type) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'warning':
|
|
||||||
return 'bg-amber-500';
|
|
||||||
case 'error':
|
|
||||||
return 'bg-rose-500';
|
|
||||||
case 'success':
|
|
||||||
return 'bg-emerald-500';
|
|
||||||
default:
|
|
||||||
return 'bg-indigo-500';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{open &&
|
|
||||||
<div className={className}>
|
|
||||||
<div className={`px-4 py-2 rounded-sm text-sm text-white ${typeColor(type)}`}>
|
|
||||||
<div className="flex w-full justify-between items-start">
|
|
||||||
<div className="flex">
|
|
||||||
{typeIcon(type)}
|
|
||||||
<div className="font-medium">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button className="opacity-70 hover:opacity-80 ml-3 mt-[3px]" onClick={() => setOpen(false)}>
|
|
||||||
<div className="sr-only">Close</div>
|
|
||||||
<svg className="w-4 h-4 fill-current">
|
|
||||||
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Banner;
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function Banner2({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
type,
|
|
||||||
open,
|
|
||||||
setOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const typeIcon = (type) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'warning':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
case 'error':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3.5 10.1l-1.4 1.4L8 9.4l-2.1 2.1-1.4-1.4L6.6 8 4.5 5.9l1.4-1.4L8 6.6l2.1-2.1 1.4 1.4L9.4 8l2.1 2.1z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
case 'success':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zM7 11.4L3.6 8 5 6.6l2 2 4-4L12.4 6 7 11.4z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm1 12H7V7h2v5zM8 6c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const typeColor = (type) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'warning':
|
|
||||||
return 'bg-amber-100 border-amber-200 text-amber-600';
|
|
||||||
case 'error':
|
|
||||||
return 'bg-rose-100 border-rose-200 text-rose-600';
|
|
||||||
case 'success':
|
|
||||||
return 'bg-emerald-100 border-emerald-200 text-emerald-600';
|
|
||||||
default:
|
|
||||||
return 'bg-indigo-100 border-indigo-200 text-indigo-500';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{open &&
|
|
||||||
<div className={className}>
|
|
||||||
<div className={`px-4 py-2 rounded-sm text-sm border ${typeColor(type)}`}>
|
|
||||||
<div className="flex w-full justify-between items-start">
|
|
||||||
<div className="flex">
|
|
||||||
{typeIcon(type)}
|
|
||||||
<div>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button className="opacity-70 hover:opacity-80 ml-3 mt-[3px]" onClick={() => setOpen(false)}>
|
|
||||||
<div className="sr-only">Close</div>
|
|
||||||
<svg className="w-4 h-4 fill-current">
|
|
||||||
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Banner2;
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function DateSelect() {
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
period: 'Today'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
period: 'Last 7 Days'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
period: 'Last Month'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
period: 'Last 12 Months'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
period: 'All Time'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
||||||
const [selected, setSelected] = useState(2);
|
|
||||||
|
|
||||||
const trigger = useRef(null);
|
|
||||||
const dropdown = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!dropdown.current) return;
|
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!dropdownOpen || keyCode !== 27) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<button
|
|
||||||
ref={trigger}
|
|
||||||
className="btn justify-between min-w-44 bg-white border-slate-200 hover:border-slate-300 text-slate-500 hover:text-slate-600"
|
|
||||||
aria-label="Select date range"
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
aria-expanded={dropdownOpen}
|
|
||||||
>
|
|
||||||
<span className="flex items-center">
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-500 shrink-0 mr-2" viewBox="0 0 16 16">
|
|
||||||
<path d="M15 2h-2V0h-2v2H9V0H7v2H5V0H3v2H1a1 1 0 00-1 1v12a1 1 0 001 1h14a1 1 0 001-1V3a1 1 0 00-1-1zm-1 12H2V6h12v8z" />
|
|
||||||
</svg>
|
|
||||||
<span>{options[selected].period}</span>
|
|
||||||
</span>
|
|
||||||
<svg className="shrink-0 ml-1 fill-current text-slate-400" width="11" height="7" viewBox="0 0 11 7">
|
|
||||||
<path d="M5.4 6.8L0 1.4 1.4 0l4 4 4-4 1.4 1.4z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Transition
|
|
||||||
show={dropdownOpen}
|
|
||||||
tag="div"
|
|
||||||
className="z-10 absolute top-full right-0 w-full bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1"
|
|
||||||
enter="transition ease-out duration-100 transform"
|
|
||||||
enterStart="opacity-0 -translate-y-2"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-out duration-100"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={dropdown}
|
|
||||||
className="font-medium text-sm text-slate-600"
|
|
||||||
onFocus={() => setDropdownOpen(true)}
|
|
||||||
onBlur={() => setDropdownOpen(false)}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
options.map(option => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
key={option.id}
|
|
||||||
tabIndex="0"
|
|
||||||
className={`flex items-center w-full hover:bg-slate-50 py-1 px-3 cursor-pointer ${option.id === selected && 'text-indigo-500'}`}
|
|
||||||
onClick={() => { setSelected(option.id); setDropdownOpen(false); }}
|
|
||||||
>
|
|
||||||
<svg className={`shrink-0 mr-2 fill-current text-indigo-500 ${option.id !== selected && 'invisible'}`} width="12" height="9" viewBox="0 0 12 9">
|
|
||||||
<path d="M10.28.28L3.989 6.575 1.695 4.28A1 1 0 00.28 5.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28.28z" />
|
|
||||||
</svg>
|
|
||||||
<span>{option.period}</span>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DateSelect;
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Flatpickr from 'react-flatpickr';
|
|
||||||
|
|
||||||
function Datepicker({
|
|
||||||
align
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
mode: 'range',
|
|
||||||
static: true,
|
|
||||||
monthSelectorType: 'static',
|
|
||||||
dateFormat: 'M j, Y',
|
|
||||||
defaultDate: [new Date().setDate(new Date().getDate() - 6), new Date()],
|
|
||||||
prevArrow: '<svg class="fill-current" width="7" height="11" viewBox="0 0 7 11"><path d="M5.4 10.8l1.4-1.4-4-4 4-4L5.4 0 0 5.4z" /></svg>',
|
|
||||||
nextArrow: '<svg class="fill-current" width="7" height="11" viewBox="0 0 7 11"><path d="M1.4 10.8L0 9.4l4-4-4-4L1.4 0l5.4 5.4z" /></svg>',
|
|
||||||
onReady: (selectedDates, dateStr, instance) => {
|
|
||||||
instance.element.value = dateStr.replace('to', '-');
|
|
||||||
const customClass = (align) ? align : '';
|
|
||||||
instance.calendarContainer.classList.add(`flatpickr-${customClass}`);
|
|
||||||
},
|
|
||||||
onChange: (selectedDates, dateStr, instance) => {
|
|
||||||
instance.element.value = dateStr.replace('to', '-');
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<Flatpickr className="form-input pl-9 text-slate-500 hover:text-slate-600 font-medium focus:border-slate-300 w-60" options={options} />
|
|
||||||
<div className="absolute inset-0 right-auto flex items-center pointer-events-none">
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-500 ml-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M15 2h-2V0h-2v2H9V0H7v2H5V0H3v2H1a1 1 0 00-1 1v12a1 1 0 001 1h14a1 1 0 001-1V3a1 1 0 00-1-1zm-1 12H2V6h12v8z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Datepicker;
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function DropdownClassic() {
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
period: 'Today'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
period: 'Last 7 Days'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
period: 'Last Month'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
period: 'Last 12 Months'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
period: 'All Time'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
||||||
const [selected, setSelected] = useState(2);
|
|
||||||
|
|
||||||
const trigger = useRef(null);
|
|
||||||
const dropdown = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!dropdown.current) return;
|
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!dropdownOpen || keyCode !== 27) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative inline-flex">
|
|
||||||
<button
|
|
||||||
ref={trigger}
|
|
||||||
className="btn justify-between min-w-44 bg-white border-slate-200 hover:border-slate-300 text-slate-500 hover:text-slate-600"
|
|
||||||
aria-label="Select date range"
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
aria-expanded={dropdownOpen}
|
|
||||||
>
|
|
||||||
<span className="flex items-center">
|
|
||||||
<span>{options[selected].period}</span>
|
|
||||||
</span>
|
|
||||||
<svg className="shrink-0 ml-1 fill-current text-slate-400" width="11" height="7" viewBox="0 0 11 7">
|
|
||||||
<path d="M5.4 6.8L0 1.4 1.4 0l4 4 4-4 1.4 1.4z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Transition
|
|
||||||
show={dropdownOpen}
|
|
||||||
tag="div"
|
|
||||||
className="z-10 absolute top-full left-0 w-full bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1"
|
|
||||||
enter="transition ease-out duration-100 transform"
|
|
||||||
enterStart="opacity-0 -translate-y-2"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-out duration-100"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={dropdown}
|
|
||||||
className="font-medium text-sm text-slate-600"
|
|
||||||
onFocus={() => setDropdownOpen(true)}
|
|
||||||
onBlur={() => setDropdownOpen(false)}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
options.map(option => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
key={option.id}
|
|
||||||
tabIndex="0"
|
|
||||||
className={`flex items-center w-full hover:bg-slate-50 py-1 px-3 cursor-pointer ${option.id === selected && 'text-indigo-500'}`}
|
|
||||||
onClick={() => { setSelected(option.id); setDropdownOpen(false); }}
|
|
||||||
>
|
|
||||||
<svg className={`shrink-0 mr-2 fill-current text-indigo-500 ${option.id !== selected && 'invisible'}`} width="12" height="9" viewBox="0 0 12 9">
|
|
||||||
<path d="M10.28.28L3.989 6.575 1.695 4.28A1 1 0 00.28 5.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28.28z" />
|
|
||||||
</svg>
|
|
||||||
<span>{option.period}</span>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DropdownClassic;
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function DropdownEditMenu({
|
|
||||||
children,
|
|
||||||
align,
|
|
||||||
...rest
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
||||||
|
|
||||||
const trigger = useRef(null);
|
|
||||||
const dropdown = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!dropdown.current) return;
|
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!dropdownOpen || keyCode !== 27) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div {...rest}>
|
|
||||||
<button
|
|
||||||
ref={trigger}
|
|
||||||
className={`bg-white text-slate-400 hover:text-slate-500 rounded-full ${dropdownOpen && 'bg-slate-100 text-slate-500'}`}
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
aria-expanded={dropdownOpen}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Menu</span>
|
|
||||||
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32">
|
|
||||||
<circle cx="16" cy="16" r="2" />
|
|
||||||
<circle cx="10" cy="16" r="2" />
|
|
||||||
<circle cx="22" cy="16" r="2" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Transition
|
|
||||||
show={dropdownOpen}
|
|
||||||
tag="div"
|
|
||||||
className={`origin-top-right z-10 absolute top-full min-w-36 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`}
|
|
||||||
enter="transition ease-out duration-200 transform"
|
|
||||||
enterStart="opacity-0 -translate-y-2"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-out duration-200"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
>
|
|
||||||
<ul
|
|
||||||
ref={dropdown}
|
|
||||||
onFocus={() => setDropdownOpen(true)}
|
|
||||||
onBlur={() => setDropdownOpen(false)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</ul>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DropdownEditMenu;
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function DropdownFilter({
|
|
||||||
align
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
||||||
|
|
||||||
const trigger = useRef(null);
|
|
||||||
const dropdown = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!dropdown.current) return;
|
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!dropdownOpen || keyCode !== 27) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative inline-flex">
|
|
||||||
<button
|
|
||||||
ref={trigger}
|
|
||||||
className="btn bg-white border-slate-200 hover:border-slate-300 text-slate-500 hover:text-slate-600"
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
aria-expanded={dropdownOpen}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Filter</span><wbr />
|
|
||||||
<svg className="w-4 h-4 fill-current" viewBox="0 0 16 16">
|
|
||||||
<path d="M9 15H7a1 1 0 010-2h2a1 1 0 010 2zM11 11H5a1 1 0 010-2h6a1 1 0 010 2zM13 7H3a1 1 0 010-2h10a1 1 0 010 2zM15 3H1a1 1 0 010-2h14a1 1 0 010 2z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Transition
|
|
||||||
show={dropdownOpen}
|
|
||||||
tag="div"
|
|
||||||
className={`origin-top-right z-10 absolute top-full min-w-56 bg-white border border-slate-200 pt-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`}
|
|
||||||
enter="transition ease-out duration-200 transform"
|
|
||||||
enterStart="opacity-0 -translate-y-2"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-out duration-200"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
>
|
|
||||||
<div ref={dropdown}>
|
|
||||||
<div className="text-xs font-semibold text-slate-400 uppercase pt-1.5 pb-2 px-4">Filters</div>
|
|
||||||
<ul className="mb-4">
|
|
||||||
<li className="py-1 px-3">
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input type="checkbox" className="form-checkbox" />
|
|
||||||
<span className="text-sm font-medium ml-2">Direct VS Indirect</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li className="py-1 px-3">
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input type="checkbox" className="form-checkbox" />
|
|
||||||
<span className="text-sm font-medium ml-2">Real Time Value</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li className="py-1 px-3">
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input type="checkbox" className="form-checkbox" />
|
|
||||||
<span className="text-sm font-medium ml-2">Top Channels</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li className="py-1 px-3">
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input type="checkbox" className="form-checkbox" />
|
|
||||||
<span className="text-sm font-medium ml-2">Sales VS Refunds</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li className="py-1 px-3">
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input type="checkbox" className="form-checkbox" />
|
|
||||||
<span className="text-sm font-medium ml-2">Last Order</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li className="py-1 px-3">
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input type="checkbox" className="form-checkbox" />
|
|
||||||
<span className="text-sm font-medium ml-2">Total Spent</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div className="py-2 px-3 border-t border-slate-200 bg-slate-50">
|
|
||||||
<ul className="flex items-center justify-between">
|
|
||||||
<li>
|
|
||||||
<button className="btn-xs bg-white border-slate-200 hover:border-slate-300 text-slate-500 hover:text-slate-600">Clear</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button className="btn-xs bg-indigo-500 hover:bg-indigo-600 text-white" onClick={() => setDropdownOpen(false)} onBlur={() => setDropdownOpen(false)}>Apply</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DropdownFilter;
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function DropdownFull() {
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
period: 'Most Popular'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
period: 'Newest'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
period: 'Lowest Price'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
period: 'Highest Price'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
||||||
const [selected, setSelected] = useState(0);
|
|
||||||
|
|
||||||
const trigger = useRef(null);
|
|
||||||
const dropdown = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!dropdown.current) return;
|
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!dropdownOpen || keyCode !== 27) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative inline-flex w-full">
|
|
||||||
<button
|
|
||||||
ref={trigger}
|
|
||||||
className="btn w-full justify-between min-w-44 bg-white border-slate-200 hover:border-slate-300 text-slate-500 hover:text-slate-600"
|
|
||||||
aria-label="Select date range"
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
aria-expanded={dropdownOpen}
|
|
||||||
>
|
|
||||||
<span className="flex items-center">
|
|
||||||
<span>{options[selected].period}</span>
|
|
||||||
</span>
|
|
||||||
<svg className="shrink-0 ml-1 fill-current text-slate-400" width="11" height="7" viewBox="0 0 11 7">
|
|
||||||
<path d="M5.4 6.8L0 1.4 1.4 0l4 4 4-4 1.4 1.4z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Transition
|
|
||||||
show={dropdownOpen}
|
|
||||||
tag="div"
|
|
||||||
className="z-10 absolute top-full left-0 w-full bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1"
|
|
||||||
enter="transition ease-out duration-100 transform"
|
|
||||||
enterStart="opacity-0 -translate-y-2"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-out duration-100"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={dropdown}
|
|
||||||
className="font-medium text-sm text-slate-600 divide-y divide-slate-200"
|
|
||||||
onFocus={() => setDropdownOpen(true)}
|
|
||||||
onBlur={() => setDropdownOpen(false)}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
options.map(option => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
key={option.id}
|
|
||||||
tabIndex="0"
|
|
||||||
className={`flex items-center justify-between w-full hover:bg-slate-50 py-2 px-3 cursor-pointer ${option.id === selected && 'text-indigo-500'}`}
|
|
||||||
onClick={() => { setSelected(option.id); setDropdownOpen(false); }}
|
|
||||||
>
|
|
||||||
<span>{option.period}</span>
|
|
||||||
<svg className={`shrink-0 mr-2 fill-current text-indigo-500 ${option.id !== selected && 'invisible'}`} width="12" height="9" viewBox="0 0 12 9">
|
|
||||||
<path d="M10.28.28L3.989 6.575 1.695 4.28A1 1 0 00.28 5.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28.28z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DropdownFull;
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function DropdownHelp({
|
|
||||||
align
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
||||||
|
|
||||||
const trigger = useRef(null);
|
|
||||||
const dropdown = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!dropdown.current) return;
|
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!dropdownOpen || keyCode !== 27) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative inline-flex">
|
|
||||||
<button
|
|
||||||
ref={trigger}
|
|
||||||
className={`w-8 h-8 flex items-center justify-center bg-slate-100 hover:bg-slate-200 transition duration-150 rounded-full ${dropdownOpen && 'bg-slate-200'}`}
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
aria-expanded={dropdownOpen}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Need help?</span>
|
|
||||||
<svg className="w-4 h-4" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path className="fill-current text-slate-500" d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Transition
|
|
||||||
className={`origin-top-right z-10 absolute top-full min-w-44 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`}
|
|
||||||
show={dropdownOpen}
|
|
||||||
enter="transition ease-out duration-200 transform"
|
|
||||||
enterStart="opacity-0 -translate-y-2"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-out duration-200"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={dropdown}
|
|
||||||
onFocus={() => setDropdownOpen(true)}
|
|
||||||
onBlur={() => setDropdownOpen(false)}
|
|
||||||
>
|
|
||||||
<div className="text-xs font-semibold text-slate-400 uppercase pt-1.5 pb-2 px-4">Need help?</div>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-3 h-3 fill-current text-indigo-300 shrink-0 mr-2" viewBox="0 0 12 12">
|
|
||||||
<rect y="3" width="12" height="9" rx="1" />
|
|
||||||
<path d="M2 0h8v2H2z" />
|
|
||||||
</svg>
|
|
||||||
<span>Documentation</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-3 h-3 fill-current text-indigo-300 shrink-0 mr-2" viewBox="0 0 12 12">
|
|
||||||
<path d="M10.5 0h-9A1.5 1.5 0 000 1.5v9A1.5 1.5 0 001.5 12h9a1.5 1.5 0 001.5-1.5v-9A1.5 1.5 0 0010.5 0zM10 7L8.207 5.207l-3 3-1.414-1.414 3-3L5 2h5v5z" />
|
|
||||||
</svg>
|
|
||||||
<span>Support Site</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-3 h-3 fill-current text-indigo-300 shrink-0 mr-2" viewBox="0 0 12 12">
|
|
||||||
<path d="M11.854.146a.5.5 0 00-.525-.116l-11 4a.5.5 0 00-.015.934l4.8 1.921 1.921 4.8A.5.5 0 007.5 12h.008a.5.5 0 00.462-.329l4-11a.5.5 0 00-.116-.525z" />
|
|
||||||
</svg>
|
|
||||||
<span>Contact us</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DropdownHelp;
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function DropdownNotifications({
|
|
||||||
align
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
||||||
|
|
||||||
const trigger = useRef(null);
|
|
||||||
const dropdown = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!dropdown.current) return;
|
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!dropdownOpen || keyCode !== 27) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative inline-flex">
|
|
||||||
<button
|
|
||||||
ref={trigger}
|
|
||||||
className={`w-8 h-8 flex items-center justify-center bg-slate-100 hover:bg-slate-200 transition duration-150 rounded-full ${dropdownOpen && 'bg-slate-200'}`}
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
aria-expanded={dropdownOpen}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Notifications</span>
|
|
||||||
<svg className="w-4 h-4" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path className="fill-current text-slate-500" d="M6.5 0C2.91 0 0 2.462 0 5.5c0 1.075.37 2.074 1 2.922V12l2.699-1.542A7.454 7.454 0 006.5 11c3.59 0 6.5-2.462 6.5-5.5S10.09 0 6.5 0z" />
|
|
||||||
<path className="fill-current text-slate-400" d="M16 9.5c0-.987-.429-1.897-1.147-2.639C14.124 10.348 10.66 13 6.5 13c-.103 0-.202-.018-.305-.021C7.231 13.617 8.556 14 10 14c.449 0 .886-.04 1.307-.11L15 16v-4h-.012C15.627 11.285 16 10.425 16 9.5z" />
|
|
||||||
</svg>
|
|
||||||
<div className="absolute top-0 right-0 w-2.5 h-2.5 bg-rose-500 border-2 border-white rounded-full"></div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Transition
|
|
||||||
className={`origin-top-right z-10 absolute top-full -mr-48 sm:mr-0 min-w-80 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`}
|
|
||||||
show={dropdownOpen}
|
|
||||||
enter="transition ease-out duration-200 transform"
|
|
||||||
enterStart="opacity-0 -translate-y-2"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-out duration-200"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={dropdown}
|
|
||||||
onFocus={() => setDropdownOpen(true)}
|
|
||||||
onBlur={() => setDropdownOpen(false)}
|
|
||||||
>
|
|
||||||
<div className="text-xs font-semibold text-slate-400 uppercase pt-1.5 pb-2 px-4">Notifications</div>
|
|
||||||
<ul>
|
|
||||||
<li className="border-b border-slate-200 last:border-0">
|
|
||||||
<Link
|
|
||||||
className="block py-2 px-4 hover:bg-slate-50"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
>
|
|
||||||
<span className="block text-sm mb-2">📣 <span className="font-medium text-slate-800">Edit your information in a swipe</span> Sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim.</span>
|
|
||||||
<span className="block text-xs font-medium text-slate-400">Feb 12, 2021</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li className="border-b border-slate-200 last:border-0">
|
|
||||||
<Link
|
|
||||||
className="block py-2 px-4 hover:bg-slate-50"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
>
|
|
||||||
<span className="block text-sm mb-2">📣 <span className="font-medium text-slate-800">Edit your information in a swipe</span> Sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim.</span>
|
|
||||||
<span className="block text-xs font-medium text-slate-400">Feb 9, 2021</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li className="border-b border-slate-200 last:border-0">
|
|
||||||
<Link
|
|
||||||
className="block py-2 px-4 hover:bg-slate-50"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
>
|
|
||||||
<span className="block text-sm mb-2">🚀<span className="font-medium text-slate-800">Say goodbye to paper receipts!</span> Sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim.</span>
|
|
||||||
<span className="block text-xs font-medium text-slate-400">Jan 24, 2020</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DropdownNotifications;
|
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import Transition from '../utils/Transition';
|
import Transition from '../utils/Transition';
|
||||||
|
import { store } from '../store/store';
|
||||||
|
import { generateSvgAvatar } from '../images/GenerateOnboardingSvg/GenerateSvg';
|
||||||
import UserAvatar from '../images/user-avatar-32.png';
|
import UserAvatar from '../images/user-avatar-32.png';
|
||||||
|
|
||||||
function DropdownProfile({
|
const DropdownProfile = observer(({ align }) => {
|
||||||
align
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||||
|
|
||||||
const trigger = useRef(null);
|
const trigger = useRef(null);
|
||||||
@@ -17,7 +16,12 @@ function DropdownProfile({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const clickHandler = ({ target }) => {
|
const clickHandler = ({ target }) => {
|
||||||
if (!dropdown.current) return;
|
if (!dropdown.current) return;
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
if (
|
||||||
|
!dropdownOpen ||
|
||||||
|
dropdown.current.contains(target) ||
|
||||||
|
trigger.current.contains(target)
|
||||||
|
)
|
||||||
|
return;
|
||||||
setDropdownOpen(false);
|
setDropdownOpen(false);
|
||||||
};
|
};
|
||||||
document.addEventListener('click', clickHandler);
|
document.addEventListener('click', clickHandler);
|
||||||
@@ -34,6 +38,11 @@ function DropdownProfile({
|
|||||||
return () => document.removeEventListener('keydown', keyHandler);
|
return () => document.removeEventListener('keydown', keyHandler);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const logOut = () => {
|
||||||
|
sessionStorage.removeItem('passPhrase');
|
||||||
|
store.fetchPassPhrase();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative inline-flex">
|
<div className="relative inline-flex">
|
||||||
<button
|
<button
|
||||||
@@ -43,17 +52,30 @@ function DropdownProfile({
|
|||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
onClick={() => setDropdownOpen(!dropdownOpen)}
|
||||||
aria-expanded={dropdownOpen}
|
aria-expanded={dropdownOpen}
|
||||||
>
|
>
|
||||||
<img className="w-8 h-8 rounded-full" src={UserAvatar} width="32" height="32" alt="User" />
|
<img
|
||||||
|
className="w-8 h-8 rounded-full"
|
||||||
|
src={generateSvgAvatar(store.pubKey) || UserAvatar}
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
alt="User"
|
||||||
|
/>
|
||||||
<div className="flex items-center truncate">
|
<div className="flex items-center truncate">
|
||||||
<span className="truncate ml-2 text-sm font-medium group-hover:text-slate-800">Acme Inc.</span>
|
<span className="truncate ml-2 text-sm font-medium group-hover:text-slate-800">
|
||||||
<svg className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400" viewBox="0 0 12 12">
|
{store.accountName}
|
||||||
|
</span>
|
||||||
|
<svg
|
||||||
|
className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400"
|
||||||
|
viewBox="0 0 12 12"
|
||||||
|
>
|
||||||
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
|
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
className={`origin-top-right z-10 absolute top-full min-w-44 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`}
|
className={`origin-top-right z-10 absolute top-full min-w-44 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${
|
||||||
|
align === 'right' ? 'right-0' : 'left-0'
|
||||||
|
}`}
|
||||||
show={dropdownOpen}
|
show={dropdownOpen}
|
||||||
enter="transition ease-out duration-200 transform"
|
enter="transition ease-out duration-200 transform"
|
||||||
enterStart="opacity-0 -translate-y-2"
|
enterStart="opacity-0 -translate-y-2"
|
||||||
@@ -68,24 +90,24 @@ function DropdownProfile({
|
|||||||
onBlur={() => setDropdownOpen(false)}
|
onBlur={() => setDropdownOpen(false)}
|
||||||
>
|
>
|
||||||
<div className="pt-0.5 pb-2 px-3 mb-1 border-b border-slate-200">
|
<div className="pt-0.5 pb-2 px-3 mb-1 border-b border-slate-200">
|
||||||
<div className="font-medium text-slate-800">Acme Inc.</div>
|
<div className="font-medium text-slate-800">
|
||||||
<div className="text-xs text-slate-500 italic">Administrator</div>
|
{store.accountName}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<Link
|
<span
|
||||||
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
|
className="cursor-pointer font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
|
||||||
to="/settings"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
onClick={() => setDropdownOpen(!dropdownOpen)}
|
||||||
>
|
>
|
||||||
Settings
|
Settings
|
||||||
</Link>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link
|
<Link
|
||||||
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
|
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
|
||||||
to="/signin"
|
to="/"
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
onClick={() => logOut()}
|
||||||
>
|
>
|
||||||
Sign Out
|
Sign Out
|
||||||
</Link>
|
</Link>
|
||||||
@@ -94,7 +116,7 @@ function DropdownProfile({
|
|||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
export default DropdownProfile;
|
export default DropdownProfile;
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function DropdownSort({
|
|
||||||
align
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
||||||
|
|
||||||
const trigger = useRef(null);
|
|
||||||
const dropdown = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!dropdown.current) return;
|
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!dropdownOpen || keyCode !== 27) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative inline-flex">
|
|
||||||
<button
|
|
||||||
ref={trigger}
|
|
||||||
className="inline-flex justify-center items-center group"
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
aria-expanded={dropdownOpen}
|
|
||||||
>
|
|
||||||
<div className="flex items-center truncate">
|
|
||||||
<span className="truncate font-medium text-indigo-500 group-hover:text-indigo-600">Newest</span>
|
|
||||||
<svg className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400" viewBox="0 0 12 12">
|
|
||||||
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Transition
|
|
||||||
className={`origin-top-right z-10 absolute top-full bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${
|
|
||||||
align === 'right' ? 'right-0' : 'left-0'
|
|
||||||
}`}
|
|
||||||
show={dropdownOpen}
|
|
||||||
enter="transition ease-out duration-200 transform"
|
|
||||||
enterStart="opacity-0 -translate-y-2"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-out duration-200"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
>
|
|
||||||
<div ref={dropdown} onFocus={() => setDropdownOpen(true)} onBlur={() => setDropdownOpen(false)}>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="font-medium text-sm text-slate-600 hover:text-slate-800 flex items-center py-1 px-3"
|
|
||||||
href="#0"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
>
|
|
||||||
Oldest
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="font-medium text-sm text-slate-600 hover:text-slate-800 flex items-center py-1 px-3"
|
|
||||||
href="#0"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
>
|
|
||||||
Popular
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DropdownSort;
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
import DropdownImage from '../images/user-avatar-32.png';
|
|
||||||
import DropdownImage01 from '../images/channel-01.png';
|
|
||||||
import DropdownImage02 from '../images/channel-02.png';
|
|
||||||
import DropdownImage03 from '../images/channel-03.png';
|
|
||||||
|
|
||||||
function DropdownSwitch({
|
|
||||||
align
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
||||||
|
|
||||||
const trigger = useRef(null);
|
|
||||||
const dropdown = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!dropdown.current) return;
|
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!dropdownOpen || keyCode !== 27) return;
|
|
||||||
setDropdownOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<button
|
|
||||||
ref={trigger}
|
|
||||||
className="grow flex items-center truncate"
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
aria-expanded={dropdownOpen}
|
|
||||||
>
|
|
||||||
<img className="w-8 h-8 rounded-full mr-2" src={DropdownImage} width="32" height="32" alt="Group 01" />
|
|
||||||
<div className="truncate">
|
|
||||||
<span className="text-sm font-medium">Acme Inc.</span>
|
|
||||||
</div>
|
|
||||||
<svg className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400" viewBox="0 0 12 12">
|
|
||||||
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Transition
|
|
||||||
className={`origin-top-right z-10 absolute top-full min-w-60 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`}
|
|
||||||
show={dropdownOpen}
|
|
||||||
enter="transition ease-out duration-200 transform"
|
|
||||||
enterStart="opacity-0 -translate-y-2"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-out duration-200"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
>
|
|
||||||
<ul
|
|
||||||
ref={dropdown}
|
|
||||||
onFocus={() => setDropdownOpen(true)}
|
|
||||||
onBlur={() => setDropdownOpen(false)}
|
|
||||||
>
|
|
||||||
<li>
|
|
||||||
<a className="font-medium text-sm text-slate-600 hover:text-slate-800 block py-1.5 px-3" href="#0" onClick={() => setDropdownOpen(false)}>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="grow flex items-center truncate">
|
|
||||||
<img className="w-7 h-7 rounded-full mr-2" src={DropdownImage01} width="28" height="28" alt="Channel 01" />
|
|
||||||
<div className="truncate">Acme Inc.</div>
|
|
||||||
</div>
|
|
||||||
<svg className="w-3 h-3 shrink-0 fill-current text-indigo-500 ml-1" viewBox="0 0 12 12">
|
|
||||||
<path d="M10.28 1.28L3.989 7.575 1.695 5.28A1 1 0 00.28 6.695l3 3a1 1 0 001.414 0l7-7A1 1 0 0010.28 1.28z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a className="font-medium text-sm text-slate-600 hover:text-slate-800 block py-1.5 px-3" href="#0" onClick={() => setDropdownOpen(false)}>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="grow flex items-center truncate">
|
|
||||||
<img className="w-7 h-7 rounded-full mr-2" src={DropdownImage02} width="28" height="28" alt="Channel 02" />
|
|
||||||
<div className="truncate">Acme Limited</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a className="font-medium text-sm text-slate-600 hover:text-slate-800 block py-1.5 px-3" href="#0" onClick={() => setDropdownOpen(false)}>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="grow flex items-center truncate">
|
|
||||||
<img className="w-7 h-7 rounded-full mr-2" src={DropdownImage03} width="28" height="28" alt="Channel 03" />
|
|
||||||
<div className="truncate">Acme Srl</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DropdownSwitch;
|
|
||||||
@@ -1,20 +1,27 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import Transition from '../utils/Transition';
|
import Transition from '../utils/Transition';
|
||||||
|
|
||||||
function DropdownTransaction({
|
function DropdownTransaction({ align, value, onChange }) {
|
||||||
align
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||||
|
|
||||||
const trigger = useRef(null);
|
const trigger = useRef(null);
|
||||||
const dropdown = useRef(null);
|
const dropdown = useRef(null);
|
||||||
|
|
||||||
|
const changeValue = (val) => {
|
||||||
|
setDropdownOpen(false);
|
||||||
|
onChange(val);
|
||||||
|
};
|
||||||
|
|
||||||
// close on click outside
|
// close on click outside
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const clickHandler = ({ target }) => {
|
const clickHandler = ({ target }) => {
|
||||||
if (!dropdown.current) return;
|
if (!dropdown.current) return;
|
||||||
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
|
if (
|
||||||
|
!dropdownOpen ||
|
||||||
|
dropdown.current.contains(target) ||
|
||||||
|
trigger.current.contains(target)
|
||||||
|
)
|
||||||
|
return;
|
||||||
setDropdownOpen(false);
|
setDropdownOpen(false);
|
||||||
};
|
};
|
||||||
document.addEventListener('click', clickHandler);
|
document.addEventListener('click', clickHandler);
|
||||||
@@ -37,12 +44,17 @@ function DropdownTransaction({
|
|||||||
ref={trigger}
|
ref={trigger}
|
||||||
className="inline-flex justify-center items-center group"
|
className="inline-flex justify-center items-center group"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
aria-expanded={dropdownOpen}
|
aria-expanded={dropdownOpen}
|
||||||
|
onClick={() => setDropdownOpen(!dropdownOpen)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center truncate">
|
<div className="flex items-center truncate">
|
||||||
<span className="truncate font-medium text-indigo-500 group-hover:text-indigo-600">My Personal Account</span>
|
<span className="truncate font-medium text-indigo-500 group-hover:text-indigo-600">
|
||||||
<svg className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400" viewBox="0 0 12 12">
|
{value}
|
||||||
|
</span>
|
||||||
|
<svg
|
||||||
|
className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400"
|
||||||
|
viewBox="0 0 12 12"
|
||||||
|
>
|
||||||
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
|
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,25 +72,27 @@ function DropdownTransaction({
|
|||||||
leaveStart="opacity-100"
|
leaveStart="opacity-100"
|
||||||
leaveEnd="opacity-0"
|
leaveEnd="opacity-0"
|
||||||
>
|
>
|
||||||
<div ref={dropdown} onFocus={() => setDropdownOpen(true)} onBlur={() => setDropdownOpen(false)}>
|
<div
|
||||||
|
ref={dropdown}
|
||||||
|
onFocus={() => setDropdownOpen(true)}
|
||||||
|
onBlur={() => setDropdownOpen(false)}
|
||||||
|
>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<span
|
||||||
className="font-medium text-sm text-slate-600 hover:text-slate-800 flex items-center py-1 px-3"
|
className="font-medium text-sm text-slate-600 hover:text-slate-800 flex items-center py-1 px-3 cursor-pointer"
|
||||||
href="#0"
|
onClick={() => changeValue('Voiting')}
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
>
|
>
|
||||||
Business Account
|
Voiting
|
||||||
</a>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<span
|
||||||
className="font-medium text-sm text-slate-600 hover:text-slate-800 flex items-center py-1 px-3"
|
className="font-medium text-sm text-slate-600 hover:text-slate-800 flex items-center py-1 px-3 cursor-pointer"
|
||||||
href="#0"
|
onClick={() => changeValue('Unlocking')}
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
||||||
>
|
>
|
||||||
Family Account
|
Unlocking
|
||||||
</a>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
31
src/components/Filters.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const Filters = ({ value, onChange, values = [] }) => {
|
||||||
|
return (
|
||||||
|
<div className="mb-5">
|
||||||
|
<ul className="flex flex-wrap -m-1">
|
||||||
|
{values.map((e) =>
|
||||||
|
e === value ? (
|
||||||
|
<li className="m-1" key={e}>
|
||||||
|
<button
|
||||||
|
className="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-transparent shadow-sm bg-indigo-500 text-white duration-150 ease-in-out"
|
||||||
|
onClick={() => onChange(e)}
|
||||||
|
>
|
||||||
|
{e}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
) : (
|
||||||
|
<li className="m-1" key={e}>
|
||||||
|
<button
|
||||||
|
className="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-slate-200 hover:border-slate-300 shadow-sm bg-white text-slate-500 duration-150 ease-in-out"
|
||||||
|
onClick={() => onChange(e)}
|
||||||
|
>
|
||||||
|
{e}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
17
src/components/LoadingOverlay.jsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import React from 'react';
|
||||||
|
import { ThreeCircles } from 'react-loader-spinner';
|
||||||
|
|
||||||
|
export const LoadingOverlay = () =>
|
||||||
|
ReactDOM.createPortal(
|
||||||
|
<div className="absolute left-0 top-0 h-screen w-screen z-50 loading-overlay">
|
||||||
|
<ThreeCircles
|
||||||
|
height="100"
|
||||||
|
width="100"
|
||||||
|
color="#6366f1"
|
||||||
|
visible={true}
|
||||||
|
ariaLabel="three-circles-rotating"
|
||||||
|
/>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function ModalAction({
|
|
||||||
children,
|
|
||||||
id,
|
|
||||||
modalOpen,
|
|
||||||
setModalOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const modalContent = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!modalOpen || modalContent.current.contains(target)) return
|
|
||||||
setModalOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!modalOpen || keyCode !== 27) return;
|
|
||||||
setModalOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Modal backdrop */}
|
|
||||||
<Transition
|
|
||||||
className="fixed inset-0 bg-slate-900 bg-opacity-30 z-50 transition-opacity"
|
|
||||||
show={modalOpen}
|
|
||||||
enter="transition ease-out duration-200"
|
|
||||||
enterStart="opacity-0"
|
|
||||||
enterEnd="opacity-100"
|
|
||||||
leave="transition ease-out duration-100"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
{/* Modal dialog */}
|
|
||||||
<Transition
|
|
||||||
id={id}
|
|
||||||
className="fixed inset-0 z-50 overflow-hidden flex items-center my-4 justify-center transform px-4 sm:px-6"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
show={modalOpen}
|
|
||||||
enter="transition ease-in-out duration-200"
|
|
||||||
enterStart="opacity-0 translate-y-4"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-in-out duration-200"
|
|
||||||
leaveStart="opacity-100 translate-y-0"
|
|
||||||
leaveEnd="opacity-0 translate-y-4"
|
|
||||||
>
|
|
||||||
<div ref={modalContent} className="bg-white rounded shadow-lg overflow-auto max-w-lg w-full max-h-full">
|
|
||||||
<div className="p-6">
|
|
||||||
<div className="relative">
|
|
||||||
{/* Close button */}
|
|
||||||
<button className="absolute top-0 right-0 text-slate-400 hover:text-slate-500" onClick={(e) => { e.stopPropagation(); setModalOpen(false); }}>
|
|
||||||
<div className="sr-only">Close</div>
|
|
||||||
<svg className="w-4 h-4 fill-current">
|
|
||||||
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ModalAction;
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function ModalBasic({
|
|
||||||
children,
|
|
||||||
id,
|
|
||||||
title,
|
|
||||||
modalOpen,
|
|
||||||
setModalOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const modalContent = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!modalOpen || modalContent.current.contains(target)) return
|
|
||||||
setModalOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!modalOpen || keyCode !== 27) return;
|
|
||||||
setModalOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Modal backdrop */}
|
|
||||||
<Transition
|
|
||||||
className="fixed inset-0 bg-slate-900 bg-opacity-30 z-50 transition-opacity"
|
|
||||||
show={modalOpen}
|
|
||||||
enter="transition ease-out duration-200"
|
|
||||||
enterStart="opacity-0"
|
|
||||||
enterEnd="opacity-100"
|
|
||||||
leave="transition ease-out duration-100"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
{/* Modal dialog */}
|
|
||||||
<Transition
|
|
||||||
id={id}
|
|
||||||
className="fixed inset-0 z-50 overflow-hidden flex items-center my-4 justify-center transform px-4 sm:px-6"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
show={modalOpen}
|
|
||||||
enter="transition ease-in-out duration-200"
|
|
||||||
enterStart="opacity-0 translate-y-4"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-in-out duration-200"
|
|
||||||
leaveStart="opacity-100 translate-y-0"
|
|
||||||
leaveEnd="opacity-0 translate-y-4"
|
|
||||||
>
|
|
||||||
<div ref={modalContent} className="bg-white rounded shadow-lg overflow-auto max-w-lg w-full max-h-full">
|
|
||||||
{/* Modal header */}
|
|
||||||
<div className="px-5 py-3 border-b border-slate-200">
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<div className="font-semibold text-slate-800">{title}</div>
|
|
||||||
<button className="text-slate-400 hover:text-slate-500" onClick={(e) => { e.stopPropagation(); setModalOpen(false); }}>
|
|
||||||
<div className="sr-only">Close</div>
|
|
||||||
<svg className="w-4 h-4 fill-current">
|
|
||||||
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ModalBasic;
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function ModalBlank({
|
|
||||||
children,
|
|
||||||
id,
|
|
||||||
modalOpen,
|
|
||||||
setModalOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const modalContent = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!modalOpen || modalContent.current.contains(target)) return
|
|
||||||
setModalOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!modalOpen || keyCode !== 27) return;
|
|
||||||
setModalOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Modal backdrop */}
|
|
||||||
<Transition
|
|
||||||
className="fixed inset-0 bg-slate-900 bg-opacity-30 z-50 transition-opacity"
|
|
||||||
show={modalOpen}
|
|
||||||
enter="transition ease-out duration-200"
|
|
||||||
enterStart="opacity-0"
|
|
||||||
enterEnd="opacity-100"
|
|
||||||
leave="transition ease-out duration-100"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
{/* Modal dialog */}
|
|
||||||
<Transition
|
|
||||||
id={id}
|
|
||||||
className="fixed inset-0 z-50 overflow-hidden flex items-center my-4 justify-center transform px-4 sm:px-6"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
show={modalOpen}
|
|
||||||
enter="transition ease-in-out duration-200"
|
|
||||||
enterStart="opacity-0 translate-y-4"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-in-out duration-200"
|
|
||||||
leaveStart="opacity-100 translate-y-0"
|
|
||||||
leaveEnd="opacity-0 translate-y-4"
|
|
||||||
>
|
|
||||||
<div ref={modalContent} className="bg-white rounded shadow-lg overflow-auto max-w-lg w-full max-h-full">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ModalBlank;
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function ModalCookies({
|
|
||||||
children,
|
|
||||||
id,
|
|
||||||
title,
|
|
||||||
modalOpen,
|
|
||||||
setModalOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const modalContent = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!modalOpen || modalContent.current.contains(target)) return
|
|
||||||
setModalOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!modalOpen || keyCode !== 27) return;
|
|
||||||
setModalOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Modal backdrop */}
|
|
||||||
<Transition
|
|
||||||
className="fixed inset-0 bg-slate-900 bg-opacity-30 z-50 transition-opacity"
|
|
||||||
show={modalOpen}
|
|
||||||
enter="transition ease-out duration-200"
|
|
||||||
enterStart="opacity-0"
|
|
||||||
enterEnd="opacity-100"
|
|
||||||
leave="transition ease-out duration-100"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
{/* Modal dialog */}
|
|
||||||
<Transition
|
|
||||||
id={id}
|
|
||||||
className="fixed inset-0 z-50 overflow-hidden flex items-center my-4 justify-center transform px-4 sm:px-6"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
show={modalOpen}
|
|
||||||
enter="transition ease-in-out duration-200"
|
|
||||||
enterStart="opacity-0 translate-y-4"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-in-out duration-200"
|
|
||||||
leaveStart="opacity-100 translate-y-0"
|
|
||||||
leaveEnd="opacity-0 translate-y-4"
|
|
||||||
>
|
|
||||||
<div ref={modalContent} className="bg-white rounded shadow-lg overflow-auto max-w-lg w-full max-h-full">
|
|
||||||
<div className="p-5">
|
|
||||||
{/* Modal header */}
|
|
||||||
<div className="mb-2">
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<div className="text-lg font-semibold text-slate-800">{title}</div>
|
|
||||||
<button className="text-slate-400 hover:text-slate-500" onClick={(e) => { e.stopPropagation(); setModalOpen(false); }}>
|
|
||||||
<div className="sr-only">Close</div>
|
|
||||||
<svg className="w-4 h-4 fill-current">
|
|
||||||
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ModalCookies;
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function ModalSearch({
|
|
||||||
id,
|
|
||||||
searchId,
|
|
||||||
modalOpen,
|
|
||||||
setModalOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const modalContent = useRef(null);
|
|
||||||
const searchInput = useRef(null);
|
|
||||||
|
|
||||||
// close on click outside
|
|
||||||
useEffect(() => {
|
|
||||||
const clickHandler = ({ target }) => {
|
|
||||||
if (!modalOpen || modalContent.current.contains(target)) return
|
|
||||||
setModalOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('click', clickHandler);
|
|
||||||
return () => document.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// close if the esc key is pressed
|
|
||||||
useEffect(() => {
|
|
||||||
const keyHandler = ({ keyCode }) => {
|
|
||||||
if (!modalOpen || keyCode !== 27) return;
|
|
||||||
setModalOpen(false);
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', keyHandler);
|
|
||||||
return () => document.removeEventListener('keydown', keyHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
modalOpen && searchInput.current.focus();
|
|
||||||
}, [modalOpen]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Modal backdrop */}
|
|
||||||
<Transition
|
|
||||||
className="fixed inset-0 bg-slate-900 bg-opacity-30 z-50 transition-opacity"
|
|
||||||
show={modalOpen}
|
|
||||||
enter="transition ease-out duration-200"
|
|
||||||
enterStart="opacity-0"
|
|
||||||
enterEnd="opacity-100"
|
|
||||||
leave="transition ease-out duration-100"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
{/* Modal dialog */}
|
|
||||||
<Transition
|
|
||||||
id={id}
|
|
||||||
className="fixed inset-0 z-50 overflow-hidden flex items-start top-20 mb-4 justify-center transform px-4 sm:px-6"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
show={modalOpen}
|
|
||||||
enter="transition ease-in-out duration-200"
|
|
||||||
enterStart="opacity-0 translate-y-4"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-in-out duration-200"
|
|
||||||
leaveStart="opacity-100 translate-y-0"
|
|
||||||
leaveEnd="opacity-0 translate-y-4"
|
|
||||||
>
|
|
||||||
<div ref={modalContent} className="bg-white overflow-auto max-w-2xl w-full max-h-full rounded shadow-lg">
|
|
||||||
{/* Search form */}
|
|
||||||
<form className="border-b border-slate-200">
|
|
||||||
<div className="relative">
|
|
||||||
<label htmlFor={searchId} className="sr-only">Search</label>
|
|
||||||
<input id={searchId} className="w-full border-0 focus:ring-transparent placeholder-slate-400 appearance-none py-3 pl-10 pr-4" type="search" placeholder="Search Anything…" ref={searchInput} />
|
|
||||||
<button className="absolute inset-0 right-auto group" type="submit" aria-label="Search">
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current text-slate-400 group-hover:text-slate-500 ml-4 mr-2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M7 14c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7zM7 2C4.243 2 2 4.243 2 7s2.243 5 5 5 5-2.243 5-5-2.243-5-5-5z" />
|
|
||||||
<path d="M15.707 14.293L13.314 11.9a8.019 8.019 0 01-1.414 1.414l2.393 2.393a.997.997 0 001.414 0 .999.999 0 000-1.414z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div className="py-4 px-2">
|
|
||||||
{/* Recent searches */}
|
|
||||||
<div className="mb-3 last:mb-0">
|
|
||||||
<div className="text-xs font-semibold text-slate-400 uppercase px-2 mb-2">Recent searches</div>
|
|
||||||
<ul className="text-sm">
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setModalOpen(!modalOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
|
|
||||||
</svg>
|
|
||||||
<span>Form Builder - 23 hours on-demand video</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setModalOpen(!modalOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
|
|
||||||
</svg>
|
|
||||||
<span>Access Mosaic on mobile and TV</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setModalOpen(!modalOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
|
|
||||||
</svg>
|
|
||||||
<span>Product Update - Q4 2021</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setModalOpen(!modalOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
|
|
||||||
</svg>
|
|
||||||
<span>Master Digital Marketing Strategy course</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setModalOpen(!modalOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
|
|
||||||
</svg>
|
|
||||||
<span>Dedicated forms for products</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setModalOpen(!modalOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
|
|
||||||
</svg>
|
|
||||||
<span>Product Update - Q4 2021</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{/* Recent pages */}
|
|
||||||
<div className="mb-3 last:mb-0">
|
|
||||||
<div className="text-xs font-semibold text-slate-400 uppercase px-2 mb-2">Recent pages</div>
|
|
||||||
<ul className="text-sm">
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setModalOpen(!modalOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M14 0H2c-.6 0-1 .4-1 1v14c0 .6.4 1 1 1h8l5-5V1c0-.6-.4-1-1-1zM3 2h10v8H9v4H3V2z" />
|
|
||||||
</svg>
|
|
||||||
<span><span className="font-medium text-slate-800 group-hover:text-white">Messages</span> - Conversation / … / Mike Mills</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
|
|
||||||
to="#0"
|
|
||||||
onClick={() => setModalOpen(!modalOpen)}
|
|
||||||
>
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M14 0H2c-.6 0-1 .4-1 1v14c0 .6.4 1 1 1h8l5-5V1c0-.6-.4-1-1-1zM3 2h10v8H9v4H3V2z" />
|
|
||||||
</svg>
|
|
||||||
<span><span className="font-medium text-slate-800 group-hover:text-white">Messages</span> - Conversation / … / Eva Patrick</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ModalSearch;
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function Notification({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
type,
|
|
||||||
open,
|
|
||||||
setOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const typeIcon = (type) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'warning':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current text-amber-500 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
case 'error':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current text-rose-500 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3.5 10.1l-1.4 1.4L8 9.4l-2.1 2.1-1.4-1.4L6.6 8 4.5 5.9l1.4-1.4L8 6.6l2.1-2.1 1.4 1.4L9.4 8l2.1 2.1z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
case 'success':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current text-emerald-500 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zM7 11.4L3.6 8 5 6.6l2 2 4-4L12.4 6 7 11.4z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current text-indigo-500 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm1 12H7V7h2v5zM8 6c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{open &&
|
|
||||||
<div className={className}>
|
|
||||||
<div className="inline-flex flex-col max-w-lg px-4 py-2 rounded-sm text-sm bg-white shadow-lg border border-slate-200 text-slate-600">
|
|
||||||
<div className="flex w-full justify-between items-start">
|
|
||||||
<div className="flex">
|
|
||||||
{typeIcon(type)}
|
|
||||||
<div>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button className="opacity-70 hover:opacity-80 ml-3 mt-[3px]" onClick={() => setOpen(false)}>
|
|
||||||
<div className="sr-only">Close</div>
|
|
||||||
<svg className="w-4 h-4 fill-current">
|
|
||||||
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="text-right mt-1">
|
|
||||||
<a className="font-medium text-indigo-500 hover:text-indigo-600" href="#0">Action -></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Notification;
|
|
||||||
14
src/components/NotificationContainer.jsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import { Toast } from './Toast2';
|
||||||
|
import { store } from '../store/store';
|
||||||
|
|
||||||
|
export const NotificationContainer = observer(() => (
|
||||||
|
<div className="absolute z-50 top-2 right-2 gap-2">
|
||||||
|
{store.notifications.map((e) => (
|
||||||
|
<Toast type={e.type} key={e.id} id={e.id}>
|
||||||
|
{e.message}
|
||||||
|
</Toast>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
));
|
||||||
@@ -1,20 +1,49 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
|
||||||
function PaginationClassic() {
|
function PaginationClassic({ count, limit, offset, onNextPage, onPrevPage }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
||||||
<nav className="mb-4 sm:mb-0 sm:order-1" role="navigation" aria-label="Navigation">
|
<nav
|
||||||
|
className="mb-4 sm:mb-0 sm:order-1"
|
||||||
|
role="navigation"
|
||||||
|
aria-label="Navigation"
|
||||||
|
>
|
||||||
<ul className="flex justify-center">
|
<ul className="flex justify-center">
|
||||||
<li className="ml-3 first:ml-0">
|
<li className="ml-3 first:ml-0">
|
||||||
<a className="btn bg-white border-slate-200 text-slate-300 cursor-not-allowed" href="#0" disabled><- Previous</a>
|
<button
|
||||||
|
className={cn(
|
||||||
|
'btn bg-white border-slate-200 text-indigo-500',
|
||||||
|
{ 'cursor-not-allowed text-slate-300': offset === 0 },
|
||||||
|
{ 'hover:border-slate-300': offset !== 0 }
|
||||||
|
)}
|
||||||
|
disabled={offset === 0}
|
||||||
|
onClick={onPrevPage}
|
||||||
|
>
|
||||||
|
<- Previous
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li className="ml-3 first:ml-0">
|
<li className="ml-3 first:ml-0">
|
||||||
<a className="btn bg-white border-slate-200 hover:border-slate-300 text-indigo-500" href="#0">Next -></a>
|
<button
|
||||||
|
className={cn(
|
||||||
|
'btn bg-white border-slate-200 text-indigo-500',
|
||||||
|
{
|
||||||
|
'cursor-not-allowed text-slate-300': offset + limit >= count,
|
||||||
|
},
|
||||||
|
{ 'hover:border-slate-300': offset + limit < count }
|
||||||
|
)}
|
||||||
|
disabled={offset + limit >= count}
|
||||||
|
onClick={onNextPage}
|
||||||
|
>
|
||||||
|
Next ->
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<div className="text-sm text-slate-500 text-center sm:text-left">
|
<div className="text-sm text-slate-500 text-center sm:text-left">
|
||||||
Showing <span className="font-medium text-slate-600">1</span> to <span className="font-medium text-slate-600">10</span> of <span className="font-medium text-slate-600">467</span> results
|
Showing <span className="font-medium text-slate-600">{offset + 1}</span>{' '}
|
||||||
|
to <span className="font-medium text-slate-600">{offset + limit}</span>{' '}
|
||||||
|
of <span className="font-medium text-slate-600">{count}</span> results
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function PaginationNumeric() {
|
|
||||||
return (
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<nav className="flex" role="navigation" aria-label="Navigation">
|
|
||||||
<div className="mr-2">
|
|
||||||
<span className="inline-flex items-center justify-center rounded leading-5 px-2.5 py-2 bg-white border border-slate-200 text-slate-300">
|
|
||||||
<span className="sr-only">Previous</span><wbr />
|
|
||||||
<svg className="h-4 w-4 fill-current" viewBox="0 0 16 16">
|
|
||||||
<path d="M9.4 13.4l1.4-1.4-4-4 4-4-1.4-1.4L4 8z" />
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<ul className="inline-flex text-sm font-medium -space-x-px shadow-sm">
|
|
||||||
<li>
|
|
||||||
<span className="inline-flex items-center justify-center rounded-l leading-5 px-3.5 py-2 bg-white border border-slate-200 text-indigo-500">1</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a className="inline-flex items-center justify-center leading-5 px-3.5 py-2 bg-white hover:bg-indigo-500 border border-slate-200 text-slate-600 hover:text-white" href="#0">2</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a className="inline-flex items-center justify-center leading-5 px-3.5 py-2 bg-white hover:bg-indigo-500 border border-slate-200 text-slate-600 hover:text-white" href="#0">3</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className="inline-flex items-center justify-center leading-5 px-3.5 py-2 bg-white border border-slate-200 text-slate-400">…</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a className="inline-flex items-center justify-center rounded-r leading-5 px-3.5 py-2 bg-white hover:bg-indigo-500 border border-slate-200 text-slate-600 hover:text-white" href="#0">9</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div className="ml-2">
|
|
||||||
<a href="#0" className="inline-flex items-center justify-center rounded leading-5 px-2.5 py-2 bg-white hover:bg-indigo-500 border border-slate-200 text-slate-600 hover:text-white shadow-sm">
|
|
||||||
<span className="sr-only">Next</span><wbr />
|
|
||||||
<svg className="h-4 w-4 fill-current" viewBox="0 0 16 16">
|
|
||||||
<path d="M6.6 13.4L5.2 12l4-4-4-4 1.4-1.4L12 8z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PaginationNumeric;
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function PaginationNumeric2() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<nav className="flex justify-between" role="navigation" aria-label="Navigation">
|
|
||||||
<div className="flex-1 mr-2">
|
|
||||||
<a className="btn bg-white border-slate-200 text-slate-300 cursor-not-allowed" href="#0" disabled><-<span className="hidden sm:inline"> Previous</span></a>
|
|
||||||
</div>
|
|
||||||
<div className="grow text-center">
|
|
||||||
<ul className="inline-flex text-sm font-medium -space-x-px">
|
|
||||||
<li>
|
|
||||||
<span className="inline-flex items-center justify-center rounded-full leading-5 px-2 py-2 bg-white border border-slate-200 text-indigo-500 shadow-sm"><span className="w-5">1</span></span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a className="inline-flex items-center justify-center leading-5 px-2 py-2 text-slate-600 hover:text-indigo-500 border border-transparent" href="#0"><span className="w-5">2</span></a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a className="inline-flex items-center justify-center leading-5 px-2 py-2 text-slate-600 hover:text-indigo-500 border border-transparent" href="#0"><span className="w-5">3</span></a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className="inline-flex items-center justify-center leading-5 px-2 py-2 text-slate-400">…</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a className="inline-flex items-center justify-center rounded-r leading-5 px-2 py-2 text-slate-600 hover:text-indigo-500 border border-transparent" href="#0"><span className="w-5">9</span></a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 text-right ml-2">
|
|
||||||
<a className="btn bg-white border-slate-200 hover:border-slate-300 text-indigo-500" href="#0"><span className="hidden sm:inline">Next </span>-></a>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PaginationNumeric2;
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function Toast({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
type,
|
|
||||||
open,
|
|
||||||
setOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const typeIcon = (type) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'warning':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
case 'error':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3.5 10.1l-1.4 1.4L8 9.4l-2.1 2.1-1.4-1.4L6.6 8 4.5 5.9l1.4-1.4L8 6.6l2.1-2.1 1.4 1.4L9.4 8l2.1 2.1z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
case 'success':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zM7 11.4L3.6 8 5 6.6l2 2 4-4L12.4 6 7 11.4z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm1 12H7V7h2v5zM8 6c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const typeColor = (type) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'warning':
|
|
||||||
return 'bg-amber-500';
|
|
||||||
case 'error':
|
|
||||||
return 'bg-rose-500';
|
|
||||||
case 'success':
|
|
||||||
return 'bg-emerald-500';
|
|
||||||
default:
|
|
||||||
return 'bg-indigo-500';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{open &&
|
|
||||||
<div className={className}>
|
|
||||||
<div className={`inline-flex min-w-80 px-4 py-2 rounded-sm text-sm text-white ${typeColor(type)}`}>
|
|
||||||
<div className="flex w-full justify-between items-start">
|
|
||||||
<div className="flex">
|
|
||||||
{typeIcon(type)}
|
|
||||||
<div className="font-medium">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button className="opacity-70 hover:opacity-80 ml-3 mt-[3px]" onClick={() => setOpen(false)}>
|
|
||||||
<div className="sr-only">Close</div>
|
|
||||||
<svg className="w-4 h-4 fill-current">
|
|
||||||
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Toast;
|
|
||||||
@@ -1,36 +1,43 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import { store } from '../store/store';
|
||||||
function Toast2({
|
import { observer } from 'mobx-react-lite';
|
||||||
children,
|
|
||||||
className,
|
|
||||||
type,
|
|
||||||
open,
|
|
||||||
setOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
|
export const Toast = observer(({ children, className, type, id }) => {
|
||||||
const typeIcon = (type) => {
|
const typeIcon = (type) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'warning':
|
case 'warning':
|
||||||
return (
|
return (
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
<svg
|
||||||
|
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
|
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
case 'error':
|
case 'error':
|
||||||
return (
|
return (
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
<svg
|
||||||
|
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3.5 10.1l-1.4 1.4L8 9.4l-2.1 2.1-1.4-1.4L6.6 8 4.5 5.9l1.4-1.4L8 6.6l2.1-2.1 1.4 1.4L9.4 8l2.1 2.1z" />
|
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3.5 10.1l-1.4 1.4L8 9.4l-2.1 2.1-1.4-1.4L6.6 8 4.5 5.9l1.4-1.4L8 6.6l2.1-2.1 1.4 1.4L9.4 8l2.1 2.1z" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
case 'success':
|
case 'success':
|
||||||
return (
|
return (
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
<svg
|
||||||
|
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zM7 11.4L3.6 8 5 6.6l2 2 4-4L12.4 6 7 11.4z" />
|
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zM7 11.4L3.6 8 5 6.6l2 2 4-4L12.4 6 7 11.4z" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3" viewBox="0 0 16 16">
|
<svg
|
||||||
|
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm1 12H7V7h2v5zM8 6c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z" />
|
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm1 12H7V7h2v5zM8 6c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
@@ -50,30 +57,34 @@ function Toast2({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
useEffect(() => {
|
||||||
<>
|
const timerId = setTimeout(() => store.removeNotification(id), 3000);
|
||||||
{open &&
|
return () => clearTimeout(timerId);
|
||||||
<div className={className}>
|
}, []);
|
||||||
<div className={`inline-flex min-w-80 px-4 py-2 rounded-sm text-sm border ${typeColor(type)}`}>
|
|
||||||
<div className="flex w-full justify-between items-start">
|
|
||||||
<div className="flex">
|
|
||||||
{typeIcon(type)}
|
|
||||||
<div>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button className="opacity-70 hover:opacity-80 ml-3 mt-[3px]" onClick={() => setOpen(false)}>
|
|
||||||
<div className="sr-only">Close</div>
|
|
||||||
<svg className="w-4 h-4 fill-current">
|
|
||||||
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Toast2;
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
<div
|
||||||
|
className={`inline-flex min-w-80 px-4 py-2 rounded-sm text-sm border ${typeColor(
|
||||||
|
type
|
||||||
|
)}`}
|
||||||
|
>
|
||||||
|
<div className="flex w-full justify-between items-start">
|
||||||
|
<div className="flex">
|
||||||
|
{typeIcon(type)}
|
||||||
|
<div>{children}</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="opacity-70 hover:opacity-80 ml-3 mt-[3px]"
|
||||||
|
onClick={() => store.removeNotification(id)}
|
||||||
|
>
|
||||||
|
<div className="sr-only">Close</div>
|
||||||
|
<svg className="w-4 h-4 fill-current">
|
||||||
|
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function Toast3({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
type,
|
|
||||||
open,
|
|
||||||
setOpen
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const typeIcon = (type) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'warning':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current text-amber-500 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
case 'error':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current text-rose-500 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3.5 10.1l-1.4 1.4L8 9.4l-2.1 2.1-1.4-1.4L6.6 8 4.5 5.9l1.4-1.4L8 6.6l2.1-2.1 1.4 1.4L9.4 8l2.1 2.1z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
case 'success':
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current text-emerald-500 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zM7 11.4L3.6 8 5 6.6l2 2 4-4L12.4 6 7 11.4z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<svg className="w-4 h-4 shrink-0 fill-current text-indigo-500 mt-[3px] mr-3" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm1 12H7V7h2v5zM8 6c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{open &&
|
|
||||||
<div className={className}>
|
|
||||||
<div className="inline-flex min-w-80 px-4 py-2 rounded-sm text-sm bg-white shadow-lg border border-slate-200 text-slate-600">
|
|
||||||
<div className="flex w-full justify-between items-start">
|
|
||||||
<div className="flex">
|
|
||||||
{typeIcon(type)}
|
|
||||||
<div>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button className="opacity-70 hover:opacity-80 ml-3 mt-[3px]" onClick={() => setOpen(false)}>
|
|
||||||
<div className="sr-only">Close</div>
|
|
||||||
<svg className="w-4 h-4 fill-current">
|
|
||||||
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Toast3;
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import Transition from '../utils/Transition';
|
|
||||||
|
|
||||||
function Tooltip({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
bg,
|
|
||||||
size,
|
|
||||||
position,
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const [tooltipOpen, setTooltipOpen] = useState(false);
|
|
||||||
|
|
||||||
const positionOuterClasses = (position) => {
|
|
||||||
switch (position) {
|
|
||||||
case 'right':
|
|
||||||
return 'left-full top-1/2 transform -translate-y-1/2';
|
|
||||||
case 'left':
|
|
||||||
return 'right-full top-1/2 transform -translate-y-1/2';
|
|
||||||
case 'bottom':
|
|
||||||
return 'top-full left-1/2 transform -translate-x-1/2';
|
|
||||||
default:
|
|
||||||
return 'bottom-full left-1/2 transform -translate-x-1/2';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const sizeClasses = (size) => {
|
|
||||||
switch (size) {
|
|
||||||
case 'lg':
|
|
||||||
return 'min-w-72 p-3';
|
|
||||||
case 'md':
|
|
||||||
return 'min-w-56 p-3';
|
|
||||||
case 'sm':
|
|
||||||
return 'min-w-44 p-2';
|
|
||||||
default:
|
|
||||||
return 'p-2';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const positionInnerClasses = (position) => {
|
|
||||||
switch (position) {
|
|
||||||
case 'right':
|
|
||||||
return 'ml-2';
|
|
||||||
case 'left':
|
|
||||||
return 'mr-2';
|
|
||||||
case 'bottom':
|
|
||||||
return 'mt-2';
|
|
||||||
default:
|
|
||||||
return 'mb-2';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`relative ${className}`}
|
|
||||||
onMouseEnter={() => setTooltipOpen(true)}
|
|
||||||
onMouseLeave={() => setTooltipOpen(false)}
|
|
||||||
onFocus={() => setTooltipOpen(true)}
|
|
||||||
onBlur={() => setTooltipOpen(false)}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="block"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded={tooltipOpen}
|
|
||||||
onClick={(e) => e.preventDefault()}
|
|
||||||
>
|
|
||||||
<svg className="w-4 h-4 fill-current text-slate-400" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<div className={`z-10 absolute ${positionOuterClasses(position)}`}>
|
|
||||||
<Transition
|
|
||||||
show={tooltipOpen}
|
|
||||||
tag="div"
|
|
||||||
className={`rounded overflow-hidden ${bg === 'dark' ? 'bg-slate-800' : 'bg-white border border-slate-200 shadow-lg'} ${sizeClasses(size)} ${positionInnerClasses(position)}`}
|
|
||||||
enter="transition ease-out duration-200 transform"
|
|
||||||
enterStart="opacity-0 -translate-y-2"
|
|
||||||
enterEnd="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-out duration-200"
|
|
||||||
leaveStart="opacity-100"
|
|
||||||
leaveEnd="opacity-0"
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Tooltip;
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
// Customise flatpickr
|
// Customise flatpickr
|
||||||
$calendarPadding: 24px;
|
$calendarPadding: 24px;
|
||||||
$daySize: 36px;
|
$daySize: 36px;
|
||||||
$daysWidth: $daySize*7;
|
$daysWidth: $daySize * 7;
|
||||||
|
|
||||||
@keyframes fpFadeInDown {
|
@keyframes fpFadeInDown {
|
||||||
from {
|
from {
|
||||||
@@ -18,59 +18,59 @@ $daysWidth: $daySize*7;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-calendar {
|
.flatpickr-calendar {
|
||||||
border: inherit;
|
border: inherit;
|
||||||
@apply rounded shadow-lg border border-slate-200 left-1/2;
|
@apply rounded shadow-lg border border-slate-200 left-1/2;
|
||||||
margin-left: - ($daysWidth + $calendarPadding*2)*0.5;
|
margin-left: -($daysWidth + $calendarPadding * 2) * 0.5;
|
||||||
padding: $calendarPadding;
|
padding: $calendarPadding;
|
||||||
width: $daysWidth + $calendarPadding*2;
|
width: $daysWidth + $calendarPadding * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@screen lg {
|
@screen lg {
|
||||||
.flatpickr-calendar {
|
.flatpickr-calendar {
|
||||||
@apply left-0 right-auto;
|
@apply left-0 right-auto;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-right.flatpickr-calendar {
|
.flatpickr-right.flatpickr-calendar {
|
||||||
@apply right-0 left-auto;
|
@apply right-0 left-auto;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-calendar.animate.open {
|
.flatpickr-calendar.animate.open {
|
||||||
animation: fpFadeInDown 200ms ease-out;
|
animation: fpFadeInDown 200ms ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-calendar.static {
|
.flatpickr-calendar.static {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(100% + 4px);
|
top: calc(100% + 4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-calendar.static.open {
|
.flatpickr-calendar.static.open {
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-days {
|
.flatpickr-days {
|
||||||
width: $daysWidth;
|
width: $daysWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dayContainer {
|
.dayContainer {
|
||||||
width: $daysWidth;
|
width: $daysWidth;
|
||||||
min-width: $daysWidth;
|
min-width: $daysWidth;
|
||||||
max-width: $daysWidth;
|
max-width: $daysWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-day {
|
.flatpickr-day {
|
||||||
@apply bg-slate-50 text-sm font-medium text-slate-600;
|
@apply bg-slate-50 text-sm font-medium text-slate-600;
|
||||||
max-width: $daySize;
|
max-width: $daySize;
|
||||||
height: $daySize;
|
height: $daySize;
|
||||||
line-height: $daySize;
|
line-height: $daySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-day,
|
.flatpickr-day,
|
||||||
.flatpickr-day.prevMonthDay,
|
.flatpickr-day.prevMonthDay,
|
||||||
.flatpickr-day.nextMonthDay {
|
.flatpickr-day.nextMonthDay {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-day,
|
.flatpickr-day,
|
||||||
@@ -85,7 +85,7 @@ $daysWidth: $daySize*7;
|
|||||||
.flatpickr-day.selected.startRange.endRange,
|
.flatpickr-day.selected.startRange.endRange,
|
||||||
.flatpickr-day.startRange.startRange.endRange,
|
.flatpickr-day.startRange.startRange.endRange,
|
||||||
.flatpickr-day.endRange.startRange.endRange {
|
.flatpickr-day.endRange.startRange.endRange {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-day.flatpickr-disabled,
|
.flatpickr-day.flatpickr-disabled,
|
||||||
@@ -95,11 +95,11 @@ $daysWidth: $daySize*7;
|
|||||||
.flatpickr-day.notAllowed,
|
.flatpickr-day.notAllowed,
|
||||||
.flatpickr-day.notAllowed.prevMonthDay,
|
.flatpickr-day.notAllowed.prevMonthDay,
|
||||||
.flatpickr-day.notAllowed.nextMonthDay {
|
.flatpickr-day.notAllowed.nextMonthDay {
|
||||||
@apply text-slate-400;
|
@apply text-slate-400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rangeMode .flatpickr-day {
|
.rangeMode .flatpickr-day {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-day.selected,
|
.flatpickr-day.selected,
|
||||||
@@ -120,7 +120,7 @@ $daysWidth: $daySize*7;
|
|||||||
.flatpickr-day.selected.nextMonthDay,
|
.flatpickr-day.selected.nextMonthDay,
|
||||||
.flatpickr-day.startRange.nextMonthDay,
|
.flatpickr-day.startRange.nextMonthDay,
|
||||||
.flatpickr-day.endRange.nextMonthDay {
|
.flatpickr-day.endRange.nextMonthDay {
|
||||||
@apply bg-indigo-500 text-indigo-50;
|
@apply bg-indigo-500 text-indigo-50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-day.inRange,
|
.flatpickr-day.inRange,
|
||||||
@@ -137,92 +137,92 @@ $daysWidth: $daySize*7;
|
|||||||
.flatpickr-day.nextMonthDay:focus,
|
.flatpickr-day.nextMonthDay:focus,
|
||||||
.flatpickr-day.today:hover,
|
.flatpickr-day.today:hover,
|
||||||
.flatpickr-day.today:focus {
|
.flatpickr-day.today:focus {
|
||||||
@apply bg-indigo-400 text-indigo-50;
|
@apply bg-indigo-400 text-indigo-50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-day.inRange,
|
.flatpickr-day.inRange,
|
||||||
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),
|
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n + 1)),
|
||||||
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),
|
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n + 1)),
|
||||||
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) {
|
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n + 1)) {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-months {
|
.flatpickr-months {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: -8px;
|
margin-top: -8px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-months .flatpickr-prev-month,
|
.flatpickr-months .flatpickr-prev-month,
|
||||||
.flatpickr-months .flatpickr-next-month {
|
.flatpickr-months .flatpickr-next-month {
|
||||||
position: static;
|
position: static;
|
||||||
height: auto;
|
height: auto;
|
||||||
@apply text-slate-600;
|
@apply text-slate-600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-months .flatpickr-prev-month svg,
|
.flatpickr-months .flatpickr-prev-month svg,
|
||||||
.flatpickr-months .flatpickr-next-month svg {
|
.flatpickr-months .flatpickr-next-month svg {
|
||||||
width: 7px;
|
width: 7px;
|
||||||
height: 11px;
|
height: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-months .flatpickr-prev-month:hover,
|
.flatpickr-months .flatpickr-prev-month:hover,
|
||||||
.flatpickr-months .flatpickr-next-month:hover,
|
.flatpickr-months .flatpickr-next-month:hover,
|
||||||
.flatpickr-months .flatpickr-prev-month:hover svg,
|
.flatpickr-months .flatpickr-prev-month:hover svg,
|
||||||
.flatpickr-months .flatpickr-next-month:hover svg {
|
.flatpickr-months .flatpickr-next-month:hover svg {
|
||||||
fill: inherit;
|
fill: inherit;
|
||||||
@apply text-slate-400;
|
@apply text-slate-400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-months .flatpickr-prev-month {
|
.flatpickr-months .flatpickr-prev-month {
|
||||||
margin-left: -10px;
|
margin-left: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-months .flatpickr-next-month {
|
.flatpickr-months .flatpickr-next-month {
|
||||||
margin-right: -10px;
|
margin-right: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-months .flatpickr-month {
|
.flatpickr-months .flatpickr-month {
|
||||||
@apply text-slate-800;
|
@apply text-slate-800;
|
||||||
height: auto;
|
height: auto;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-current-month {
|
.flatpickr-current-month {
|
||||||
@apply text-sm font-medium;
|
@apply text-sm font-medium;
|
||||||
position: static;
|
position: static;
|
||||||
height: auto;
|
height: auto;
|
||||||
width: auto;
|
width: auto;
|
||||||
left: auto;
|
left: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-current-month span.cur-month {
|
.flatpickr-current-month span.cur-month {
|
||||||
@apply font-medium m-0;
|
@apply font-medium m-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-current-month span.cur-month:hover {
|
.flatpickr-current-month span.cur-month:hover {
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-current-month input.cur-year {
|
.flatpickr-current-month input.cur-year {
|
||||||
font-weight: inherit;
|
font-weight: inherit;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.numInputWrapper:hover {
|
.numInputWrapper:hover {
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.numInputWrapper span {
|
.numInputWrapper span {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.flatpickr-weekday {
|
span.flatpickr-weekday {
|
||||||
@apply text-slate-400 font-medium text-xs;
|
@apply text-slate-400 font-medium text-xs;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flatpickr-calendar.arrowTop::before,
|
.flatpickr-calendar.arrowTop::before,
|
||||||
.flatpickr-calendar.arrowTop::after {
|
.flatpickr-calendar.arrowTop::after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -1,55 +1,55 @@
|
|||||||
// Range slider
|
// Range slider
|
||||||
$range-thumb-size: 36px;
|
$range-thumb-size: 36px;
|
||||||
|
|
||||||
input[type=range] {
|
input[type='range'] {
|
||||||
|
appearance: none;
|
||||||
|
background: #ccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
height: 6px;
|
||||||
|
margin-top: ($range-thumb-size - 6px) * 0.5;
|
||||||
|
margin-bottom: ($range-thumb-size - 6px) * 0.5;
|
||||||
|
--thumb-size: #{$range-thumb-size};
|
||||||
|
|
||||||
|
&::-webkit-slider-thumb {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
background: #ccc;
|
-webkit-appearance: none;
|
||||||
border-radius: 3px;
|
background-color: #000;
|
||||||
height: 6px;
|
background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 .5v7L12 4zM0 4l4 3.5v-7z' fill='%23FFF' fill-rule='nonzero'/%3E%3C/svg%3E");
|
||||||
margin-top: ($range-thumb-size - 6px) * 0.5;
|
background-position: center;
|
||||||
margin-bottom: ($range-thumb-size - 6px) * 0.5;
|
background-repeat: no-repeat;
|
||||||
--thumb-size: #{$range-thumb-size};
|
border: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
height: $range-thumb-size;
|
||||||
|
width: $range-thumb-size;
|
||||||
|
}
|
||||||
|
|
||||||
&::-webkit-slider-thumb {
|
&::-moz-range-thumb {
|
||||||
appearance: none;
|
background-color: #000;
|
||||||
-webkit-appearance: none;
|
background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 .5v7L12 4zM0 4l4 3.5v-7z' fill='%23FFF' fill-rule='nonzero'/%3E%3C/svg%3E");
|
||||||
background-color: #000;
|
background-position: center;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 .5v7L12 4zM0 4l4 3.5v-7z' fill='%23FFF' fill-rule='nonzero'/%3E%3C/svg%3E");
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
border: 0;
|
||||||
background-repeat: no-repeat;
|
border: none;
|
||||||
border: 0;
|
border-radius: 50%;
|
||||||
border-radius: 50%;
|
cursor: pointer;
|
||||||
cursor: pointer;
|
height: $range-thumb-size;
|
||||||
height: $range-thumb-size;
|
width: $range-thumb-size;
|
||||||
width: $range-thumb-size;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-range-thumb {
|
&::-ms-thumb {
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 .5v7L12 4zM0 4l4 3.5v-7z' fill='%23FFF' fill-rule='nonzero'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 .5v7L12 4zM0 4l4 3.5v-7z' fill='%23FFF' fill-rule='nonzero'/%3E%3C/svg%3E");
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
border: 0;
|
border: 0;
|
||||||
border: none;
|
border-radius: 50%;
|
||||||
border-radius: 50%;
|
cursor: pointer;
|
||||||
cursor: pointer;
|
height: $range-thumb-size;
|
||||||
height: $range-thumb-size;
|
width: $range-thumb-size;
|
||||||
width: $range-thumb-size;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&::-ms-thumb {
|
&::-moz-focus-outer {
|
||||||
background-color: #000;
|
border: 0;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 .5v7L12 4zM0 4l4 3.5v-7z' fill='%23FFF' fill-rule='nonzero'/%3E%3C/svg%3E");
|
}
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
cursor: pointer;
|
|
||||||
height: $range-thumb-size;
|
|
||||||
width: $range-thumb-size;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-focus-outer {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,14 @@
|
|||||||
.form-select,
|
.form-select,
|
||||||
.form-checkbox,
|
.form-checkbox,
|
||||||
.form-radio {
|
.form-radio {
|
||||||
|
&:focus {
|
||||||
&:focus {
|
@apply ring-0;
|
||||||
@apply ring-0;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading-overlay {
|
||||||
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,41 @@
|
|||||||
// Switch element
|
// Switch element
|
||||||
.form-switch {
|
.form-switch {
|
||||||
@apply relative select-none;
|
@apply relative select-none;
|
||||||
width: 44px;
|
width: 44px;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
@apply block overflow-hidden cursor-pointer h-6 rounded-full;
|
@apply block overflow-hidden cursor-pointer h-6 rounded-full;
|
||||||
|
|
||||||
|
> span:first-child {
|
||||||
|
@apply absolute block rounded-full;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
right: 50%;
|
||||||
|
transition: all 0.15s ease-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='checkbox'] {
|
||||||
|
&:checked {
|
||||||
|
+ label {
|
||||||
|
@apply bg-indigo-500;
|
||||||
|
|
||||||
> span:first-child {
|
> span:first-child {
|
||||||
@apply absolute block rounded-full;
|
left: 22px;
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
top: 2px;
|
|
||||||
left: 2px;
|
|
||||||
right: 50%;
|
|
||||||
transition: all .15s ease-out;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"] {
|
&:disabled {
|
||||||
|
+ label {
|
||||||
|
@apply cursor-not-allowed bg-slate-100 border border-slate-200;
|
||||||
|
|
||||||
&:checked {
|
> span:first-child {
|
||||||
|
@apply bg-slate-400;
|
||||||
+ label {
|
|
||||||
@apply bg-indigo-500;
|
|
||||||
|
|
||||||
> span:first-child {
|
|
||||||
left: 22px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
|
|
||||||
+ label {
|
|
||||||
@apply cursor-not-allowed bg-slate-100 border border-slate-200;
|
|
||||||
|
|
||||||
> span:first-child {
|
|
||||||
@apply bg-slate-400;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,28 +1,28 @@
|
|||||||
// Typography
|
// Typography
|
||||||
.h1 {
|
.h1 {
|
||||||
@apply text-4xl font-extrabold tracking-tighter;
|
@apply text-4xl font-extrabold tracking-tighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h2 {
|
.h2 {
|
||||||
@apply text-3xl font-extrabold tracking-tighter;
|
@apply text-3xl font-extrabold tracking-tighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h3 {
|
.h3 {
|
||||||
@apply text-3xl font-extrabold;
|
@apply text-3xl font-extrabold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h4 {
|
.h4 {
|
||||||
@apply text-2xl font-extrabold tracking-tight;
|
@apply text-2xl font-extrabold tracking-tight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@screen md {
|
@screen md {
|
||||||
.h1 {
|
.h1 {
|
||||||
@apply text-5xl;
|
@apply text-5xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h2 {
|
.h2 {
|
||||||
@apply text-4xl;
|
@apply text-4xl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
@@ -30,30 +30,30 @@
|
|||||||
.btn-lg,
|
.btn-lg,
|
||||||
.btn-sm,
|
.btn-sm,
|
||||||
.btn-xs {
|
.btn-xs {
|
||||||
@apply font-medium text-sm inline-flex items-center justify-center border border-transparent rounded leading-5 shadow-sm transition duration-150 ease-in-out;
|
@apply font-medium text-sm inline-flex items-center justify-center border border-transparent rounded leading-5 shadow-sm transition duration-150 ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
@apply px-3 py-2;
|
@apply px-3 py-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-lg {
|
.btn-lg {
|
||||||
@apply px-4 py-3;
|
@apply px-4 py-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-sm {
|
.btn-sm {
|
||||||
@apply px-2 py-1;
|
@apply px-2 py-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-xs {
|
.btn-xs {
|
||||||
@apply px-2 py-0.5;
|
@apply px-2 py-0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forms
|
// Forms
|
||||||
input[type="search"]::-webkit-search-decoration,
|
input[type='search']::-webkit-search-decoration,
|
||||||
input[type="search"]::-webkit-search-cancel-button,
|
input[type='search']::-webkit-search-cancel-button,
|
||||||
input[type="search"]::-webkit-search-results-button,
|
input[type='search']::-webkit-search-results-button,
|
||||||
input[type="search"]::-webkit-search-results-decoration {
|
input[type='search']::-webkit-search-results-decoration {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ input[type="search"]::-webkit-search-results-decoration {
|
|||||||
.form-select,
|
.form-select,
|
||||||
.form-checkbox,
|
.form-checkbox,
|
||||||
.form-radio {
|
.form-radio {
|
||||||
@apply text-sm text-slate-800 bg-white border;
|
@apply text-sm text-slate-800 bg-white border;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input,
|
.form-input,
|
||||||
@@ -71,28 +71,28 @@ input[type="search"]::-webkit-search-results-decoration {
|
|||||||
.form-multiselect,
|
.form-multiselect,
|
||||||
.form-select,
|
.form-select,
|
||||||
.form-checkbox {
|
.form-checkbox {
|
||||||
@apply rounded;
|
@apply rounded;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input,
|
.form-input,
|
||||||
.form-textarea,
|
.form-textarea,
|
||||||
.form-multiselect,
|
.form-multiselect,
|
||||||
.form-select {
|
.form-select {
|
||||||
@apply leading-5 py-2 px-3 border-slate-200 hover:border-slate-300 focus:border-indigo-300 shadow-sm;
|
@apply leading-5 py-2 px-3 border-slate-200 hover:border-slate-300 focus:border-indigo-300 shadow-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input,
|
.form-input,
|
||||||
.form-textarea {
|
.form-textarea {
|
||||||
@apply placeholder-slate-400;
|
@apply placeholder-slate-400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-select {
|
.form-select {
|
||||||
@apply pr-10;
|
@apply pr-10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-checkbox,
|
.form-checkbox,
|
||||||
.form-radio {
|
.form-radio {
|
||||||
@apply text-indigo-500 border border-slate-300;
|
@apply text-indigo-500 border border-slate-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Chrome, Safari and Opera */
|
/* Chrome, Safari and Opera */
|
||||||
@@ -101,6 +101,6 @@ input[type="search"]::-webkit-search-results-decoration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.no-scrollbar {
|
.no-scrollbar {
|
||||||
-ms-overflow-style: none; /* IE and Edge */
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
scrollbar-width: none; /* Firefox */
|
scrollbar-width: none; /* Firefox */
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
|
|
||||||
// Additional styles
|
|
||||||
@import 'additional-styles/utility-patterns.scss';
|
@import 'additional-styles/utility-patterns.scss';
|
||||||
@import 'additional-styles/range-slider.scss';
|
@import 'additional-styles/range-slider.scss';
|
||||||
@import 'additional-styles/toggle-switch.scss';
|
@import 'additional-styles/toggle-switch.scss';
|
||||||
|
|||||||
112
src/images/GenerateOnboardingSvg/GenerateSvg.js
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
function createBlob(options) {
|
||||||
|
let points = [];
|
||||||
|
let slice = (Math.PI * 2) / options.numPoints;
|
||||||
|
let startAngle = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < options.subkey.length; i++) {
|
||||||
|
let angle = startAngle + i * slice;
|
||||||
|
let rnd = Number('0x' + options.subkey.substring(i + 0, i + 1));
|
||||||
|
let point = {
|
||||||
|
x: options.centerX + Math.cos(angle) * (options.radius + rnd),
|
||||||
|
y: options.centerY + Math.sin(angle) * (options.radius + rnd),
|
||||||
|
};
|
||||||
|
points.push(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = points.length;
|
||||||
|
let path = 'M' + points[0].x + ' ' + points[0].y + ' C';
|
||||||
|
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
let p0 = points[(i - 1 + size) % size];
|
||||||
|
let p1 = points[i];
|
||||||
|
let p2 = points[(i + 1) % size];
|
||||||
|
let p3 = points[(i + 2) % size];
|
||||||
|
|
||||||
|
let x1 = p1.x + (p2.x - p0.x) / 6;
|
||||||
|
let y1 = p1.y + (p2.y - p0.y) / 6;
|
||||||
|
|
||||||
|
let x2 = p2.x - (p3.x - p1.x) / 6;
|
||||||
|
let y2 = p2.y - (p3.y - p1.y) / 6;
|
||||||
|
|
||||||
|
path += ' ' + x1 + ' ' + y1 + ' ' + x2 + ' ' + y2 + ' ' + p2.x + ' ' + p2.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path + 'z';
|
||||||
|
}
|
||||||
|
|
||||||
|
export var generateSvgAvatar = (function () {
|
||||||
|
return function (publicKey, seed, raw) {
|
||||||
|
let pubKey =
|
||||||
|
publicKey ||
|
||||||
|
Math.floor(Math.random() * 65535 * 65535).toString(16) +
|
||||||
|
Math.floor(Math.random() * 65535 * 65535).toString(16) +
|
||||||
|
Math.floor(Math.random() * 65535 * 65535).toString(16) +
|
||||||
|
Math.floor(Math.random() * 65535 * 65535).toString(16) +
|
||||||
|
Math.floor(Math.random() * 65535 * 65535).toString(16) +
|
||||||
|
Math.floor(Math.random() * 65535 * 65535).toString(16) +
|
||||||
|
Math.floor(Math.random() * 65535 * 65535).toString(16) +
|
||||||
|
Math.floor(Math.random() * 65535 * 65535).toString(16);
|
||||||
|
//pubKey = "73f47d4929d7b2b6598ca4999d52567f9bb5aef3e353e0a66b468096db6e4e88"
|
||||||
|
let particles = [];
|
||||||
|
let gradients = ['#000000'];
|
||||||
|
let defs = [];
|
||||||
|
|
||||||
|
for (let a = 0; a < 64; a = a + 8) {
|
||||||
|
let blob = createBlob({
|
||||||
|
numPoints: 8,
|
||||||
|
centerX: 256,
|
||||||
|
centerY: 256 - a * 4,
|
||||||
|
radius: 16,
|
||||||
|
subkey: pubKey.substring(a, a + 8),
|
||||||
|
});
|
||||||
|
|
||||||
|
let color = '#' + pubKey.substring(a + 2, a + 8);
|
||||||
|
let width = Number('0x' + pubKey.substring(a, a + 1));
|
||||||
|
let opacity = Number('0x' + pubKey.substring(a + 1, a + 2)) / 8;
|
||||||
|
|
||||||
|
gradients.push(color);
|
||||||
|
defs.push(
|
||||||
|
`<radialGradient id="a-${a}" cx="0" cy="0" r="0.75" fx="0.35" fy="0.5" spreadMethod="reflect"><stop offset="0%" stop-color="${
|
||||||
|
gradients[gradients.length - 1]
|
||||||
|
}"/><stop offset="100%" stop-color="${
|
||||||
|
gradients[gradients.length]
|
||||||
|
}"/></radialGradient>`
|
||||||
|
);
|
||||||
|
particles.push(
|
||||||
|
`<path d="${blob}" stroke="url(#a-${a})" stroke-width="${width}" stroke-opacity = "${opacity}" fill="transparent"/>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ph = '';
|
||||||
|
let off = 4 + Number('0x' + pubKey.substring(0, 1));
|
||||||
|
let rotate = 0;
|
||||||
|
for (var i = 0; i < off; i++) {
|
||||||
|
ph =
|
||||||
|
ph +
|
||||||
|
`<use xlink:href="#pattern-${pubKey}" mask="url(#mask)" class="segment" style="transform-origin:center center;transform: scaleY(1) rotate(${rotate}deg)"></use>,` +
|
||||||
|
'\n';
|
||||||
|
rotate = rotate + 360 / off;
|
||||||
|
}
|
||||||
|
|
||||||
|
var svg = [
|
||||||
|
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" preserveAspectRatio="xMidYMid slice" viewBox="0 0 512 512">',
|
||||||
|
'<defs>',
|
||||||
|
'<radialGradient id="e" cx="0" cy="0" r="0.75" fx="0.35" fy="0.5" spreadMethod="reflect">',
|
||||||
|
'<stop offset="0%" stop-color="red"/>',
|
||||||
|
'<stop offset="100%" stop-color="blue"/>',
|
||||||
|
'</radialGradient>',
|
||||||
|
defs.join('\n'),
|
||||||
|
'</defs>',
|
||||||
|
'<symbol id="pattern-' + pubKey + '">',
|
||||||
|
particles.join(''),
|
||||||
|
'</symbol>',
|
||||||
|
'<g style="transform-origin:center center; transform: rotate(' +
|
||||||
|
pubKey +
|
||||||
|
'deg)">',
|
||||||
|
ph,
|
||||||
|
'</g>',
|
||||||
|
'</svg>',
|
||||||
|
].join('');
|
||||||
|
return raw ? svg : 'data:image/svg+xml;base64,' + btoa(svg);
|
||||||
|
};
|
||||||
|
})();
|
||||||
10
src/images/profile-icon.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<circle cx="18" cy="18" r="18" fill="#E1E1F7"/>
|
||||||
|
<rect x="6" y="6" width="24" height="24" fill="url(#pattern0)"/>
|
||||||
|
<defs>
|
||||||
|
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||||
|
<use xlink:href="#image0_21_1525" transform="scale(0.0125)"/>
|
||||||
|
</pattern>
|
||||||
|
<image id="image0_21_1525" width="80" height="80" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAUKADAAQAAAABAAAAUAAAAAAx4ExPAAAHHUlEQVR4Ae1bWYwVRRRlwBGXaBRxRQaMGvDDiAG/lCigjHEDE/VHPox8uGCi4o8mij/G6Iea6JcJ0TCSEDVKYhC3EOPglhgMGoMxKswQBRWXuG8snjNTd7xTr+u9qq7q9/pp3eRQ1bfvPffW7br9+r0eJkzIkiuQK5ArkCuQK5ArkCuQK5ArkCuQK5ArkCvw/6pAT5uWOw1xLgQWAWcCUw3+xrgL2ApsAJ4B/gJccixOLAb6gdnACQB1oTxw6Q45G2k+DewFDnhgGDaXA7acAcU6YB8Qw2Pz1vZ4MjJ7TC32d8yfB24B5gLckbQ5AmBxbgC2ACzOfuA2gNILPAroC8CdeitwDjAdeAt4FyDPjYDmoV3XyYnIWBbBwj0AsNVaCW8nKwEWkFgGvAGwqH8C24BBwJbNUGg9eW4HyMEd2w90jXBHvQ9w0Z8Ac4BQuQcOuk3/wPF5oSSwF54fMX8N4MU5Eqi1vIDsuHjuliklM50EvyFAF/ErHJ8LiLBtufNs0XofHtu/o8eXIDoXvQfoS5DJ4eBYAGwCyPsLMBug2G07qi3Wc9dtBHYDNo/4dXzkfecjk+CKxNlwJw0Y7rcjuMmzNgFPRApu13kmsSGMB7nNSp/hbmQbcwfpViahblseixTpuRv5wVbEI35B48Qga7fxEnNqPUY+cqSWX0G42pAutcj5acuC2FKk/wlG7xlDm8f2b+uxPG7wW0JVsgjELFRMGzO3VDzkSiafgomLOy0ZYyPRSSbGFxiL2pMePnrNQ58oSdXC8qDM+1RV8oMh5uNRUXvytI/+e8Vjpp0f5MZ8aIWpHAZu7nLeD2PkYDin4BnJIdUObMdVlQdz2YlSRJ+2FVuOfCak2Dyj2sB/UxVQkjkmMH6IuXDLxRJfn7YVW47ylGDzaJu2zzcjItviggojy6fn65Exzoc/c43lGUkj1Q78zixKdok5TDoIN3dOaNtq+2kmqyQ7MFUBd5qkTk5asvFk080hY4W2rbaXHCXn8VECj1J97Ro2cWcExg8xF+4hOPH3viKZX6SETuvlQgw5bIPUqXbgDhN1ZlD0MOM+Yy4XS7x1e4qOo0svxbR5tK/3PFUBvzQRZ3pHDjeUHbjLctXtqU+59EcZI5tH+7Z9/iwiHgA+rzDydhODb+5iJBVPTA4Nvj9DwwLyfUhVQm7GYCxXe/roNU90rqlaWD7RTo/OyE0g3Izlak8fveZxR2vzmUcQj7tjVYVxyc0YD0fGSMUTmcZ4d3kfwgUKxluUPxI+GS+2qHzali7iL6PNY9H6HaZqYX5Bvx+Q78SMzl89YkVzkPs+4GWL1Kdte5WPi0eZdHb6McLzKp+SII1TDRdfk8bIDDgzp1iehhxS7UBNLEnO0sqSc+EQTqHxbVuxf9xMbB45X3qsooDvmGyuKZ3Vv47CIZxyxqdtxZZjnzmwebRNbeb8eZ9/osY3YMdFZHU8fPnMRy7Oywpz4Ev5WJ6y8Uv5DcCL95znSnmPOq03HGsMR2jbiv06i8fQ1XvgHz3uMYnfXSLVe43vNxinGn/+aDto5npopudFJDSP9q31vB/Z8S+ruICHAjLlgzJ96HtRgJ9t+iAUKXhs3rYeX2YWwYX4Cm2JSwFpQ9vXR695bP9kx1V8CuvkNuiDwPmLsA/9tC2yJ09Xi+wE30WE2rt4U/G4+Ef0Ve/ApsEDT/q0bSBlvHmVBeTD66qIFOlLDpH9mHBX2eLSi53NI/pajj3Iir/MvALsA6SNQt4/0Fb8yPESUObT2OZhTsyNOdZO+HbvWuADQBbPv2FZAywAQnY6bekzAJBD+LZivgzoBXzamTwLAfLILiUXc2Suqd5Igqq8TIbrCmAHIAv9DPOVgLy8wbS0kOMOgO9ahH8Y873Aq4AtzR6qt8B4JyA82zFn7lxDR+RKRNUL4w65CuCVTy3kvBpgDF0AxgsR/r54HbANEB6ugWtpm8xCpE2AJPAh5lcAPUDVwhiMxZgSfzfm3Hm2NGtznlsKaB6uiWurVHgPkjdw32J+EzCp0ojF5Ix5M8AcWEje45YDWljUQa0wc623ebg23h+TC+8TqwG56k9hfnTyKOGEU+CyFpC8nsD8kHCakf8UpHm4Vq45iTAhPkowSX4qXg/UTZYjIfnEfhNz/S6FuTZrZ+5IEc2zEcoyF0O4xkZeVRbva+CsMW39JsyNOTJX7iAtLFKrdhb7Zjxi4z32w5IJ/QbUuXiyoDkmV+a8UJQlRs2zuIT/mAtfITKZO8c09Z/cZXLmT/i6PSVz33YWHt6+nDLReWb0xFxz/skWdnU6Lbn2IilefFv4ie2jF555NkHIMQMVBQvh6IRtqrxb8tTi+2AbKsy25c6bb8VqpbfMGw9bfXvoxt3XuMp4jbNOre6B8aEzQ65ArkCuQK5ArkCuQK5ArkCuQK5ArkCuQK5ArsB/qgL/AIxZGoBhTSTCAAAAAElFTkSuQmCC"/>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
BIN
src/images/service_icon.png
Normal file
|
After Width: | Height: | Size: 912 B |
BIN
src/images/vpn_server_icon.png
Normal file
|
After Width: | Height: | Size: 623 B |
BIN
src/images/vpn_service.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
40
src/main.jsx
@@ -1,13 +1,29 @@
|
|||||||
import React from 'react'
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom';
|
||||||
import { BrowserRouter as Router } from 'react-router-dom'
|
import { Buffer } from 'buffer';
|
||||||
import App from './App'
|
import { HashRouter as Router } from 'react-router-dom';
|
||||||
|
import App from './App';
|
||||||
|
import { store } from './store/store';
|
||||||
|
import { fetchWrapper } from './shared/fetchWrapper';
|
||||||
|
|
||||||
ReactDOM.render(
|
globalThis.Buffer = Buffer;
|
||||||
<React.StrictMode>
|
|
||||||
<Router>
|
window.store = store;
|
||||||
<App />
|
|
||||||
</Router>
|
BigInt.prototype.toJSON = function () {
|
||||||
</React.StrictMode>,
|
return this.toString();
|
||||||
document.getElementById('root')
|
};
|
||||||
)
|
|
||||||
|
fetch('settings.json')
|
||||||
|
.then((e) => e.json())
|
||||||
|
.then((data) => (fetchWrapper.baseUrl = data.API_URL))
|
||||||
|
.then(() =>
|
||||||
|
ReactDOM.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<Router>
|
||||||
|
<App />
|
||||||
|
</Router>
|
||||||
|
</React.StrictMode>,
|
||||||
|
document.getElementById('root')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
import Sidebar from '../partials/Sidebar';
|
|
||||||
import Header from '../partials/Header';
|
|
||||||
import Datepicker from '../components/Datepicker';
|
|
||||||
import AnalyticsCard01 from '../partials/analytics/AnalyticsCard01';
|
|
||||||
import AnalyticsCard02 from '../partials/analytics/AnalyticsCard02';
|
|
||||||
import AnalyticsCard03 from '../partials/analytics/AnalyticsCard03';
|
|
||||||
import AnalyticsCard04 from '../partials/analytics/AnalyticsCard04';
|
|
||||||
import AnalyticsCard05 from '../partials/analytics/AnalyticsCard05';
|
|
||||||
import AnalyticsCard06 from '../partials/analytics/AnalyticsCard06';
|
|
||||||
import AnalyticsCard07 from '../partials/analytics/AnalyticsCard07';
|
|
||||||
import AnalyticsCard08 from '../partials/analytics/AnalyticsCard08';
|
|
||||||
import AnalyticsCard09 from '../partials/analytics/AnalyticsCard09';
|
|
||||||
import AnalyticsCard10 from '../partials/analytics/AnalyticsCard10';
|
|
||||||
import AnalyticsCard11 from '../partials/analytics/AnalyticsCard11';
|
|
||||||
|
|
||||||
function Analytics() {
|
|
||||||
|
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex h-screen overflow-hidden">
|
|
||||||
|
|
||||||
{/* Sidebar */}
|
|
||||||
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
|
|
||||||
|
|
||||||
{/* Content area */}
|
|
||||||
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden">
|
|
||||||
|
|
||||||
{/* Site header */}
|
|
||||||
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
|
|
||||||
|
|
||||||
{/* Page header */}
|
|
||||||
<div className="sm:flex sm:justify-between sm:items-center mb-8">
|
|
||||||
|
|
||||||
{/* Left: Title */}
|
|
||||||
<div className="mb-4 sm:mb-0">
|
|
||||||
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">Analytics ✨</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right: Actions */}
|
|
||||||
<div className="grid grid-flow-col sm:auto-cols-max justify-start sm:justify-end gap-2">
|
|
||||||
|
|
||||||
{/* Datepicker built with flatpickr */}
|
|
||||||
<Datepicker align="right" />
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Cards */}
|
|
||||||
<div className="grid grid-cols-12 gap-6">
|
|
||||||
|
|
||||||
{/* Line chart (Analytics) */}
|
|
||||||
<AnalyticsCard01 />
|
|
||||||
{/* Line chart (Active Users Right Now) */}
|
|
||||||
<AnalyticsCard02 />
|
|
||||||
{/* Stacked bar chart (Acquisition Channels) */}
|
|
||||||
<AnalyticsCard03 />
|
|
||||||
{/* Horizontal bar chart (Audience Overview) */}
|
|
||||||
<AnalyticsCard04 />
|
|
||||||
{/* Report card (Top Channels) */}
|
|
||||||
<AnalyticsCard05 />
|
|
||||||
{/* Report card (Top Pages) */}
|
|
||||||
<AnalyticsCard06 />
|
|
||||||
{/* Report card (Top Countries) */}
|
|
||||||
<AnalyticsCard07 />
|
|
||||||
{/* Doughnut chart (Sessions By Device) */}
|
|
||||||
<AnalyticsCard08 />
|
|
||||||
{/* Doughnut chart (Visit By Age Category) */}
|
|
||||||
<AnalyticsCard09 />
|
|
||||||
{/* Polar chart (Sessions By Gender) */}
|
|
||||||
<AnalyticsCard10 />
|
|
||||||
{/* Table (Top Products) */}
|
|
||||||
<AnalyticsCard11 />
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Analytics;
|
|
||||||
@@ -1,461 +0,0 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
|
|
||||||
import Sidebar from '../partials/Sidebar';
|
|
||||||
import Header from '../partials/Header';
|
|
||||||
|
|
||||||
function Calendar() {
|
|
||||||
|
|
||||||
const today = new Date();
|
|
||||||
const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
|
|
||||||
const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
|
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
||||||
const [month, setMonth] = useState(today.getMonth());
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const [year, setYear] = useState(today.getFullYear());
|
|
||||||
const [daysInMonth, setDaysInMonth] = useState([])
|
|
||||||
const [startingBlankDays, setStartingBlankDays] = useState([])
|
|
||||||
const [endingBlankDays, setEndingBlankDays] = useState([])
|
|
||||||
const events = [
|
|
||||||
// Previous month
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth() - 1, 8, 3),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth() - 1, 8, 7),
|
|
||||||
eventName: '⛱️ Relax for 2 at Marienbad',
|
|
||||||
eventColor: 'indigo'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth() - 1, 12, 10),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 12, 11),
|
|
||||||
eventName: 'Team Catch-up',
|
|
||||||
eventColor: 'sky'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth() - 1, 18, 2),
|
|
||||||
eventEnd: '',
|
|
||||||
eventName: '✍️ New Project (2)',
|
|
||||||
eventColor: 'yellow'
|
|
||||||
},
|
|
||||||
// Current month
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 1, 10),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 1, 11),
|
|
||||||
eventName: 'Meeting w/ Patrick Lin',
|
|
||||||
eventColor: 'sky'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 1, 19),
|
|
||||||
eventEnd: '',
|
|
||||||
eventName: 'Reservation at La Ginestre',
|
|
||||||
eventColor: 'indigo'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 3, 9),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 3, 10),
|
|
||||||
eventName: '✍️ New Project',
|
|
||||||
eventColor: 'yellow'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 7, 21),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 7, 22),
|
|
||||||
eventName: '⚽ 2021 - Semi-final',
|
|
||||||
eventColor: 'red'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 9, 10),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 9, 11),
|
|
||||||
eventName: 'Meeting w/Carolyn',
|
|
||||||
eventColor: 'sky'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 9, 13),
|
|
||||||
eventEnd: '',
|
|
||||||
eventName: 'Pick up Marta at school',
|
|
||||||
eventColor: 'emerald'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 9, 14),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 9, 15),
|
|
||||||
eventName: 'Meeting w/ Patrick Lin',
|
|
||||||
eventColor: 'emerald'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 9, 19),
|
|
||||||
eventEnd: '',
|
|
||||||
eventName: 'Reservation at La Ginestre',
|
|
||||||
eventColor: 'indigo'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 11, 10),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 11, 11),
|
|
||||||
eventName: '⛱️ Relax for 2 at Marienbad',
|
|
||||||
eventColor: 'indigo'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 11, 19),
|
|
||||||
eventEnd: '',
|
|
||||||
eventName: '⚽ 2021 - Semi-final',
|
|
||||||
eventColor: 'red'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 14, 10),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 14, 11),
|
|
||||||
eventName: 'Team Catch-up',
|
|
||||||
eventColor: 'sky'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 21, 2),
|
|
||||||
eventEnd: '',
|
|
||||||
eventName: 'Pick up Marta at school',
|
|
||||||
eventColor: 'emerald'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 21, 3),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 21, 7),
|
|
||||||
eventName: '✍️ New Project (2)',
|
|
||||||
eventColor: 'yellow'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 22, 10),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 22, 11),
|
|
||||||
eventName: 'Team Catch-up',
|
|
||||||
eventColor: 'sky'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 22, 19),
|
|
||||||
eventEnd: '',
|
|
||||||
eventName: '⚽ 2021 - Semi-final',
|
|
||||||
eventColor: 'red'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 23, 0),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 23, 23),
|
|
||||||
eventName: 'You stay at Meridiana B&B',
|
|
||||||
eventColor: 'indigo'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 25, 10),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 25, 11),
|
|
||||||
eventName: 'Meeting w/ Kylie Joh',
|
|
||||||
eventColor: 'sky'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth(), 29, 10),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 29, 11),
|
|
||||||
eventName: 'Call Request ->',
|
|
||||||
eventColor: 'sky'
|
|
||||||
},
|
|
||||||
// Next month
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 2, 3),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 2, 7),
|
|
||||||
eventName: '✍️ New Project (2)',
|
|
||||||
eventColor: 'yellow'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 14, 10),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth(), 14, 11),
|
|
||||||
eventName: 'Team Catch-up',
|
|
||||||
eventColor: 'sky'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 25, 2),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 25, 3),
|
|
||||||
eventName: 'Pick up Marta at school',
|
|
||||||
eventColor: 'emerald'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
eventStart: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 27, 21),
|
|
||||||
eventEnd: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 27, 22),
|
|
||||||
eventName: '⚽ 2021 - Semi-final',
|
|
||||||
eventColor: 'red'
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const isToday = (date) => {
|
|
||||||
const day = new Date(year, month, date);
|
|
||||||
return today.toDateString() === day.toDateString() ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getEvents = (date) => {
|
|
||||||
return events.filter(e => new Date(e.eventStart).toDateString() === new Date(year, month, date).toDateString());
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventColor = (color) => {
|
|
||||||
switch (color) {
|
|
||||||
case 'sky':
|
|
||||||
return 'text-white bg-sky-500';
|
|
||||||
case 'indigo':
|
|
||||||
return 'text-white bg-indigo-500';
|
|
||||||
case 'yellow':
|
|
||||||
return 'text-white bg-amber-500';
|
|
||||||
case 'emerald':
|
|
||||||
return 'text-white bg-emerald-500';
|
|
||||||
case 'red':
|
|
||||||
return 'text-white bg-rose-400';
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDays = () => {
|
|
||||||
const days = new Date(year, month + 1, 0).getDate();
|
|
||||||
|
|
||||||
// starting empty cells (previous month)
|
|
||||||
const startingDayOfWeek = new Date(year, month).getDay();
|
|
||||||
let startingBlankDaysArray = [];
|
|
||||||
for (let i = 1; i <= startingDayOfWeek; i++) {
|
|
||||||
startingBlankDaysArray.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ending empty cells (next month)
|
|
||||||
const endingDayOfWeek = new Date(year, month + 1, 0).getDay();
|
|
||||||
let endingBlankDaysArray = [];
|
|
||||||
for (let i = 1; i < 7 - endingDayOfWeek; i++) {
|
|
||||||
endingBlankDaysArray.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// current month cells
|
|
||||||
let daysArray = [];
|
|
||||||
for (let i = 1; i <= days; i++) {
|
|
||||||
daysArray.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
setStartingBlankDays(startingBlankDaysArray);
|
|
||||||
setEndingBlankDays(endingBlankDaysArray);
|
|
||||||
setDaysInMonth(daysArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getDays();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex h-screen overflow-hidden">
|
|
||||||
|
|
||||||
{/* Sidebar */}
|
|
||||||
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
|
|
||||||
|
|
||||||
{/* Content area */}
|
|
||||||
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden">
|
|
||||||
|
|
||||||
{/* Site header */}
|
|
||||||
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
|
|
||||||
|
|
||||||
{/* Page header */}
|
|
||||||
<div className="sm:flex sm:justify-between sm:items-center mb-4">
|
|
||||||
|
|
||||||
{/* Left: Title */}
|
|
||||||
<div className="mb-4 sm:mb-0">
|
|
||||||
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold"><span>{`${monthNames[month]} ${year}`}</span> ✨</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right: Actions */}
|
|
||||||
<div className="grid grid-flow-col sm:auto-cols-max justify-start sm:justify-end gap-2">
|
|
||||||
|
|
||||||
{/* Previous month button */}
|
|
||||||
<button
|
|
||||||
className="btn px-2.5 bg-white border-slate-200 hover:border-slate-300 text-slate-500 hover:text-slate-600 disabled:border-slate-200 disabled:bg-slate-100 disabled:text-slate-400 disabled:cursor-not-allowed"
|
|
||||||
disabled={month === 0 ? true : false}
|
|
||||||
onClick={() => {setMonth(month - 1);getDays();}}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Previous month</span><wbr />
|
|
||||||
<svg className="h-4 w-4 fill-current" viewBox="0 0 16 16">
|
|
||||||
<path d="M9.4 13.4l1.4-1.4-4-4 4-4-1.4-1.4L4 8z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* Next month button */}
|
|
||||||
<button
|
|
||||||
className="btn px-2.5 bg-white border-slate-200 hover:border-slate-300 text-slate-500 hover:text-slate-600 disabled:border-slate-200 disabled:bg-slate-100 disabled:text-slate-400 disabled:cursor-not-allowed"
|
|
||||||
disabled={month === 11 ? true : false}
|
|
||||||
onClick={() => {setMonth(month + 1);getDays();}}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Next month</span><wbr />
|
|
||||||
<svg className="h-4 w-4 fill-current" viewBox="0 0 16 16">
|
|
||||||
<path d="M6.6 13.4L5.2 12l4-4-4-4 1.4-1.4L12 8z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<hr className="w-px h-full bg-slate-200 mx-1" />
|
|
||||||
|
|
||||||
{/* Create event button */}
|
|
||||||
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white">
|
|
||||||
<svg className="w-4 h-4 fill-current opacity-50 shrink-0" viewBox="0 0 16 16">
|
|
||||||
<path d="M15 7H9V1c0-.6-.4-1-1-1S7 .4 7 1v6H1c-.6 0-1 .4-1 1s.4 1 1 1h6v6c0 .6.4 1 1 1s1-.4 1-1V9h6c.6 0 1-.4 1-1s-.4-1-1-1z" />
|
|
||||||
</svg>
|
|
||||||
<span className="hidden xs:block ml-2">Create Event</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Filters and view buttons */}
|
|
||||||
<div className="sm:flex sm:justify-between sm:items-center mb-4">
|
|
||||||
|
|
||||||
{/* Filters */}
|
|
||||||
<div className="mb-4 sm:mb-0 mr-2">
|
|
||||||
<ul className="flex flex-wrap items-center -m-1">
|
|
||||||
<li className="m-1">
|
|
||||||
<button className="btn-sm bg-white border-slate-200 hover:border-slate-300 text-slate-500">
|
|
||||||
<div className="w-1 h-3.5 bg-sky-500 shrink-0"></div>
|
|
||||||
<span className="ml-1.5">Acme Inc.</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li className="m-1">
|
|
||||||
<button className="btn-sm bg-white border-slate-200 hover:border-slate-300 text-slate-500">
|
|
||||||
<div className="w-1 h-3.5 bg-emerald-500 shrink-0"></div>
|
|
||||||
<span className="ml-1.5">Life & Family</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li className="m-1">
|
|
||||||
<button className="btn-sm bg-white border-slate-200 hover:border-slate-300 text-slate-500">
|
|
||||||
<div className="w-1 h-3.5 bg-indigo-500 shrink-0"></div>
|
|
||||||
<span className="ml-1.5">Reservations</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li className="m-1">
|
|
||||||
<button className="btn-sm bg-white border-slate-200 hover:border-slate-300 text-slate-500">
|
|
||||||
<div className="w-1 h-3.5 bg-rose-400 shrink-0"></div>
|
|
||||||
<span className="ml-1.5">Events</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li className="m-1">
|
|
||||||
<button className="btn-sm bg-white border-slate-200 hover:border-slate-300 text-slate-500">
|
|
||||||
<div className="w-1 h-3.5 bg-amber-500 shrink-0"></div>
|
|
||||||
<span className="ml-1.5">Misc</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li className="m-1">
|
|
||||||
<button className="btn-sm bg-white border-slate-200 hover:border-slate-300 text-indigo-500">+Add New</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* View buttons (requires custom integration) */}
|
|
||||||
<div className="flex flex-nowrap -space-x-px">
|
|
||||||
<button className="btn bg-slate-50 border-slate-200 hover:bg-slate-50 text-indigo-500 rounded-none first:rounded-l last:rounded-r">Month</button>
|
|
||||||
<button className="btn bg-white border-slate-200 hover:bg-slate-50 text-slate-600 rounded-none first:rounded-l last:rounded-r">Week</button>
|
|
||||||
<button className="btn bg-white border-slate-200 hover:bg-slate-50 text-slate-600 rounded-none first:rounded-l last:rounded-r">Day</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Calendar table */}
|
|
||||||
<div className="bg-white rounded-sm shadow overflow-hidden">
|
|
||||||
|
|
||||||
{/* Days of the week */}
|
|
||||||
<div className="grid grid-cols-7 gap-px border-b border-slate-200">
|
|
||||||
{
|
|
||||||
dayNames.map(day => {
|
|
||||||
return (
|
|
||||||
<div className="px-1 py-3" key={day}>
|
|
||||||
<div className="text-slate-500 text-sm font-medium text-center lg:hidden">{day.substring(0,3)}</div>
|
|
||||||
<div className="text-slate-500 text-sm font-medium text-center hidden lg:block">{day}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Day cells */}
|
|
||||||
<div className="grid grid-cols-7 gap-px bg-slate-200">
|
|
||||||
{/* Diagonal stripes pattern */}
|
|
||||||
<svg className="sr-only">
|
|
||||||
<defs>
|
|
||||||
<pattern id="stripes" patternUnits="userSpaceOnUse" width="5" height="5" patternTransform="rotate(135)">
|
|
||||||
<line className="stroke-current text-slate-200 opacity-50" x1="0" y="0" x2="0" y2="5" strokeWidth="2" />
|
|
||||||
</pattern>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
{/* Empty cells (previous month) */}
|
|
||||||
{
|
|
||||||
startingBlankDays.map(blankday => {
|
|
||||||
return (
|
|
||||||
<div className="bg-slate-50 h-20 sm:h-28 lg:h-36" key={blankday}>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
|
|
||||||
<rect width="100%" height="100%" fill="url(#stripes)" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
{/* Days of the current month */}
|
|
||||||
{
|
|
||||||
daysInMonth.map(day => {
|
|
||||||
return (
|
|
||||||
<div className="relative bg-white h-20 sm:h-28 lg:h-36 overflow-hidden" key={day}>
|
|
||||||
<div className="h-full flex flex-col justify-between">
|
|
||||||
{/* Events */}
|
|
||||||
<div className="grow flex flex-col relative p-0.5 sm:p-1.5 overflow-hidden">
|
|
||||||
{
|
|
||||||
getEvents(day).map(event => {
|
|
||||||
return (
|
|
||||||
<button className="relative w-full text-left mb-1" key={event.eventName}>
|
|
||||||
<div className={`px-2 py-0.5 rounded overflow-hidden ${eventColor(event.eventColor)}`}>
|
|
||||||
{/* Event name */}
|
|
||||||
<div className="text-xs font-semibold truncate">{event.eventName}</div>
|
|
||||||
{/* Event time */}
|
|
||||||
<div className="text-xs uppercase truncate hidden sm:block">
|
|
||||||
{/* Start date */}
|
|
||||||
{event.eventStart &&
|
|
||||||
<span>{event.eventStart.toLocaleTimeString([], {hour12: true, hour: 'numeric', minute:'numeric'})}</span>
|
|
||||||
}
|
|
||||||
{/* End date */}
|
|
||||||
{event.eventEnd &&
|
|
||||||
<span>
|
|
||||||
- <span>{event.eventEnd.toLocaleTimeString([], {hour12: true, hour: 'numeric', minute:'numeric'})}</span>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
<div className="absolute bottom-0 left-0 right-0 h-4 bg-gradient-to-t from-white to-transparent pointer-events-none" aria-hidden="true"></div>
|
|
||||||
</div>
|
|
||||||
{/* Cell footer */}
|
|
||||||
<div className="flex justify-between items-center p-0.5 sm:p-1.5">
|
|
||||||
{/* More button (if more than 2 events) */}
|
|
||||||
{getEvents(day).length > 2 &&
|
|
||||||
<button className="text-xs text-slate-500 font-medium whitespace-nowrap text-center sm:py-0.5 px-0.5 sm:px-2 border border-slate-200 rounded">
|
|
||||||
<span className="md:hidden">+</span><span>{getEvents(day).length - 2}</span> <span className="hidden md:inline">more</span>
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
{/* Day number */}
|
|
||||||
<button className={`inline-flex ml-auto w-6 h-6 items-center justify-center text-xs sm:text-sm font-medium text-center rounded-full hover:bg-indigo-100 ${isToday(day) && 'text-indigo-500'}`}>{day}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
{/* Empty cells (next month) */}
|
|
||||||
{
|
|
||||||
endingBlankDays.map(blankday => {
|
|
||||||
return (
|
|
||||||
<div className="bg-slate-50 h-20 sm:h-28 lg:h-36" key={blankday}>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
|
|
||||||
<rect width="100%" height="100%" fill="url(#stripes)" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Calendar;
|
|
||||||