mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
update totp and profile style
This commit is contained in:
parent
1920d89b49
commit
556fc69365
6 changed files with 184 additions and 43 deletions
File diff suppressed because one or more lines are too long
|
|
@ -1,3 +1,5 @@
|
|||
import { Tabs, Popover } from "./utils/settings.js";
|
||||
|
||||
class SubmitProfile {
|
||||
constructor() {
|
||||
this.pwEl = document.querySelector("#admin_password");
|
||||
|
|
@ -41,7 +43,7 @@ class SubmitProfile {
|
|||
"focus:valid:!border-red-500",
|
||||
"active:!border-red-500",
|
||||
"active:valid:!border-red-500",
|
||||
"valid:!border-red-500",
|
||||
"valid:!border-red-500"
|
||||
);
|
||||
this.pwAlertEl.classList.add("opacity-0");
|
||||
this.pwAlertEl.setAttribute("aria-hidden", "true");
|
||||
|
|
@ -53,7 +55,7 @@ class SubmitProfile {
|
|||
"focus:valid:!border-red-500",
|
||||
"active:!border-red-500",
|
||||
"active:valid:!border-red-500",
|
||||
"valid:!border-red-500",
|
||||
"valid:!border-red-500"
|
||||
);
|
||||
this.pwAlertEl.classList.remove("opacity-0");
|
||||
this.pwAlertEl.setAttribute("aria-hidden", "false");
|
||||
|
|
@ -71,14 +73,14 @@ class PwBtn {
|
|||
const passwordContainer = e.target.closest("[data-input-group]");
|
||||
const inpEl = passwordContainer.querySelector("input");
|
||||
const invBtn = passwordContainer.querySelector(
|
||||
'[data-setting-password="invisible"]',
|
||||
'[data-setting-password="invisible"]'
|
||||
);
|
||||
const visBtn = passwordContainer.querySelector(
|
||||
'[data-setting-password="visible"]',
|
||||
'[data-setting-password="visible"]'
|
||||
);
|
||||
inpEl.setAttribute(
|
||||
"type",
|
||||
inpEl.getAttribute("type") === "password" ? "text" : "password",
|
||||
inpEl.getAttribute("type") === "password" ? "text" : "password"
|
||||
);
|
||||
|
||||
if (inpEl.getAttribute("type") === "password") {
|
||||
|
|
@ -96,3 +98,5 @@ class PwBtn {
|
|||
|
||||
const setPWBtn = new PwBtn();
|
||||
const setSubmit = new SubmitProfile();
|
||||
const setTabs = new Tabs();
|
||||
const setPopover = new Popover();
|
||||
|
|
|
|||
2
src/ui/templates/base.html
vendored
2
src/ui/templates/base.html
vendored
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
<!-- info -->
|
||||
<main
|
||||
class="xl:pl-75 w-full px-2 sm:px-6 pb-0 pt-20 sm:pt-6 min-h-[80vh] h-full flex flex-col justify-between"
|
||||
class="xl:pl-75 w-full px-2 sm:px-6 pb-0 pt-20 sm:pt-6 min-h-[85vh] h-full flex flex-col justify-between"
|
||||
>
|
||||
<div
|
||||
class="max-w-[1920px] grid gap-y-4 gap-3 sm:gap-4 lg:gap-6 grid-cols-12 w-full"
|
||||
|
|
|
|||
2
src/ui/templates/head.html
vendored
2
src/ui/templates/head.html
vendored
|
|
@ -44,6 +44,6 @@
|
|||
{% elif current_endpoint == "jobs" %}
|
||||
<script type="module" src="./js/jobs.js"></script>
|
||||
{% elif current_endpoint == "profile" %}
|
||||
<script defer src="./js/profile.js"></script>
|
||||
<script type="module" src="./js/profile.js"></script>
|
||||
{% endif %}
|
||||
</head>
|
||||
|
|
|
|||
193
src/ui/templates/profile.html
vendored
193
src/ui/templates/profile.html
vendored
|
|
@ -2,12 +2,12 @@
|
|||
url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
||||
|
||||
<div
|
||||
class="w-full overflow-hidden overflow-y-auto overflow-x-auto max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
class="h-fit w-full overflow-hidden overflow-y-auto overflow-x-auto col-span-12 md:col-span-6 2xl:col-span-4 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="my-2 font-bold dark:text-white/90 mx-2">PROFILE</h5>
|
||||
<div class="grid grid-cols-12 justify-items-center w-full">
|
||||
<div
|
||||
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
class="flex flex-col relative col-span-12 px-4 md:px-6 mb-4 md:mb-3 lg:px-6 lg:mb-1 max-w-[400px] w-full"
|
||||
>
|
||||
<h5
|
||||
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
|
||||
|
|
@ -31,10 +31,131 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
</div>
|
||||
|
||||
<div
|
||||
class="w-full overflow-hidden overflow-y-auto overflow-x-auto max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
data-service-content
|
||||
class="w-full overflow-hidden overflow-y-auto overflow-x-auto col-span-12 lg:col-span-6 2xl:col-span-8 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="my-2 font-bold dark:text-white/90 mx-2">EDIT PROFILE</h5>
|
||||
<!-- desktop tabs -->
|
||||
<div
|
||||
role="tablist"
|
||||
data-{{current_endpoint}}-tabs-desktop
|
||||
class="hidden md:block col-span-12 mb-4"
|
||||
>
|
||||
<!-- tabs -->
|
||||
<button
|
||||
role="tab"
|
||||
data-tab-handler="profile"
|
||||
class="active settings-tabs-tab-btn"
|
||||
>
|
||||
<span class="w-full flex justify-between items-center">
|
||||
<!-- text and icon -->
|
||||
<span class="settings-tabs-name"> User </span>
|
||||
<svg
|
||||
data-popover-btn="profile"
|
||||
class="fill-blue-500 h-5 w-5 mr-2 hover:brightness-95"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z"
|
||||
/>
|
||||
</svg>
|
||||
<!-- end text and icon -->
|
||||
<!-- popover -->
|
||||
<span
|
||||
data-popover-content="profile"
|
||||
class="settings-tabs-popover-container hidden"
|
||||
>
|
||||
<span class="settings-tabs-popover-text"
|
||||
>Update profile data (username, password...)</span
|
||||
>
|
||||
</span>
|
||||
<!-- end popover -->
|
||||
</span>
|
||||
</button>
|
||||
<button role="tab" data-tab-handler="totp" class="settings-tabs-tab-btn">
|
||||
<span class="w-full flex justify-between items-center">
|
||||
<!-- text and icon -->
|
||||
<span class="settings-tabs-name"> TOTP </span>
|
||||
<svg
|
||||
data-popover-btn="totp"
|
||||
class="fill-blue-500 h-5 w-5 mr-2 hover:brightness-95"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z"
|
||||
/>
|
||||
</svg>
|
||||
<!-- end text and icon -->
|
||||
<!-- popover -->
|
||||
<span
|
||||
data-popover-content="totp"
|
||||
class="settings-tabs-popover-container hidden"
|
||||
>
|
||||
<span class="settings-tabs-popover-text"
|
||||
>Enabled / Disabled TOTP
|
||||
</span>
|
||||
</span>
|
||||
<!-- end popover -->
|
||||
</span>
|
||||
</button>
|
||||
<!--end tabs-->
|
||||
</div>
|
||||
<!-- end desktop tabs -->
|
||||
<!-- mobile tabs -->
|
||||
<div class="md:hidden relative col-span-12 mb-4 mt-2 mx-2">
|
||||
<button
|
||||
data-tab-dropdown-btn
|
||||
aria-controls="tab-dropdown-mobile"
|
||||
class="settings-tabs-mobile-btn"
|
||||
>
|
||||
<span aria-description="current tab" class="settings-tabs-mobile-btn-text"
|
||||
>Profile
|
||||
</span>
|
||||
<!-- chevron -->
|
||||
<svg
|
||||
class="transition-transform h-4 w-4 fill-primary dark:fill-gray-300"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"
|
||||
/>
|
||||
</svg>
|
||||
<!-- end chevron -->
|
||||
</button>
|
||||
<!-- dropdown-->
|
||||
<div
|
||||
id="tab-dropdown-mobile"
|
||||
role="listbox"
|
||||
data-tab-dropdown
|
||||
class="hidden z-100 absolute flex-col w-full overflow-hidden overflow-y-auto max-h-90"
|
||||
>
|
||||
<button
|
||||
role="option"
|
||||
data-tab-handler-mobile="profile"
|
||||
data-select="false"
|
||||
id="edit-{{current_endpoint}}-profile-tab"
|
||||
class="active first settings-tabs-mobile-dropdown-btn"
|
||||
>
|
||||
Profile
|
||||
</button>
|
||||
<button
|
||||
role="option"
|
||||
data-tab-handler-mobile="totp"
|
||||
data-select="false"
|
||||
id="edit-{{current_endpoint}}-totp-tab"
|
||||
class="settings-tabs-mobile-dropdown-btn rounded-b"
|
||||
>
|
||||
TOTP
|
||||
</button>
|
||||
</div>
|
||||
<!-- end dropdown-->
|
||||
</div>
|
||||
<!-- end mobile tabs -->
|
||||
<form
|
||||
data-plugin-item="profile"
|
||||
class="grid grid-cols-12 w-full justify-items-center"
|
||||
id="profile-form"
|
||||
action="profile"
|
||||
|
|
@ -49,12 +170,12 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
/>
|
||||
<!-- username inpt-->
|
||||
<div
|
||||
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
>
|
||||
<h5
|
||||
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
|
||||
>
|
||||
username
|
||||
Username
|
||||
</h5>
|
||||
<label class="sr-only" for="admin_username">New username</label>
|
||||
<input
|
||||
|
|
@ -72,7 +193,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
<!-- password inpt-->
|
||||
<div
|
||||
data-input-group
|
||||
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
>
|
||||
<h5
|
||||
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
|
||||
|
|
@ -85,7 +206,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
id="curr_password"
|
||||
name="curr_password"
|
||||
class="col-span-12 regular-input"
|
||||
placeholder="enter password"
|
||||
placeholder="enter current password"
|
||||
value=""
|
||||
pattern="^(?=.*?\d)(?=.*?[ !\u0022#$%&'\(\)*+,.\/:;<=>?@\[\\\]^_`\u007B\u007C\u007D\u007E\u002D]).{8,}$"
|
||||
minlength="8"
|
||||
|
|
@ -131,7 +252,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
<!-- password inpt-->
|
||||
<div
|
||||
data-input-group
|
||||
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
>
|
||||
<h5
|
||||
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
|
||||
|
|
@ -144,14 +265,14 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
id="admin_password"
|
||||
name="admin_password"
|
||||
class="col-span-12 regular-input"
|
||||
placeholder="enter password"
|
||||
placeholder="enter new password"
|
||||
value=""
|
||||
pattern="^(?=.*?\d)(?=.*?[ !\u0022#$%&'\(\)*+,.\/:;<=>?@\[\\\]^_`\u007B\u007C\u007D\u007E\u002D]).{8,}$"
|
||||
minlength="8"
|
||||
/>
|
||||
<div
|
||||
data-setting-password-container
|
||||
class="absolute flex right-8 h-5 w-5 top-[60%] md:top-[45%] lg:top-11"
|
||||
class="absolute flex right-8 h-5 w-5 top-[60%] lg:top-11"
|
||||
>
|
||||
<button
|
||||
data-setting-password="visible"
|
||||
|
|
@ -189,7 +310,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
<!-- password inpt-->
|
||||
<div
|
||||
data-input-group
|
||||
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
>
|
||||
<h5
|
||||
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
|
||||
|
|
@ -204,7 +325,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
id="admin_password_check"
|
||||
name="admin_password_check"
|
||||
class="col-span-12 regular-input"
|
||||
placeholder="confirm password"
|
||||
placeholder="confirm new password"
|
||||
value=""
|
||||
pattern="^(?=.*?\d)(?=.*?[ !\u0022#$%&'\(\)*+,.\/:;<=>?@\[\\\]^_`\u007B\u007C\u007D\u007E\u002D]).{8,}$"
|
||||
minlength="8"
|
||||
|
|
@ -255,27 +376,26 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
id="profile-button"
|
||||
name="profile-button"
|
||||
value="profile"
|
||||
class="valid-btn"
|
||||
class="edit-btn"
|
||||
>
|
||||
Save
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-full overflow-hidden overflow-y-auto overflow-x-auto max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="my-2 font-bold dark:text-white/90 mx-2">
|
||||
2FA SETTINGS {% if not is_totp %} (NOT SETUP) {% else %} (SETUP) {% endif %}
|
||||
</h5>
|
||||
<form
|
||||
class="grid grid-cols-12 w-full justify-items-center"
|
||||
data-plugin-item="totp"
|
||||
class="hidden grid grid-cols-12 w-full justify-items-center"
|
||||
id="profile-form"
|
||||
action="profile"
|
||||
method="POST"
|
||||
autocomplete="off"
|
||||
>
|
||||
<h5
|
||||
class="{% if not is_totp %} text-red-500 {% else %} text-green-500 {% endif %} uppercase col-span-12 text-sm my-2 font-bold dark:text-white/90 mx-2"
|
||||
>
|
||||
{% if not is_totp %} TOTP is currently off {% else %} TOTP is currently on
|
||||
{% endif %}
|
||||
</h5>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input
|
||||
type="hidden"
|
||||
|
|
@ -286,24 +406,26 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
<!-- qr secret -->
|
||||
<div
|
||||
data-input-group
|
||||
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
>
|
||||
<h5
|
||||
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
|
||||
>
|
||||
2FA QR CODE
|
||||
</h5>
|
||||
<img
|
||||
src="data:image/png;base64, {{ totp_qr_image }}"
|
||||
alt="Secret Token"
|
||||
style="width: 200px; height: 200px"
|
||||
/>
|
||||
<div class="flex justify-center">
|
||||
<img
|
||||
src="data:image/png;base64, {{ totp_qr_image }}"
|
||||
alt="Secret Token"
|
||||
style="width: 200px; height: 200px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end qr secret -->
|
||||
<!-- secret -->
|
||||
<div
|
||||
data-input-group
|
||||
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
>
|
||||
<h5
|
||||
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
|
||||
|
|
@ -322,7 +444,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
/>
|
||||
<div
|
||||
data-setting-password-container
|
||||
class="absolute flex right-8 h-5 w-5 top-[60%] md:top-[45%] lg:top-11"
|
||||
class="absolute flex right-8 h-5 w-5 top-[60%] lg:top-11"
|
||||
>
|
||||
<button
|
||||
data-setting-password="visible"
|
||||
|
|
@ -360,7 +482,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
{% endif %} {% if is_totp or not is_totp %}
|
||||
<!-- username inpt-->
|
||||
<div
|
||||
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
>
|
||||
<h5
|
||||
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
|
||||
|
|
@ -383,7 +505,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
{% endif %}
|
||||
<div
|
||||
data-input-group
|
||||
class="flex flex-col relative col-span-12 md:col-span-6 lg:col-span-4 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full"
|
||||
>
|
||||
<h5
|
||||
class="text-base my-1 transition duration-300 ease-in-out text-md font-bold m-0"
|
||||
|
|
@ -440,17 +562,18 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
</div>
|
||||
<!-- end password inpt-->
|
||||
|
||||
<div class="col-span-12 flex justify-center">
|
||||
<div class="col-span-12 flex justify-center mt-6">
|
||||
<button
|
||||
type="submit"
|
||||
id="profile-button"
|
||||
name="profile-button"
|
||||
value="profile"
|
||||
class="valid-btn"
|
||||
class="{% if not is_totp %}valid-btn{% else %}delete-btn{% endif %}"
|
||||
>
|
||||
{% if not is_totp %} enable totp {% else %} disable totp {% endif %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
14
src/ui/templates/totp.html
vendored
14
src/ui/templates/totp.html
vendored
|
|
@ -125,7 +125,20 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- end form -->
|
||||
<!-- particles -->
|
||||
<div class="-z-10 fixed bg-primary">
|
||||
<div id="particles-js" class="login-img [&>*]:bg-primary"></div>
|
||||
<div class="hidden lg:flex justify-center">
|
||||
<img
|
||||
class="max-w-60 max-h-30"
|
||||
src="images/BUNKERWEB-print-hd-blanc.png"
|
||||
alt="logo"
|
||||
class="images login-logo"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="js/tsparticles.bundle.min.js"></script>
|
||||
<script>
|
||||
class Loader {
|
||||
constructor() {
|
||||
|
|
@ -201,6 +214,7 @@
|
|||
|
||||
const setLoader = new Loader();
|
||||
const setFlash = new FlashMsg();
|
||||
tsParticles.loadJSON("particles-js", "json/particles.json");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in a new issue