From 5a7c6a29baf08ab9b410ee1d302aec8001e001c2 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 8 Nov 2025 15:20:53 +0530 Subject: [PATCH] added recurring payment --- index.html | 2 + public/assets/vendor/libs/tagify/tagify.css | 879 ++++++++++++++++++ public/assets/vendor/libs/tagify/tagify.js | 120 +++ .../ManageRecurringExpense.jsx | 537 +++++++++++ .../RecurringExpenseFilterPanel.jsx | 0 .../RecurringExpense/RecurringExpenseList.jsx | 290 ++++++ .../RecurringExpenseSchema.js | 112 +++ .../RecurringRexpenseList.jsx | 342 +++++++ .../RecurringExpense/ViewRecurringExpense.jsx | 307 ++++++ src/components/common/PmsEmployeeInputTag.jsx | 295 ++++++ src/hooks/masterHook/useMaster.js | 10 + .../RecurringExpense/RecurringExpensePage.jsx | 165 ++++ src/repositories/MastersRepository.jsx | 2 + src/router/AppRoutes.jsx | 2 + 14 files changed, 3063 insertions(+) create mode 100644 public/assets/vendor/libs/tagify/tagify.css create mode 100644 public/assets/vendor/libs/tagify/tagify.js create mode 100644 src/components/RecurringExpense/ManageRecurringExpense.jsx create mode 100644 src/components/RecurringExpense/RecurringExpenseFilterPanel.jsx create mode 100644 src/components/RecurringExpense/RecurringExpenseList.jsx create mode 100644 src/components/RecurringExpense/RecurringExpenseSchema.js create mode 100644 src/components/RecurringExpense/RecurringRexpenseList.jsx create mode 100644 src/components/RecurringExpense/ViewRecurringExpense.jsx create mode 100644 src/components/common/PmsEmployeeInputTag.jsx create mode 100644 src/pages/RecurringExpense/RecurringExpensePage.jsx diff --git a/index.html b/index.html index 8c7ba79b..24a9ef36 100644 --- a/index.html +++ b/index.html @@ -46,6 +46,8 @@ + + diff --git a/public/assets/vendor/libs/tagify/tagify.css b/public/assets/vendor/libs/tagify/tagify.css new file mode 100644 index 00000000..9c861220 --- /dev/null +++ b/public/assets/vendor/libs/tagify/tagify.css @@ -0,0 +1,879 @@ +@charset "UTF-8"; +:root { + --tagify-dd-color-primary: rgb(53,149,246); + --tagify-dd-bg-color: white; + --tagify-dd-item-pad: .3em .5em; + --tagify-dd-max-height: 300px; +} + +.tagify { + --tags-disabled-bg: #F1F1F1; + --tags-border-color: #DDD; + --tags-hover-border-color: #CCC; + --tags-focus-border-color: #3595f6; + --tag-border-radius: 3px; + --tag-bg: rgba(167, 172, 178, 0.5); + --tag-hover: #D3E2E2; + --tag-text-color: black; + --tag-text-color--edit: black; + --tag-pad: 0.3em 0.5em; + --tag-inset-shadow-size: 2em; + --tag-invalid-color: #ff3e1d; + --tag-invalid-bg: rgba(255, 62, 29, 0.5); + --tag--min-width: 1ch; + --tag--max-width: auto; + --tag-hide-transition: 0.3s; + --tag-remove-bg: rgba(255, 62, 29, 0.3); + --tag-remove-btn-color: #7a838b; + --tag-remove-btn-bg: none; + --tag-remove-btn-bg--hover: #ff2804; + --input-color: inherit; + --placeholder-color: rgba(0, 0, 0, 0.4); + --placeholder-color-focus: rgba(0, 0, 0, 0.25); + --loader-size: .8em; + --readonly-striped: 1; + display: inline-flex; + align-items: flex-start; + flex-wrap: wrap; + border: 1px solid var(--tags-border-color); + padding: 0; + line-height: 0; + cursor: text; + outline: none; + position: relative; + box-sizing: border-box; + transition: 0.1s; +} +@keyframes tags--bump { + 30% { + transform: scale(1.2); + } +} +@keyframes rotateLoader { + to { + transform: rotate(1turn); + } +} +.tagify:hover:not(.tagify--focus):not(.tagify--invalid) { + --tags-border-color: var(--tags-hover-border-color); +} +.tagify[disabled] { + background: var(--tags-disabled-bg); + filter: saturate(0); + opacity: 0.5; + pointer-events: none; +} +.tagify[readonly].tagify--select, .tagify[disabled].tagify--select { + pointer-events: none; +} +.tagify[readonly]:not(.tagify--mix):not(.tagify--select), .tagify[disabled]:not(.tagify--mix):not(.tagify--select) { + cursor: default; +} +.tagify[readonly]:not(.tagify--mix):not(.tagify--select) > .tagify__input, .tagify[disabled]:not(.tagify--mix):not(.tagify--select) > .tagify__input { + visibility: hidden; + width: 0; + margin: 5px 0; +} +.tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div, .tagify[disabled]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div { + padding: var(--tag-pad); +} +.tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before, .tagify[disabled]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before { + animation: readonlyStyles 1s calc(-1s * (var(--readonly-striped) - 1)) paused; +} +@keyframes readonlyStyles { + 0% { + background: linear-gradient(45deg, var(--tag-bg) 25%, transparent 25%, transparent 50%, var(--tag-bg) 50%, var(--tag-bg) 75%, transparent 75%, transparent) 0/5px 5px; + box-shadow: none; + filter: brightness(0.95); + } +} +.tagify[readonly] .tagify__tag__removeBtn, .tagify[disabled] .tagify__tag__removeBtn { + display: none; +} +.tagify--loading .tagify__input > br:last-child { + display: none; +} +.tagify--loading .tagify__input::before { + content: none; +} +.tagify--loading .tagify__input::after { + content: ""; + vertical-align: middle; + opacity: 1; + width: 0.7em; + height: 0.7em; + width: var(--loader-size); + height: var(--loader-size); + min-width: 0; + border: 3px solid; + border-color: #EEE #BBB #888 transparent; + border-radius: 50%; + animation: rotateLoader 0.4s infinite linear; + content: "" !important; + margin: -2px 0 -2px 0.5em; +} +.tagify--loading .tagify__input:empty::after { + margin-left: 0; +} +.tagify + input, +.tagify + textarea { + position: absolute !important; + left: -9999em !important; + transform: scale(0) !important; +} +.tagify__tag { + display: inline-flex; + align-items: center; + max-width: calc(var(--tag--max-width) - 10px); + margin-inline: 5px 0; + margin-block: 5px; + position: relative; + z-index: 1; + outline: none; + line-height: normal; + cursor: default; + transition: 0.13s ease-out; +} +.tagify__tag > div { + vertical-align: top; + box-sizing: border-box; + max-width: 100%; + padding: var(--tag-pad); + color: var(--tag-text-color); + line-height: inherit; + border-radius: var(--tag-border-radius); + white-space: nowrap; + transition: 0.13s ease-out; +} +.tagify__tag > div > * { + white-space: pre-wrap; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + vertical-align: top; + min-width: var(--tag--min-width); + max-width: var(--tag--max-width); + transition: 0.8s ease, 0.1s color; +} +.tagify__tag > div > *[contenteditable] { + outline: none; + user-select: text; + cursor: text; + margin: -2px; + padding: 2px; + max-width: 350px; +} +.tagify__tag > div::before { + content: ""; + position: absolute; + border-radius: inherit; + inset: var(--tag-bg-inset, 0); + z-index: -1; + pointer-events: none; + transition: 120ms ease; + animation: tags--bump 0.3s ease-out 1; + box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-bg) inset; +} +.tagify__tag:hover:not([readonly]) div::before, .tagify__tag:focus div::before { + --tag-bg-inset: -2.5px; + --tag-bg: var(--tag-hover); +} +.tagify__tag--loading { + pointer-events: none; +} +.tagify__tag--loading .tagify__tag__removeBtn { + display: none; +} +.tagify__tag--loading::after { + --loader-size: .4em; + content: ""; + vertical-align: middle; + opacity: 1; + width: 0.7em; + height: 0.7em; + width: var(--loader-size); + height: var(--loader-size); + min-width: 0; + border: 3px solid; + border-color: #EEE #BBB #888 transparent; + border-radius: 50%; + animation: rotateLoader 0.4s infinite linear; + margin: 0 0.5em 0 -0.1em; +} +.tagify__tag--flash div::before { + animation: none; +} +.tagify__tag--hide { + width: 0 !important; + padding-left: 0; + padding-right: 0; + margin-left: 0; + margin-right: 0; + opacity: 0; + transform: scale(0); + transition: var(--tag-hide-transition); + pointer-events: none; +} +.tagify__tag--hide > div > * { + white-space: nowrap; +} +.tagify__tag.tagify--noAnim > div::before { + animation: none; +} +.tagify__tag.tagify--notAllowed:not(.tagify__tag--editable) div > span { + opacity: 0.5; +} +.tagify__tag.tagify--notAllowed:not(.tagify__tag--editable) div::before { + --tag-bg: var(--tag-invalid-bg); + transition: 0.2s; +} +.tagify__tag[readonly] .tagify__tag__removeBtn { + display: none; +} +.tagify__tag[readonly] > div::before { + animation: readonlyStyles 1s calc(-1s * (var(--readonly-striped) - 1)) paused; +} +@keyframes readonlyStyles { + 0% { + background: linear-gradient(45deg, var(--tag-bg) 25%, transparent 25%, transparent 50%, var(--tag-bg) 50%, var(--tag-bg) 75%, transparent 75%, transparent) 0/5px 5px; + box-shadow: none; + filter: brightness(0.95); + } +} +.tagify__tag--editable > div { + color: var(--tag-text-color--edit); +} +.tagify__tag--editable > div::before { + box-shadow: 0 0 0 2px var(--tag-hover) inset !important; +} +.tagify__tag--editable > .tagify__tag__removeBtn { + pointer-events: none; +} +.tagify__tag--editable > .tagify__tag__removeBtn::after { + opacity: 0; + transform: translateX(100%) translateX(5px); +} +.tagify__tag--editable.tagify--invalid > div::before { + box-shadow: 0 0 0 2px var(--tag-invalid-color) inset !important; +} +.tagify__tag__removeBtn { + order: 5; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50px; + cursor: pointer; + font: 14px/1 Arial; + background: var(--tag-remove-btn-bg); + color: var(--tag-remove-btn-color); + width: 14px; + height: 14px; + margin-inline: auto 4.6666666667px; + overflow: hidden; + transition: 0.2s ease-out; +} +.tagify__tag__removeBtn::after { + content: "×"; + transition: 0.3s, color 0s; +} +.tagify__tag__removeBtn:hover { + color: white; + background: var(--tag-remove-btn-bg--hover); +} +.tagify__tag__removeBtn:hover + div > span { + opacity: 0.5; +} +.tagify__tag__removeBtn:hover + div::before { + box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-remove-bg, rgba(255, 62, 29, 0.3)) inset !important; + transition: box-shadow 0.2s; +} +.tagify:not(.tagify--mix) .tagify__input br { + display: none; +} +.tagify:not(.tagify--mix) .tagify__input * { + display: inline; + white-space: nowrap; +} +.tagify__input { + flex-grow: 1; + display: inline-block; + min-width: 110px; + margin: 5px; + padding: var(--tag-pad); + line-height: normal; + position: relative; + white-space: pre-wrap; + color: var(--input-color); + box-sizing: inherit; + /* Seems firefox newer versions don't need this any more + @supports ( -moz-appearance:none ){ + &::before{ + line-height: inherit; + position:relative; + } + } + */ +} +@-moz-document url-prefix() {} +.tagify__input:empty::before { + position: static; +} +.tagify__input:focus { + outline: none; +} +.tagify__input:focus::before { + transition: 0.2s ease-out; + opacity: 0; + transform: translatex(6px); + /* ALL MS BROWSERS: hide placeholder (on focus) otherwise the caret is placed after it, which is weird */ + /* IE Edge 12+ CSS styles go here */ +} +@supports (-ms-ime-align: auto) { + .tagify__input:focus::before { + display: none; + } +} +.tagify__input:focus:empty::before { + transition: 0.2s ease-out; + opacity: 1; + transform: none; + color: rgba(0, 0, 0, 0.25); + color: var(--placeholder-color-focus); +} +@-moz-document url-prefix() { + .tagify__input:focus:empty::after { + display: none; + } +} +.tagify__input::before { + content: attr(data-placeholder); + height: 1em; + line-height: 1em; + margin: auto 0; + z-index: 1; + color: var(--placeholder-color); + white-space: nowrap; + pointer-events: none; + opacity: 0; + position: absolute; +} +.tagify__input::after { + content: attr(data-suggest); + display: inline-block; + vertical-align: middle; + position: absolute; + min-width: calc(100% - 1.5em); + text-overflow: ellipsis; + overflow: hidden; + white-space: pre; /* allows spaces at the beginning */ + color: var(--tag-text-color); + opacity: 0.3; + pointer-events: none; + max-width: 100px; +} +.tagify__input .tagify__tag { + margin: 0 1px; +} +.tagify--mix { + display: block; +} +.tagify--mix .tagify__input { + padding: 5px; + margin: 0; + width: 100%; + height: 100%; + line-height: 1.5; + display: block; +} +.tagify--mix .tagify__input::before { + height: auto; + display: none; + line-height: inherit; +} +.tagify--mix .tagify__input::after { + content: none; +} +.tagify--select::after { + content: ">"; + opacity: 0.5; + position: absolute; + top: 50%; + right: 0; + bottom: 0; + font: 16px monospace; + line-height: 8px; + height: 8px; + pointer-events: none; + transform: translate(-150%, -50%) scaleX(1.2) rotate(90deg); + transition: 0.2s ease-in-out; +} +.tagify--select[aria-expanded=true]::after { + transform: translate(-150%, -50%) rotate(270deg) scaleY(1.2); +} +.tagify--select .tagify__tag { + position: absolute; + top: 0; + right: 1.8em; + bottom: 0; +} +.tagify--select .tagify__tag div { + display: none; +} +.tagify--select .tagify__input { + width: 100%; +} +.tagify--empty .tagify__input::before { + transition: 0.2s ease-out; + opacity: 1; + transform: none; + display: inline-block; + width: auto; +} +.tagify--mix .tagify--empty .tagify__input::before { + display: inline-block; +} +.tagify--focus { + --tags-border-color: var(--tags-focus-border-color); + transition: 0s; +} +.tagify--invalid { + --tags-border-color: #ff3e1d; +} +.tagify__dropdown { + position: absolute; + z-index: 9999; + transform: translateY(-1px); + border-top: 1px solid var(--tagify-dd-color-primary); + overflow: hidden; +} +.tagify__dropdown[dir=rtl] { + transform: translate(-100%, -1px); +} +.tagify__dropdown[placement=top] { + margin-top: 0; + transform: translateY(-100%); +} +.tagify__dropdown[placement=top] .tagify__dropdown__wrapper { + border-top-width: 1.1px; + border-bottom-width: 0; +} +.tagify__dropdown[position=text] { + box-shadow: 0 0 0 3px rgba(var(--tagify-dd-color-primary), 0.1); + font-size: 0.9em; +} +.tagify__dropdown[position=text] .tagify__dropdown__wrapper { + border-width: 1px; +} +.tagify__dropdown__wrapper { + max-height: var(--tagify-dd-max-height); + overflow: hidden; + overflow-x: hidden; + background: var(--tagify-dd-bg-color); + border: 1px solid; + border-color: var(--tagify-dd-color-primary); + border-bottom-width: 1.5px; + border-top-width: 0; + box-shadow: 0 2px 4px -2px rgba(0, 0, 0, 0.2); + transition: 0.3s cubic-bezier(0.5, 0, 0.3, 1), transform 0.15s; + animation: dd-wrapper-show 0s 0.3s forwards; +} +@keyframes dd-wrapper-show { + to { + overflow-y: auto; + } +} +.tagify__dropdown__header:empty { + display: none; +} +.tagify__dropdown__footer { + display: inline-block; + margin-top: 0.5em; + padding: var(--tagify-dd-item-pad); + font-size: 0.7em; + font-style: italic; + opacity: 0.5; +} +.tagify__dropdown__footer:empty { + display: none; +} +.tagify__dropdown--initial .tagify__dropdown__wrapper { + max-height: 20px; + transform: translateY(-1em); +} +.tagify__dropdown--initial[placement=top] .tagify__dropdown__wrapper { + transform: translateY(2em); +} +.tagify__dropdown__item { + box-sizing: border-box; + padding: var(--tagify-dd-item-pad); + margin: 1px; + white-space: pre-wrap; + cursor: pointer; + border-radius: 2px; + position: relative; + outline: none; + max-height: 60px; + max-width: 100%; + /* custom hidden transition effect is needed for horizontal-layout suggestions */ +} +.tagify__dropdown__item--active { + background: var(--tagify-dd-color-primary); + color: white; +} +.tagify__dropdown__item:active { + filter: brightness(105%); +} +.tagify__dropdown__item--hidden { + padding-top: 0; + padding-bottom: 0; + margin: 0 1px; + pointer-events: none; + overflow: hidden; + max-height: 0; + transition: var(--tagify-dd-item--hidden-duration, 0.3s) !important; +} +.tagify__dropdown__item--hidden > * { + transform: translateY(-100%); + opacity: 0; + transition: inherit; +} + +/* Suggestions items */ +.tagify__dropdown.users-list { + font-size: 1rem; +} +.tagify__dropdown.users-list .addAll { + display: block !important; +} +.tagify__dropdown.users-list .tagify__dropdown__item { + padding: 0.5em 0.7em; + display: grid; + grid-template-columns: auto 1fr; + gap: 0 1em; + grid-template-areas: "avatar name" "avatar email"; +} +.tagify__dropdown.users-list .tagify__dropdown__item__avatar-wrap { + grid-area: avatar; + width: 36px; + height: 36px; + border-radius: 50%; + overflow: hidden; + transition: 0.1s ease-out; +} +.tagify__dropdown.users-list img { + width: 100%; + vertical-align: top; +} +.tagify__dropdown.users-list strong { + grid-area: name; + width: 100%; + align-self: center; + font-weight: 500; +} +.tagify__dropdown.users-list span { + grid-area: email; + width: 100%; + font-size: 0.9em; + opacity: 0.6; +} + +/* Tags items */ +.tagify__tag { + white-space: nowrap; +} +.tagify__tag .tagify__tag__avatar-wrap { + width: 22px; + height: 22px; + white-space: normal; + border-radius: 50%; + margin-right: 5px; + transition: 0.12s ease-out; + vertical-align: middle; +} +.tagify__tag img { + width: 100%; + vertical-align: top; +} + +[dir=rtl] .tagify__tag .tagify__tag__avatar-wrap { + margin-left: 5px; + margin-right: auto; +} + +.light-style .tagify__dropdown.users-list .tagify__dropdown__item__avatar-wrap { + background: #f5f5f9; +} +.light-style .tagify__tag .tagify__tag__avatar-wrap { + background: #f5f5f9; +} +.light-style .tagify__dropdown.users-list .addAll { + border-bottom: 1px solid #e4e6e8; +} + +.dark-style .tagify__dropdown.users-list .tagify__dropdown__item__avatar-wrap { + background: #232333; +} +.dark-style .tagify__tag .tagify__tag__avatar-wrap { + background: #232333; +} +.dark-style .tagify__dropdown.users-list .addAll { + border-bottom: 1px solid #4e4f6c; +} + +.tags-inline .tagify__dropdown__wrapper { + padding: 0 0.4375rem 0.4375rem 0.4375rem; +} +.tags-inline .tagify__dropdown__item { + display: inline-block; + border-radius: 3px; + padding: 0.3em 0.5em; + margin: 0.4375rem 0.4375rem 0 0; + font-size: 0.85em; + transition: 0s; +} + +[dir=rtl] .tags-inline .tagify__dropdown__item { + margin: 0.4375rem 0 0 0.4375rem; +} + +.light-style .tags-inline .tagify__dropdown__item { + border: 1px solid #e4e6e8; + color: #646e78; +} + +.dark-style .tags-inline .tagify__dropdown__item { + border: 1px solid #4e4f6c; + color: #b2b2c4; +} + +.tagify-email-list { + display: inline-block; + min-width: 0; + border: none; + /* Do not show the "remove tag" (x) button when only a single tag remains */ +} +.tagify-email-list.tagify { + padding: 0 !important; + padding-bottom: calc(0.4375rem - var(--bs-border-width)) !important; +} +.tagify-email-list.tagify { + padding: 0 !important; + padding-bottom: calc(0.4375rem - var(--bs-border-width)) !important; +} +.tagify-email-list.tagify.tagify--focus { + padding-left: 0 !important; +} +.tagify-email-list .tagify__tag { + margin: 0; + margin-inline-start: 0 !important; + margin-inline-end: 0.625rem !important; + margin-bottom: 0.4375rem !important; +} +.tagify-email-list .tagify__tag > div { + padding: 0.21875rem 0.4375rem !important; + padding-inline: 0.875rem !important; +} +.tagify-email-list .tagify__tag:only-of-type > div { + padding-inline: 0.4375rem !important; +} +.tagify-email-list .tagify__tag:only-of-type .tagify__tag__removeBtn { + display: none; +} +.tagify-email-list .tagify__tag__removeBtn { + opacity: 0; + transform: translateX(-6px) scale(0.5); + margin-left: -3ch; + transition: 0.12s; + position: absolute; + inset-inline-end: 0; +} +.tagify-email-list .tagify__tag:hover .tagify__tag__removeBtn { + transform: none; + opacity: 1; + margin-left: -1ch; +} +.tagify-email-list .tagify__input { + display: none; +} + +.tagify__tag > div { + border-radius: 50rem; +} + +[dir=rtl] .tagify-email-list .tagify__tag { + margin: 0 0.4375rem 0.4375rem 0; +} +[dir=rtl] .tagify-email-list .tagify__tag:hover .tagify__tag__removeBtn { + margin-left: auto; + margin-right: -1ch; +} +[dir=rtl] .tagify-email-list .tagify__tag__removeBtn { + transform: translateX(6px) scale(0.5); + margin-left: auto; + margin-right: -3ch; +} + +.light-style .tagify-email-list .tagify__tag--editable:not(.tagify--invalid) > div::before { + box-shadow: 0 0 0 2px #e4e6e8 inset !important; +} + +.dark-style .tagify-email-list .tagify__tag--editable:not(.tagify--invalid) > div::before { + box-shadow: 0 0 0 2px #4e4f6c inset !important; +} + +.tagify.form-control { + transition: none; + display: flex; + align-items: flex-end; + /* padding: calc(2px - var(--bs-border-width)) 0.4375rem 0.4231rem !important; */ + padding: calc(2px - var(--bs-border-width)) 0.4375rem 0.2rem !important; +} +.fv-plugins-bootstrap5-row-invalid .tagify.form-control { + padding: 0 calc(0.4375rem - var(--bs-border-width)) calc(0.4375rem - 2px) !important; +} +.tagify.tagify--focus, .tagify.form-control:focus { + padding: 0 calc(0.4375rem - var(--bs-border-width)) 0.3606rem !important; + border-width: 2px; +} +.tagify__tag, .tagify__input { + margin: 0.1875rem 0.625rem 0 0 !important; + line-height: 1; +} +.tagify__input { + line-height: 1.5rem; +} +.tagify__input:empty::before { + top: 4px; +} +.tagify__tag > div { + line-height: 1.5rem; + padding: 0 0 0 0.4375rem; +} +.tagify__tag__removeBtn { + margin-right: 0.1375rem; + margin-left: 0.21875rem; + font-family: "boxicons"; + font-size: 1rem; + opacity: 0.7; +} +.tagify__tag__removeBtn:hover { + background: none; + color: #ff2804 !important; +} +.tagify__tag__removeBtn::after { + content: "\ef06"; +} +.tagify__tag:hover:not([readonly]) div::before, .tagify__tag:focus div::before { + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} +.tagify__dropdown { + transform: translateY(0); +} +.tagify[readonly]:not(.tagify--mix) .tagify__tag > div { + padding: 0 0.4375rem 0 0.4375rem !important; +} +.tagify__input { + padding: 0; +} +.tagify__tag-text { + font-size: 0.8125rem; + font-weight: 500; +} + +.tagify.form-control { + padding-top: 0.1412rem !important; +} +.tagify.tagify--focus, .tagify.form-control:focus { + padding-top: calc(0.1412rem - 1px) !important; +} + +.tagify__tag__removeBtn { + margin-inline-end: 0.3rem; +} + +[dir=rtl] .tagify__tag, [dir=rtl] .tagify__input { + margin: 0.4375rem 0 0 0.4375rem; +} +[dir=rtl] .tagify + input, +[dir=rtl] .tagify + textarea { + left: 0; + right: -9999em !important; +} +[dir=rtl] .tagify__tag > div { + padding: 0 0.6875rem 0 0; +} +[dir=rtl] .tagify__tag__removeBtn { + margin-left: 0.4375rem; + margin-right: 0.21875rem; +} + +.light-style .tagify__tag > div::before { + box-shadow: 0 0 0 1.3em rgba(34, 48, 62, 0.08) inset; +} +.light-style .tagify__tag .tagify__tag-text { + color: #384551; +} +.light-style .tagify__tag:hover:not([readonly]) div::before, .light-style .tagify__tag:focus div::before { + box-shadow: 0 0 0 1.3em rgba(34, 48, 62, 0.12) inset; +} +.light-style .tagify__tag__removeBtn { + color: #7a838b; +} +.light-style .tagify__tag__removeBtn:hover + div::before { + background: rgba(255, 62, 29, 0.3); +} +.light-style .tagify:hover:not([readonly]) { + border-color: #ced1d5; +} +.light-style .tagify__input::before { + color: #a7acb2 !important; +} +.light-style .tagify__dropdown { + box-shadow: 0 0.25rem 0.75rem 0 rgba(34, 48, 62, 0.14); + border-top-color: #e4e6e8; +} +.light-style .tagify__dropdown__wrapper { + background: #fff; + border-color: #e4e6e8; +} + +.dark-style .tagify__tag > div::before { + box-shadow: 0 0 0 1.3em rgba(230, 230, 241, 0.08) inset; +} +.dark-style .tagify__tag > div .tagify__tag-text { + color: #d5d5e2; +} +.dark-style .tagify__tag:hover:not([readonly]) div::before, .dark-style .tagify__tag:focus div::before { + box-shadow: 0 0 0 1.3em rgba(230, 230, 241, 0.12) inset; +} +.dark-style .tagify__tag__removeBtn { + color: #a1a1b5; +} +.dark-style .tagify__tag__removeBtn:hover + div::before { + background: rgba(255, 62, 29, 0.3); +} +.dark-style .tagify:hover:not([readonly]) { + border-color: #5f607b; +} +.dark-style .tagify__input::before { + color: #7e7f96 !important; +} +.dark-style .tagify[readonly]:not(.tagify--mix) .tagify__tag > div::before { + background: linear-gradient(45deg, #5f607b 25%, transparent 25%, transparent 50%, #5f607b 50%, #5f607b 75%, transparent 75%, transparent) 0/5px 5px; +} +.dark-style .tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before { + animation: none; + box-shadow: none; +} +.dark-style .tagify__dropdown { + box-shadow: 0 0.25rem 0.75rem 0 rgba(20, 20, 29, 0.24); + border-top-color: #4e4f6c; +} +.dark-style .tagify__dropdown__wrapper { + box-shadow: 0 0.25rem 0.75rem 0 rgba(20, 20, 29, 0.24); + background: #2b2c40; + border-color: #4e4f6c; +} diff --git a/public/assets/vendor/libs/tagify/tagify.js b/public/assets/vendor/libs/tagify/tagify.js new file mode 100644 index 00000000..a952d07d --- /dev/null +++ b/public/assets/vendor/libs/tagify/tagify.js @@ -0,0 +1,120 @@ +/* + * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). + * This devtool is neither made for production nor for readable output files. + * It uses "eval()" calls to create a separate source file in the browser devtools. + * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) + * or disable the default devtool with "devtool: false". + * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else { + var a = factory(); + for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; + } +})(self, function() { +return /******/ (function() { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ "./node_modules/@yaireo/tagify/dist/tagify.min.js": +/*!********************************************************!*\ + !*** ./node_modules/@yaireo/tagify/dist/tagify.min.js ***! + \********************************************************/ +/***/ (function(module) { + +eval("/**\n * Tagify (v 4.18.3) - tags input component\n * By undefined\n * https://github.com/yairEO/tagify\n * Permission is hereby granted, free of charge, to any person obtaining a copy\r\n * of this software and associated documentation files (the \"Software\"), to deal\r\n * in the Software without restriction, including without limitation the rights\r\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n * copies of the Software, and to permit persons to whom the Software is\r\n * furnished to do so, subject to the following conditions:\r\n * \r\n * The above copyright notice and this permission notice shall be included in\r\n * all copies or substantial portions of the Software.\r\n * \r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\n * THE SOFTWARE.\r\n * \r\n * THE SOFTWARE IS NOT PERMISSIBLE TO BE SOLD.\n */\n\n!function(t,e){ true?module.exports=e():0}(this,(function(){\"use strict\";function t(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,s)}return i}function e(e){for(var s=1;s(t=\"\"+t,e=\"\"+e,s&&(t=t.trim(),e=e.trim()),i?t==e:t.toLowerCase()==e.toLowerCase()),a=(t,e)=>t&&Array.isArray(t)&&t.map((t=>n(t,e)));function n(t,e){var i,s={};for(i in t)e.indexOf(i)<0&&(s[i]=t[i]);return s}function o(t){var e=document.createElement(\"div\");return t.replace(/\\&#?[0-9a-z]+;/gi,(function(t){return e.innerHTML=t,e.innerText}))}function r(t){return(new DOMParser).parseFromString(t.trim(),\"text/html\").body.firstElementChild}function l(t,e){for(e=e||\"previous\";t=t[e+\"Sibling\"];)if(3==t.nodeType)return t}function d(t){return\"string\"==typeof t?t.replace(/&/g,\"&\").replace(//g,\">\").replace(/\"/g,\""\").replace(/`|'/g,\"'\"):t}function h(t){var e=Object.prototype.toString.call(t).split(\" \")[1].slice(0,-1);return t===Object(t)&&\"Array\"!=e&&\"Function\"!=e&&\"RegExp\"!=e&&\"HTMLUnknownElement\"!=e}function g(t,e,i){function s(t,e){for(var i in e)if(e.hasOwnProperty(i)){if(h(e[i])){h(t[i])?s(t[i],e[i]):t[i]=Object.assign({},e[i]);continue}if(Array.isArray(e[i])){t[i]=Object.assign([],e[i]);continue}t[i]=e[i]}}return t instanceof Object||(t={}),s(t,e),i&&s(t,i),t}function p(){const t=[],e={};for(let i of arguments)for(let s of i)h(s)?e[s.value]||(t.push(s),e[s.value]=1):t.includes(s)||t.push(s);return t}function c(t){return String.prototype.normalize?\"string\"==typeof t?t.normalize(\"NFD\").replace(/[\\u0300-\\u036f]/g,\"\"):void 0:t}var u=()=>/(?=.*chrome)(?=.*android)/i.test(navigator.userAgent);function m(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(t=>(t^crypto.getRandomValues(new Uint8Array(1))[0]&15>>t/4).toString(16)))}function v(t){return t&&t.classList&&t.classList.contains(this.settings.classNames.tag)}function f(t,e){var i=window.getSelection();return e=e||i.getRangeAt(0),\"string\"==typeof t&&(t=document.createTextNode(t)),e&&(e.deleteContents(),e.insertNode(t)),t}function T(t,e,i){return t?(e&&(t.__tagifyTagData=i?e:g({},t.__tagifyTagData||{},e)),t.__tagifyTagData):(console.warn(\"tag element doesn't exist\",t,e),e)}function w(t){if(t&&t.parentNode){var e=t,i=window.getSelection(),s=i.getRangeAt(0);i.rangeCount&&(s.setStartAfter(e),s.collapse(!0),i.removeAllRanges(),i.addRange(s))}}function b(t,e){t.forEach((t=>{if(T(t.previousSibling)||!t.previousSibling){var i=document.createTextNode(\"​\");t.before(i),e&&w(i)}}))}var y={delimiters:\",\",pattern:null,tagTextProp:\"value\",maxTags:1/0,callbacks:{},addTagOnBlur:!0,addTagOn:[\"blur\",\"tab\",\"enter\"],onChangeAfterBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,userInput:!0,keepInvalidTags:!1,createInvalidTags:!0,mixTagsAllowedAfter:/,|\\.|\\:|\\s/,mixTagsInterpolator:[\"[[\",\"]]\"],backspace:!0,skipInvalid:!1,pasteAsTags:!0,editTags:{clicks:2,keepInvalid:!0},transformTag:()=>{},trim:!0,a11y:{focusableTags:!1},mixMode:{insertAfterTag:\" \"},autoComplete:{enabled:!0,rightKey:!1,tabKey:!1},classNames:{namespace:\"tagify\",mixMode:\"tagify--mix\",selectMode:\"tagify--select\",input:\"tagify__input\",focus:\"tagify--focus\",tagNoAnimation:\"tagify--noAnim\",tagInvalid:\"tagify--invalid\",tagNotAllowed:\"tagify--notAllowed\",scopeLoading:\"tagify--loading\",hasMaxTags:\"tagify--hasMaxTags\",hasNoTags:\"tagify--noTags\",empty:\"tagify--empty\",inputInvalid:\"tagify__input--invalid\",dropdown:\"tagify__dropdown\",dropdownWrapper:\"tagify__dropdown__wrapper\",dropdownHeader:\"tagify__dropdown__header\",dropdownFooter:\"tagify__dropdown__footer\",dropdownItem:\"tagify__dropdown__item\",dropdownItemActive:\"tagify__dropdown__item--active\",dropdownItemHidden:\"tagify__dropdown__item--hidden\",dropdownInital:\"tagify__dropdown--initial\",tag:\"tagify__tag\",tagText:\"tagify__tag-text\",tagX:\"tagify__tag__removeBtn\",tagLoading:\"tagify__tag--loading\",tagEditing:\"tagify__tag--editable\",tagFlash:\"tagify__tag--flash\",tagHide:\"tagify__tag--hide\"},dropdown:{classname:\"\",enabled:2,maxItems:10,searchKeys:[\"value\",\"searchBy\"],fuzzySearch:!0,caseSensitive:!1,accentedSearch:!0,includeSelectedTags:!1,escapeHTML:!0,highlightFirst:!1,closeOnSelect:!0,clearOnSelect:!0,position:\"all\",appendTarget:null},hooks:{beforeRemoveTag:()=>Promise.resolve(),beforePaste:()=>Promise.resolve(),suggestionClick:()=>Promise.resolve(),beforeKeyDown:()=>Promise.resolve()}};function x(){this.dropdown={};for(let t in this._dropdown)this.dropdown[t]=\"function\"==typeof this._dropdown[t]?this._dropdown[t].bind(this):this._dropdown[t];this.dropdown.refs()}var O={refs(){this.DOM.dropdown=this.parseTemplate(\"dropdown\",[this.settings]),this.DOM.dropdown.content=this.DOM.dropdown.querySelector(\"[data-selector='tagify-suggestions-wrapper']\")},getHeaderRef(){return this.DOM.dropdown.querySelector(\"[data-selector='tagify-suggestions-header']\")},getFooterRef(){return this.DOM.dropdown.querySelector(\"[data-selector='tagify-suggestions-footer']\")},getAllSuggestionsRefs(){return[...this.DOM.dropdown.content.querySelectorAll(this.settings.classNames.dropdownItemSelector)]},show(t){var e,i,a,n=this.settings,o=\"mix\"==n.mode&&!n.enforceWhitelist,r=!n.whitelist||!n.whitelist.length,l=\"manual\"==n.dropdown.position;if(t=void 0===t?this.state.inputText:t,!(r&&!o&&!n.templates.dropdownItemNoMatch||!1===n.dropdown.enable||this.state.isLoading||this.settings.readonly)){if(clearTimeout(this.dropdownHide__bindEventsTimeout),this.suggestedListItems=this.dropdown.filterListItems(t),t&&!this.suggestedListItems.length&&(this.trigger(\"dropdown:noMatch\",t),n.templates.dropdownItemNoMatch&&(a=n.templates.dropdownItemNoMatch.call(this,{value:t}))),!a){if(this.suggestedListItems.length)t&&o&&!this.state.editing.scope&&!s(this.suggestedListItems[0].value,t)&&this.suggestedListItems.unshift({value:t});else{if(!t||!o||this.state.editing.scope)return this.input.autocomplete.suggest.call(this),void this.dropdown.hide();this.suggestedListItems=[{value:t}]}i=\"\"+(h(e=this.suggestedListItems[0])?e.value:e),n.autoComplete&&i&&0==i.indexOf(t)&&this.input.autocomplete.suggest.call(this,e)}this.dropdown.fill(a),n.dropdown.highlightFirst&&this.dropdown.highlightOption(this.DOM.dropdown.content.querySelector(n.classNames.dropdownItemSelector)),this.state.dropdown.visible||setTimeout(this.dropdown.events.binding.bind(this)),this.state.dropdown.visible=t||!0,this.state.dropdown.query=t,this.setStateSelection(),l||setTimeout((()=>{this.dropdown.position(),this.dropdown.render()})),setTimeout((()=>{this.trigger(\"dropdown:show\",this.DOM.dropdown)}))}},hide(t){var e=this.DOM,i=e.scope,s=e.dropdown,a=\"manual\"==this.settings.dropdown.position&&!t;if(s&&document.body.contains(s)&&!a)return window.removeEventListener(\"resize\",this.dropdown.position),this.dropdown.events.binding.call(this,!1),i.setAttribute(\"aria-expanded\",!1),s.parentNode.removeChild(s),setTimeout((()=>{this.state.dropdown.visible=!1}),100),this.state.dropdown.query=this.state.ddItemData=this.state.ddItemElm=this.state.selection=null,this.state.tag&&this.state.tag.value.length&&(this.state.flaggedTags[this.state.tag.baseOffset]=this.state.tag),this.trigger(\"dropdown:hide\",s),this},toggle(t){this.dropdown[this.state.dropdown.visible&&!t?\"hide\":\"show\"]()},render(){var t,e,i,s=(t=this.DOM.dropdown,(i=t.cloneNode(!0)).style.cssText=\"position:fixed; top:-9999px; opacity:0\",document.body.appendChild(i),e=i.clientHeight,i.parentNode.removeChild(i),e),a=this.settings;return\"number\"==typeof a.dropdown.enabled&&a.dropdown.enabled>=0?(this.DOM.scope.setAttribute(\"aria-expanded\",!0),document.body.contains(this.DOM.dropdown)||(this.DOM.dropdown.classList.add(a.classNames.dropdownInital),this.dropdown.position(s),a.dropdown.appendTarget.appendChild(this.DOM.dropdown),setTimeout((()=>this.DOM.dropdown.classList.remove(a.classNames.dropdownInital)))),this):this},fill(t){t=\"string\"==typeof t?t:this.dropdown.createListHTML(t||this.suggestedListItems);var e,i=this.settings.templates.dropdownContent.call(this,t);this.DOM.dropdown.content.innerHTML=(e=i)?e.replace(/\\>[\\r\\n ]+\\<\").split(/>\\s+<\").trim():\"\"},fillHeaderFooter(){var t=this.dropdown.filterListItems(this.state.dropdown.query),e=this.parseTemplate(\"dropdownHeader\",[t]),i=this.parseTemplate(\"dropdownFooter\",[t]),s=this.dropdown.getHeaderRef(),a=this.dropdown.getFooterRef();e&&s?.parentNode.replaceChild(e,s),i&&a?.parentNode.replaceChild(i,a)},refilter(t){t=t||this.state.dropdown.query||\"\",this.suggestedListItems=this.dropdown.filterListItems(t),this.dropdown.fill(),this.suggestedListItems.length||this.dropdown.hide(),this.trigger(\"dropdown:updated\",this.DOM.dropdown)},position(t){var e=this.settings.dropdown;if(\"manual\"!=e.position){var i,s,a,n,o,r,l,d,h,g=this.DOM.dropdown,p=e.RTL,c=e.appendTarget===document.body,u=c?window.pageYOffset:e.appendTarget.scrollTop,m=document.fullscreenElement||document.webkitFullscreenElement||document.documentElement,v=m.clientHeight,f=Math.max(m.clientWidth||0,window.innerWidth||0)>480?e.position:\"all\",T=this.DOM[\"input\"==f?\"input\":\"scope\"];if(t=t||g.clientHeight,this.state.dropdown.visible){if(\"text\"==f?(a=(i=function(){const t=document.getSelection();if(t.rangeCount){const e=t.getRangeAt(0),i=e.startContainer,s=e.startOffset;let a,n;if(s>0)return n=document.createRange(),n.setStart(i,s-1),n.setEnd(i,s),a=n.getBoundingClientRect(),{left:a.right,top:a.top,bottom:a.bottom};if(i.getBoundingClientRect)return i.getBoundingClientRect()}return{left:-9999,top:-9999}}()).bottom,s=i.top,n=i.left,o=\"auto\"):(r=function(t){for(var e=0,i=0;t&&t!=m;)e+=t.offsetTop||0,i+=t.offsetLeft||0,t=t.parentNode;return{top:e,left:i}}(e.appendTarget),s=(i=T.getBoundingClientRect()).top-r.top,a=i.bottom-1-r.top,n=i.left-r.left,o=i.width+\"px\"),!c){let t=function(){for(var t=0,i=e.appendTarget.parentNode;i;)t+=i.scrollTop||0,i=i.parentNode;return t}();s+=t,a+=t}s=Math.floor(s),a=Math.ceil(a),d=((l=e.placeAbove??v-i.bottom0&&void 0!==arguments[0])||arguments[0];var e=this.dropdown.events.callbacks,i=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this,null),onKeyDown:e.onKeyDown.bind(this),onMouseOver:e.onMouseOver.bind(this),onMouseLeave:e.onMouseLeave.bind(this),onClick:e.onClick.bind(this),onScroll:e.onScroll.bind(this)},s=t?\"addEventListener\":\"removeEventListener\";\"manual\"!=this.settings.dropdown.position&&(document[s](\"scroll\",i.position,!0),window[s](\"resize\",i.position),window[s](\"keydown\",i.onKeyDown)),this.DOM.dropdown[s](\"mouseover\",i.onMouseOver),this.DOM.dropdown[s](\"mouseleave\",i.onMouseLeave),this.DOM.dropdown[s](\"mousedown\",i.onClick),this.DOM.dropdown.content[s](\"scroll\",i.onScroll)},callbacks:{onKeyDown(t){if(this.state.hasFocus&&!this.state.composing){var e=this.settings,i=this.DOM.dropdown.querySelector(e.classNames.dropdownItemActiveSelector),s=this.dropdown.getSuggestionDataByNode(i),a=\"mix\"==e.mode;e.hooks.beforeKeyDown(t,{tagify:this}).then((n=>{switch(t.key){case\"ArrowDown\":case\"ArrowUp\":case\"Down\":case\"Up\":t.preventDefault();var o=this.dropdown.getAllSuggestionsRefs(),r=\"ArrowUp\"==t.key||\"Up\"==t.key;i&&(i=this.dropdown.getNextOrPrevOption(i,!r)),i&&i.matches(e.classNames.dropdownItemSelector)||(i=o[r?o.length-1:0]),this.dropdown.highlightOption(i,!0);break;case\"Escape\":case\"Esc\":this.dropdown.hide();break;case\"ArrowRight\":if(this.state.actions.ArrowLeft)return;case\"Tab\":{let n=!e.autoComplete.rightKey||!e.autoComplete.tabKey;if(!a&&i&&n&&!this.state.editing){t.preventDefault();var l=this.dropdown.getMappedValue(s);return this.input.autocomplete.set.call(this,l),!1}return!0}case\"Enter\":t.preventDefault(),e.hooks.suggestionClick(t,{tagify:this,tagData:s,suggestionElm:i}).then((()=>{if(i)return this.dropdown.selectOption(i),i=this.dropdown.getNextOrPrevOption(i,!r),void this.dropdown.highlightOption(i);this.dropdown.hide(),a||this.addTags(this.state.inputText.trim(),!0)})).catch((t=>t));break;case\"Backspace\":{if(a||this.state.editing.scope)return;const t=this.input.raw.call(this);\"\"!=t&&8203!=t.charCodeAt(0)||(!0===e.backspace?this.removeTags():\"edit\"==e.backspace&&setTimeout(this.editTag.bind(this),0))}}}))}},onMouseOver(t){var e=t.target.closest(this.settings.classNames.dropdownItemSelector);this.dropdown.highlightOption(e)},onMouseLeave(t){this.dropdown.highlightOption()},onClick(t){if(0==t.button&&t.target!=this.DOM.dropdown&&t.target!=this.DOM.dropdown.content){var e=t.target.closest(this.settings.classNames.dropdownItemSelector),i=this.dropdown.getSuggestionDataByNode(e);this.state.actions.selectOption=!0,setTimeout((()=>this.state.actions.selectOption=!1),50),this.settings.hooks.suggestionClick(t,{tagify:this,tagData:i,suggestionElm:e}).then((()=>{e?this.dropdown.selectOption(e,t):this.dropdown.hide()})).catch((t=>console.warn(t)))}},onScroll(t){var e=t.target,i=e.scrollTop/(e.scrollHeight-e.parentNode.clientHeight)*100;this.trigger(\"dropdown:scroll\",{percentage:Math.round(i)})}}},getSuggestionDataByNode(t){var e=t&&t.getAttribute(\"value\");return this.suggestedListItems.find((t=>t.value==e))||null},getNextOrPrevOption(t){let e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];var i=this.dropdown.getAllSuggestionsRefs(),s=i.findIndex((e=>e===t));return e?i[s+1]:i[s-1]},highlightOption(t,e){var i,s=this.settings.classNames.dropdownItemActive;if(this.state.ddItemElm&&(this.state.ddItemElm.classList.remove(s),this.state.ddItemElm.removeAttribute(\"aria-selected\")),!t)return this.state.ddItemData=null,this.state.ddItemElm=null,void this.input.autocomplete.suggest.call(this);i=this.dropdown.getSuggestionDataByNode(t),this.state.ddItemData=i,this.state.ddItemElm=t,t.classList.add(s),t.setAttribute(\"aria-selected\",!0),e&&(t.parentNode.scrollTop=t.clientHeight+t.offsetTop-t.parentNode.clientHeight),this.settings.autoComplete&&(this.input.autocomplete.suggest.call(this,i),this.dropdown.position())},selectOption(t,e){var i=this.settings,s=i.dropdown,a=s.clearOnSelect,n=s.closeOnSelect;if(!t)return this.addTags(this.state.inputText,!0),void(n&&this.dropdown.hide());e=e||{};var o=t.getAttribute(\"value\"),r=\"noMatch\"==o,l=this.suggestedListItems.find((t=>(t.value??t)==o));if(this.trigger(\"dropdown:select\",{data:l,elm:t,event:e}),o&&(l||r)){if(this.state.editing){let t=this.normalizeTags([l])[0];l=i.transformTag.call(this,t)||t,this.onEditTagDone(null,g({__isValid:!0},l))}else this[\"mix\"==i.mode?\"addMixTags\":\"addTags\"]([l||this.input.raw.call(this)],a);this.DOM.input.parentNode&&(setTimeout((()=>{this.DOM.input.focus(),this.toggleFocusClass(!0)})),n&&setTimeout(this.dropdown.hide.bind(this)),t.addEventListener(\"transitionend\",(()=>{this.dropdown.fillHeaderFooter(),setTimeout((()=>t.remove()),100)}),{once:!0}),t.classList.add(this.settings.classNames.dropdownItemHidden))}else n&&setTimeout(this.dropdown.hide.bind(this))},selectAll(t){this.suggestedListItems.length=0,this.dropdown.hide(),this.dropdown.filterListItems(\"\");var e=this.dropdown.filterListItems(\"\");return t||(e=this.state.dropdown.suggestions),this.addTags(e,!0),this},filterListItems(t,e){var i,s,a,n,o,r=this.settings,l=r.dropdown,d=(e=e||{},[]),g=[],p=r.whitelist,u=l.maxItems>=0?l.maxItems:1/0,m=l.searchKeys,v=0;if(!(t=\"select\"==r.mode&&this.value.length&&this.value[0][r.tagTextProp]==t?\"\":t)||!m.length)return d=l.includeSelectedTags?p:p.filter((t=>!this.isTagDuplicate(h(t)?t.value:t))),this.state.dropdown.suggestions=d,d.slice(0,u);function f(t,e){return e.toLowerCase().split(\" \").every((e=>t.includes(e.toLowerCase())))}for(o=l.caseSensitive?\"\"+t:(\"\"+t).toLowerCase();vm.includes(t)))?[\"value\"]:m;l.fuzzySearch&&!e.exact?(a=u.reduce(((t,e)=>t+\" \"+(i[e]||\"\")),\"\").toLowerCase().trim(),l.accentedSearch&&(a=c(a),o=c(o)),t=0==a.indexOf(o),r=a===o,s=f(a,o)):(t=!0,s=u.some((t=>{var s=\"\"+(i[t]||\"\");return l.accentedSearch&&(s=c(s),o=c(o)),l.caseSensitive||(s=s.toLowerCase()),r=s===o,e.exact?s===o:0==s.indexOf(o)}))),n=!l.includeSelectedTags&&this.isTagDuplicate(h(i)?i.value:i),s&&!n&&(r&&t?g.push(i):\"startsWith\"==l.sortby&&t?d.unshift(i):d.push(i))}return this.state.dropdown.suggestions=g.concat(d),\"function\"==typeof l.sortby?l.sortby(g.concat(d),o):g.concat(d).slice(0,u)},getMappedValue(t){var e=this.settings.dropdown.mapValueTo;return e?\"function\"==typeof e?e(t):t[e]||t.value:t.value},createListHTML(t){return g([],t).map(((t,i)=>{\"string\"!=typeof t&&\"number\"!=typeof t||(t={value:t});var s=this.dropdown.getMappedValue(t);return s=\"string\"==typeof s&&this.settings.dropdown.escapeHTML?d(s):s,this.settings.templates.dropdownItem.apply(this,[e(e({},t),{},{mappedValue:s}),this])})).join(\"\")}};const D=\"@yaireo/tagify/\";var M,I={empty:\"empty\",exceed:\"number of tags exceeded\",pattern:\"pattern mismatch\",duplicate:\"already exists\",notAllowed:\"not allowed\"},N={wrapper:(t,e)=>`\\n \\n ​\\n `,tag(t,e){let i=e.settings;return`\\n \\n
\\n ${t[i.tagTextProp]||t.value}\\n
\\n
`},dropdown(t){var e=t.dropdown;return`
\\n
\\n
`},dropdownContent(t){var e=this.settings.templates,i=this.state.dropdown.suggestions;return`\\n ${e.dropdownHeader.call(this,i)}\\n ${t}\\n ${e.dropdownFooter.call(this,i)}\\n `},dropdownItem(t){return`
${t.mappedValue||t.value}
`},dropdownHeader(t){return`
`},dropdownFooter(t){var e=t.length-this.settings.dropdown.maxItems;return e>0?`
\\n ${e} more items. Refine your search.\\n
`:\"\"},dropdownItemNoMatch:null};var _={customBinding(){this.customEventsList.forEach((t=>{this.on(t,this.settings.callbacks[t])}))},binding(){let t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];var e,i=this.events.callbacks,s=t?\"addEventListener\":\"removeEventListener\";if(!this.state.mainEvents||!t){for(var a in this.state.mainEvents=t,t&&!this.listeners.main&&(this.events.bindGlobal.call(this),this.settings.isJQueryPlugin&&jQuery(this.DOM.originalInput).on(\"tagify.removeAllTags\",this.removeAllTags.bind(this))),e=this.listeners.main=this.listeners.main||{focus:[\"input\",i.onFocusBlur.bind(this)],keydown:[\"input\",i.onKeydown.bind(this)],click:[\"scope\",i.onClickScope.bind(this)],dblclick:[\"scope\",i.onDoubleClickScope.bind(this)],paste:[\"input\",i.onPaste.bind(this)],drop:[\"input\",i.onDrop.bind(this)],compositionstart:[\"input\",i.onCompositionStart.bind(this)],compositionend:[\"input\",i.onCompositionEnd.bind(this)]})this.DOM[e[a][0]][s](a,e[a][1]);clearInterval(this.listeners.main.originalInputValueObserverInterval),this.listeners.main.originalInputValueObserverInterval=setInterval(i.observeOriginalInputValue.bind(this),500);var n=this.listeners.main.inputMutationObserver||new MutationObserver(i.onInputDOMChange.bind(this));n.disconnect(),\"mix\"==this.settings.mode&&n.observe(this.DOM.input,{childList:!0})}},bindGlobal(t){var e,i=this.events.callbacks,s=t?\"removeEventListener\":\"addEventListener\";if(this.listeners&&(t||!this.listeners.global))for(e of(this.listeners.global=this.listeners.global||[{type:this.isIE?\"keydown\":\"input\",target:this.DOM.input,cb:i[this.isIE?\"onInputIE\":\"onInput\"].bind(this)},{type:\"keydown\",target:window,cb:i.onWindowKeyDown.bind(this)},{type:\"blur\",target:this.DOM.input,cb:i.onFocusBlur.bind(this)},{type:\"click\",target:document,cb:i.onClickAnywhere.bind(this)}],this.listeners.global))e.target[s](e.type,e.cb)},unbindGlobal(){this.events.bindGlobal.call(this,!0)},callbacks:{onFocusBlur(t){var e=this.settings,i=t.target?this.trim(t.target.textContent):\"\",s=this.value?.[0]?.[e.tagTextProp],a=t.type,n=e.dropdown.enabled>=0,o={relatedTarget:t.relatedTarget},r=this.state.actions.selectOption&&(n||!e.dropdown.closeOnSelect),l=this.state.actions.addNew&&n,d=t.relatedTarget&&v.call(this,t.relatedTarget)&&this.DOM.scope.contains(t.relatedTarget);if(\"blur\"==a){if(t.relatedTarget===this.DOM.scope)return this.dropdown.hide(),void this.DOM.input.focus();this.postUpdate(),e.onChangeAfterBlur&&this.triggerChangeEvent()}if(!r&&!l)if(this.state.hasFocus=\"focus\"==a&&+new Date,this.toggleFocusClass(this.state.hasFocus),\"mix\"!=e.mode){if(\"focus\"==a)return this.trigger(\"focus\",o),void(0!==e.dropdown.enabled&&e.userInput||this.dropdown.show(this.value.length?\"\":void 0));\"blur\"==a&&(this.trigger(\"blur\",o),this.loading(!1),\"select\"==e.mode&&(d&&(this.removeTags(),i=\"\"),s===i&&(i=\"\")),i&&!this.state.actions.selectOption&&e.addTagOnBlur&&e.addTagOn.includes(\"blur\")&&this.addTags(i,!0)),this.DOM.input.removeAttribute(\"style\"),this.dropdown.hide()}else\"focus\"==a?this.trigger(\"focus\",o):\"blur\"==t.type&&(this.trigger(\"blur\",o),this.loading(!1),this.dropdown.hide(),this.state.dropdown.visible=void 0,this.setStateSelection())},onCompositionStart(t){this.state.composing=!0},onCompositionEnd(t){this.state.composing=!1},onWindowKeyDown(t){var e,i=document.activeElement,s=v.call(this,i)&&this.DOM.scope.contains(document.activeElement),a=s&&i.hasAttribute(\"readonly\");if(s&&!a)switch(e=i.nextElementSibling,t.key){case\"Backspace\":this.settings.readonly||(this.removeTags(i),(e||this.DOM.input).focus());break;case\"Enter\":setTimeout(this.editTag.bind(this),0,i)}},onKeydown(t){var e=this.settings;if(!this.state.composing&&e.userInput){\"select\"==e.mode&&e.enforceWhitelist&&this.value.length&&\"Tab\"!=t.key&&t.preventDefault();var i=this.trim(t.target.textContent);this.trigger(\"keydown\",{event:t}),e.hooks.beforeKeyDown(t,{tagify:this}).then((s=>{if(\"mix\"==e.mode){switch(t.key){case\"Left\":case\"ArrowLeft\":this.state.actions.ArrowLeft=!0;break;case\"Delete\":case\"Backspace\":if(this.state.editing)return;var a=document.getSelection(),n=\"Delete\"==t.key&&a.anchorOffset==(a.anchorNode.length||0),r=a.anchorNode.previousSibling,d=1==a.anchorNode.nodeType||!a.anchorOffset&&r&&1==r.nodeType&&a.anchorNode.previousSibling;o(this.DOM.input.innerHTML);var h,g,p,c=this.getTagElms(),m=1===a.anchorNode.length&&a.anchorNode.nodeValue==String.fromCharCode(8203);if(\"edit\"==e.backspace&&d)return h=1==a.anchorNode.nodeType?null:a.anchorNode.previousElementSibling,setTimeout(this.editTag.bind(this),0,h),void t.preventDefault();if(u()&&d instanceof Element)return p=l(d),d.hasAttribute(\"readonly\")||d.remove(),this.DOM.input.focus(),void setTimeout((()=>{w(p),this.DOM.input.click()}));if(\"BR\"==a.anchorNode.nodeName)return;if((n||d)&&1==a.anchorNode.nodeType?g=0==a.anchorOffset?n?c[0]:null:c[Math.min(c.length,a.anchorOffset)-1]:n?g=a.anchorNode.nextElementSibling:d instanceof Element&&(g=d),3==a.anchorNode.nodeType&&!a.anchorNode.nodeValue&&a.anchorNode.previousElementSibling&&t.preventDefault(),(d||n)&&!e.backspace)return void t.preventDefault();if(\"Range\"!=a.type&&!a.anchorOffset&&a.anchorNode==this.DOM.input&&\"Delete\"!=t.key)return void t.preventDefault();if(\"Range\"!=a.type&&g&&g.hasAttribute(\"readonly\"))return void w(l(g));\"Delete\"==t.key&&m&&T(a.anchorNode.nextSibling)&&this.removeTags(a.anchorNode.nextSibling),clearTimeout(M),M=setTimeout((()=>{var t=document.getSelection();o(this.DOM.input.innerHTML),!n&&t.anchorNode.previousSibling,this.value=[].map.call(c,((t,e)=>{var i=T(t);if(t.parentNode||i.readonly)return i;this.trigger(\"remove\",{tag:t,index:e,data:i})})).filter((t=>t))}),20)}return!0}var v=\"manual\"==e.dropdown.position;switch(t.key){case\"Backspace\":\"select\"==e.mode&&e.enforceWhitelist&&this.value.length?this.removeTags():this.state.dropdown.visible&&\"manual\"!=e.dropdown.position||\"\"!=t.target.textContent&&8203!=i.charCodeAt(0)||(!0===e.backspace?this.removeTags():\"edit\"==e.backspace&&setTimeout(this.editTag.bind(this),0));break;case\"Esc\":case\"Escape\":if(this.state.dropdown.visible)return;t.target.blur();break;case\"Down\":case\"ArrowDown\":this.state.dropdown.visible||this.dropdown.show();break;case\"ArrowRight\":{let t=this.state.inputSuggestion||this.state.ddItemData;if(t&&e.autoComplete.rightKey)return void this.addTags([t],!0);break}case\"Tab\":{let s=\"select\"==e.mode;if(!i||s)return!0;t.preventDefault()}case\"Enter\":if(this.state.dropdown.visible&&!v)return;t.preventDefault(),setTimeout((()=>{this.state.dropdown.visible&&!v||this.state.actions.selectOption||!e.addTagOn.includes(t.key.toLowerCase())||this.addTags(i,!0)}))}})).catch((t=>t))}},onInput(t){this.postUpdate();var e=this.settings;if(\"mix\"==e.mode)return this.events.callbacks.onMixTagsInput.call(this,t);var i=this.input.normalize.call(this,void 0,{trim:!1}),s=i.length>=e.dropdown.enabled,a={value:i,inputElm:this.DOM.input},n=this.validateTag({value:i});\"select\"==e.mode&&this.toggleScopeValidation(n),a.isValid=n,this.state.inputText!=i&&(this.input.set.call(this,i,!1),-1!=i.search(e.delimiters)?this.addTags(i)&&this.input.set.call(this):e.dropdown.enabled>=0&&this.dropdown[s?\"show\":\"hide\"](i),this.trigger(\"input\",a))},onMixTagsInput(t){var e,i,s,a,n,o,r,l,d=this.settings,h=this.value.length,p=this.getTagElms(),c=document.createDocumentFragment(),m=window.getSelection().getRangeAt(0),v=[].map.call(p,(t=>T(t).value));if(\"deleteContentBackward\"==t.inputType&&u()&&this.events.callbacks.onKeydown.call(this,{target:t.target,key:\"Backspace\"}),b(this.getTagElms()),this.value.slice().forEach((t=>{t.readonly&&!v.includes(t.value)&&c.appendChild(this.createTagElem(t))})),c.childNodes.length&&(m.insertNode(c),this.setRangeAtStartEnd(!1,c.lastChild)),p.length!=h)return this.value=[].map.call(this.getTagElms(),(t=>T(t))),void this.update({withoutChangeEvent:!0});if(this.hasMaxTags())return!0;if(window.getSelection&&(o=window.getSelection()).rangeCount>0&&3==o.anchorNode.nodeType){if((m=o.getRangeAt(0).cloneRange()).collapse(!0),m.setStart(o.focusNode,0),s=(e=m.toString().slice(0,m.endOffset)).split(d.pattern).length-1,(i=e.match(d.pattern))&&(a=e.slice(e.lastIndexOf(i[i.length-1]))),a){if(this.state.actions.ArrowLeft=!1,this.state.tag={prefix:a.match(d.pattern)[0],value:a.replace(d.pattern,\"\")},this.state.tag.baseOffset=o.baseOffset-this.state.tag.value.length,l=this.state.tag.value.match(d.delimiters))return this.state.tag.value=this.state.tag.value.replace(d.delimiters,\"\"),this.state.tag.delimiters=l[0],this.addTags(this.state.tag.value,d.dropdown.clearOnSelect),void this.dropdown.hide();n=this.state.tag.value.length>=d.dropdown.enabled;try{r=(r=this.state.flaggedTags[this.state.tag.baseOffset]).prefix==this.state.tag.prefix&&r.value[0]==this.state.tag.value[0],this.state.flaggedTags[this.state.tag.baseOffset]&&!this.state.tag.value&&delete this.state.flaggedTags[this.state.tag.baseOffset]}catch(t){}(r||s{this.update({withoutChangeEvent:!0}),this.trigger(\"input\",g({},this.state.tag,{textContent:this.DOM.input.textContent})),this.state.tag&&this.dropdown[n?\"show\":\"hide\"](this.state.tag.value)}),10)},onInputIE(t){var e=this;setTimeout((function(){e.events.callbacks.onInput.call(e,t)}))},observeOriginalInputValue(){this.DOM.originalInput.parentNode||this.destroy(),this.DOM.originalInput.value!=this.DOM.originalInput.tagifyValue&&this.loadOriginalValues()},onClickAnywhere(t){t.target==this.DOM.scope||this.DOM.scope.contains(t.target)||(this.toggleFocusClass(!1),this.state.hasFocus=!1)},onClickScope(t){var e=this.settings,i=t.target.closest(\".\"+e.classNames.tag),s=+new Date-this.state.hasFocus;if(t.target!=this.DOM.scope){if(!t.target.classList.contains(e.classNames.tagX))return i?(this.trigger(\"click\",{tag:i,index:this.getNodeIndex(i),data:T(i),event:t}),void(1!==e.editTags&&1!==e.editTags.clicks||this.events.callbacks.onDoubleClickScope.call(this,t))):void(t.target==this.DOM.input&&(\"mix\"==e.mode&&this.fixFirefoxLastTagNoCaret(),s>500)?this.state.dropdown.visible?this.dropdown.hide():0===e.dropdown.enabled&&\"mix\"!=e.mode&&this.dropdown.show(this.value.length?\"\":void 0):\"select\"!=e.mode||0!==e.dropdown.enabled||this.state.dropdown.visible||this.dropdown.show());this.removeTags(t.target.parentNode)}else this.DOM.input.focus()},onPaste(t){t.preventDefault();var e,i,s=this.settings;if(\"select\"==s.mode&&s.enforceWhitelist||!s.userInput)return!1;s.readonly||(e=t.clipboardData||window.clipboardData,i=e.getData(\"Text\"),s.hooks.beforePaste(t,{tagify:this,pastedText:i,clipboardData:e}).then((e=>{void 0===e&&(e=i),e&&(this.injectAtCaret(e,window.getSelection().getRangeAt(0)),\"mix\"==this.settings.mode?this.events.callbacks.onMixTagsInput.call(this,t):this.settings.pasteAsTags?this.addTags(this.state.inputText+e,!0):(this.state.inputText=e,this.dropdown.show(e)))})).catch((t=>t)))},onDrop(t){t.preventDefault()},onEditTagInput(t,e){var i=t.closest(\".\"+this.settings.classNames.tag),s=this.getNodeIndex(i),a=T(i),n=this.input.normalize.call(this,t),o={[this.settings.tagTextProp]:n,__tagId:a.__tagId},r=this.validateTag(o);this.editTagChangeDetected(g(a,o))||!0!==t.originalIsValid||(r=!0),i.classList.toggle(this.settings.classNames.tagInvalid,!0!==r),a.__isValid=r,i.title=!0===r?a.title||a.value:r,n.length>=this.settings.dropdown.enabled&&(this.state.editing&&(this.state.editing.value=n),this.dropdown.show(n)),this.trigger(\"edit:input\",{tag:i,index:s,data:g({},this.value[s],{newValue:n}),event:e})},onEditTagPaste(t,e){var i=(e.clipboardData||window.clipboardData).getData(\"Text\");e.preventDefault();var s=f(i);this.setRangeAtStartEnd(!1,s)},onEditTagFocus(t){this.state.editing={scope:t,input:t.querySelector(\"[contenteditable]\")}},onEditTagBlur(t){if(this.state.editing&&(this.state.hasFocus||this.toggleFocusClass(),this.DOM.scope.contains(t))){var e,i,s=this.settings,a=t.closest(\".\"+s.classNames.tag),n=T(a),o=this.input.normalize.call(this,t),r={[s.tagTextProp]:o,__tagId:n.__tagId},l=n.__originalData,d=this.editTagChangeDetected(g(n,r)),h=this.validateTag(r);if(o)if(d){if(e=this.hasMaxTags(),i=g({},l,{[s.tagTextProp]:this.trim(o),__isValid:h}),s.transformTag.call(this,i,l),!0!==(h=(!e||!0===l.__isValid)&&this.validateTag(i))){if(this.trigger(\"invalid\",{data:i,tag:a,message:h}),s.editTags.keepInvalid)return;s.keepInvalidTags?i.__isValid=h:i=l}else s.keepInvalidTags&&(delete i.title,delete i[\"aria-invalid\"],delete i.class);this.onEditTagDone(a,i)}else this.onEditTagDone(a,l);else this.onEditTagDone(a)}},onEditTagkeydown(t,e){if(!this.state.composing)switch(this.trigger(\"edit:keydown\",{event:t}),t.key){case\"Esc\":case\"Escape\":this.state.editing=!1,!!e.__tagifyTagData.__originalData.value?e.parentNode.replaceChild(e.__tagifyTagData.__originalHTML,e):e.remove();break;case\"Enter\":case\"Tab\":t.preventDefault(),t.target.blur()}},onDoubleClickScope(t){var e,i,s=t.target.closest(\".\"+this.settings.classNames.tag),a=T(s),n=this.settings;s&&n.userInput&&!1!==a.editable&&(e=s.classList.contains(this.settings.classNames.tagEditing),i=s.hasAttribute(\"readonly\"),\"select\"==n.mode||n.readonly||e||i||!this.settings.editTags||this.editTag(s),this.toggleFocusClass(!0),this.trigger(\"dblclick\",{tag:s,index:this.getNodeIndex(s),data:T(s)}))},onInputDOMChange(t){t.forEach((t=>{t.addedNodes.forEach((t=>{if(\"

\"==t.outerHTML)t.replaceWith(document.createElement(\"br\"));else if(1==t.nodeType&&t.querySelector(this.settings.classNames.tagSelector)){let e=document.createTextNode(\"\");3==t.childNodes[0].nodeType&&\"BR\"!=t.previousSibling.nodeName&&(e=document.createTextNode(\"\\n\")),t.replaceWith(e,...[...t.childNodes].slice(0,-1)),w(e)}else if(v.call(this,t))if(3!=t.previousSibling?.nodeType||t.previousSibling.textContent||t.previousSibling.remove(),t.previousSibling&&\"BR\"==t.previousSibling.nodeName){t.previousSibling.replaceWith(\"\\n​\");let e=t.nextSibling,i=\"\";for(;e;)i+=e.textContent,e=e.nextSibling;i.trim()&&w(t.previousSibling)}else t.previousSibling&&!T(t.previousSibling)||t.before(\"​\")})),t.removedNodes.forEach((t=>{t&&\"BR\"==t.nodeName&&v.call(this,e)&&(this.removeTags(e),this.fixFirefoxLastTagNoCaret())}))}));var e=this.DOM.input.lastChild;e&&\"\"==e.nodeValue&&e.remove(),e&&\"BR\"==e.nodeName||this.DOM.input.appendChild(document.createElement(\"br\"))}}};function S(t,e){if(!t){console.warn(\"Tagify:\",\"input element not found\",t);const e=new Proxy(this,{get:()=>()=>e});return e}if(t.__tagify)return console.warn(\"Tagify: \",\"input element is already Tagified - Same instance is returned.\",t),t.__tagify;var i;g(this,function(t){var e=document.createTextNode(\"\");function i(t,i,s){s&&i.split(/\\s+/g).forEach((i=>e[t+\"EventListener\"].call(e,i,s)))}return{off(t,e){return i(\"remove\",t,e),this},on(t,e){return e&&\"function\"==typeof e&&i(\"add\",t,e),this},trigger(i,s,a){var n;if(a=a||{cloneData:!0},i)if(t.settings.isJQueryPlugin)\"remove\"==i&&(i=\"removeTag\"),jQuery(t.DOM.originalInput).triggerHandler(i,[s]);else{try{var o=\"object\"==typeof s?s:{value:s};if((o=a.cloneData?g({},o):o).tagify=this,s.event&&(o.event=this.cloneEvent(s.event)),s instanceof Object)for(var r in s)s[r]instanceof HTMLElement&&(o[r]=s[r]);n=new CustomEvent(i,{detail:o})}catch(t){console.warn(t)}e.dispatchEvent(n)}}}}(this)),this.isFirefox=/firefox|fxios/i.test(navigator.userAgent)&&!/seamonkey/i.test(navigator.userAgent),this.isIE=window.document.documentMode,e=e||{},this.getPersistedData=(i=e.id,t=>{let e,s=\"/\"+t;if(1==localStorage.getItem(D+i+\"/v\",1))try{e=JSON.parse(localStorage[D+i+s])}catch(t){}return e}),this.setPersistedData=(t=>t?(localStorage.setItem(D+t+\"/v\",1),(e,i)=>{let s=\"/\"+i,a=JSON.stringify(e);e&&i&&(localStorage.setItem(D+t+s,a),dispatchEvent(new Event(\"storage\")))}):()=>{})(e.id),this.clearPersistedData=(t=>e=>{const i=D+\"/\"+t+\"/\";if(e)localStorage.removeItem(i+e);else for(let t in localStorage)t.includes(i)&&localStorage.removeItem(t)})(e.id),this.applySettings(t,e),this.state={inputText:\"\",editing:!1,composing:!1,actions:{},mixMode:{},dropdown:{},flaggedTags:{}},this.value=[],this.listeners={},this.DOM={},this.build(t),x.call(this),this.getCSSVars(),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this),t.autofocus&&this.DOM.input.focus(),t.__tagify=this}return S.prototype={_dropdown:O,placeCaretAfterNode:w,getSetTagData:T,helpers:{sameStr:s,removeCollectionProp:a,omit:n,isObject:h,parseHTML:r,escapeHTML:d,extend:g,concatWithoutDups:p,getUID:m,isNodeTag:v},customEventsList:[\"change\",\"add\",\"remove\",\"invalid\",\"input\",\"click\",\"keydown\",\"focus\",\"blur\",\"edit:input\",\"edit:beforeUpdate\",\"edit:updated\",\"edit:start\",\"edit:keydown\",\"dropdown:show\",\"dropdown:hide\",\"dropdown:select\",\"dropdown:updated\",\"dropdown:noMatch\",\"dropdown:scroll\"],dataProps:[\"__isValid\",\"__removed\",\"__originalData\",\"__originalHTML\",\"__tagId\"],trim(t){return this.settings.trim&&t&&\"string\"==typeof t?t.trim():t},parseHTML:r,templates:N,parseTemplate(t,e){return r((t=this.settings.templates[t]||t).apply(this,e))},set whitelist(t){const e=t&&Array.isArray(t);this.settings.whitelist=e?t:[],this.setPersistedData(e?t:[],\"whitelist\")},get whitelist(){return this.settings.whitelist},generateClassSelectors(t){for(let e in t){let i=e;Object.defineProperty(t,i+\"Selector\",{get(){return\".\"+this[i].split(\" \")[0]}})}},applySettings(t,i){y.templates=this.templates;var s=g({},y,\"mix\"==i.mode?{dropdown:{position:\"text\"}}:{}),a=this.settings=g({},s,i);if(a.disabled=t.hasAttribute(\"disabled\"),a.readonly=a.readonly||t.hasAttribute(\"readonly\"),a.placeholder=d(t.getAttribute(\"placeholder\")||a.placeholder||\"\"),a.required=t.hasAttribute(\"required\"),this.generateClassSelectors(a.classNames),void 0===a.dropdown.includeSelectedTags&&(a.dropdown.includeSelectedTags=a.duplicates),this.isIE&&(a.autoComplete=!1),[\"whitelist\",\"blacklist\"].forEach((e=>{var i=t.getAttribute(\"data-\"+e);i&&(i=i.split(a.delimiters))instanceof Array&&(a[e]=i)})),\"autoComplete\"in i&&!h(i.autoComplete)&&(a.autoComplete=y.autoComplete,a.autoComplete.enabled=i.autoComplete),\"mix\"==a.mode&&(a.pattern=a.pattern||/@/,a.autoComplete.rightKey=!0,a.delimiters=i.delimiters||null,a.tagTextProp&&!a.dropdown.searchKeys.includes(a.tagTextProp)&&a.dropdown.searchKeys.push(a.tagTextProp)),t.pattern)try{a.pattern=new RegExp(t.pattern)}catch(t){}if(a.delimiters){a._delimiters=a.delimiters;try{a.delimiters=new RegExp(this.settings.delimiters,\"g\")}catch(t){}}a.disabled&&(a.userInput=!1),this.TEXTS=e(e({},I),a.texts||{}),(\"select\"!=a.mode||i.dropdown?.enabled)&&a.userInput||(a.dropdown.enabled=0),a.dropdown.appendTarget=i.dropdown?.appendTarget||document.body;let n=this.getPersistedData(\"whitelist\");Array.isArray(n)&&(this.whitelist=Array.isArray(a.whitelist)?p(a.whitelist,n):n)},getAttributes(t){var e,i=this.getCustomAttributes(t),s=\"\";for(e in i)s+=\" \"+e+(void 0!==t[e]?`=\"${i[e]}\"`:\"\");return s},getCustomAttributes(t){if(!h(t))return\"\";var e,i={};for(e in t)\"__\"!=e.slice(0,2)&&\"class\"!=e&&t.hasOwnProperty(e)&&void 0!==t[e]&&(i[e]=d(t[e]));return i},setStateSelection(){var t=window.getSelection(),e={anchorOffset:t.anchorOffset,anchorNode:t.anchorNode,range:t.getRangeAt&&t.rangeCount&&t.getRangeAt(0)};return this.state.selection=e,e},getCSSVars(){var t=getComputedStyle(this.DOM.scope,null);var e;this.CSSVars={tagHideTransition:(t=>{let e=t.value;return\"s\"==t.unit?1e3*e:e})(function(t){if(!t)return{};var e=(t=t.trim().split(\" \")[0]).split(/\\d+/g).filter((t=>t)).pop().trim();return{value:+t.split(e).filter((t=>t))[0].trim(),unit:e}}((e=\"tag-hide-transition\",t.getPropertyValue(\"--\"+e))))}},build(t){var e=this.DOM;this.settings.mixMode.integrated?(e.originalInput=null,e.scope=t,e.input=t):(e.originalInput=t,e.originalInput_tabIndex=t.tabIndex,e.scope=this.parseTemplate(\"wrapper\",[t,this.settings]),e.input=e.scope.querySelector(this.settings.classNames.inputSelector),t.parentNode.insertBefore(e.scope,t),t.tabIndex=-1)},destroy(){this.events.unbindGlobal.call(this),this.DOM.scope.parentNode.removeChild(this.DOM.scope),this.DOM.originalInput.tabIndex=this.DOM.originalInput_tabIndex,delete this.DOM.originalInput.__tagify,this.dropdown.hide(!0),clearTimeout(this.dropdownHide__bindEventsTimeout),clearInterval(this.listeners.main.originalInputValueObserverInterval)},loadOriginalValues(t){var e,i=this.settings;if(this.state.blockChangeEvent=!0,void 0===t){const e=this.getPersistedData(\"value\");t=e&&!this.DOM.originalInput.value?e:i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value}if(this.removeAllTags(),t)if(\"mix\"==i.mode)this.parseMixTags(t),(e=this.DOM.input.lastChild)&&\"BR\"==e.tagName||this.DOM.input.insertAdjacentHTML(\"beforeend\",\"
\");else{try{JSON.parse(t)instanceof Array&&(t=JSON.parse(t))}catch(t){}this.addTags(t,!0).forEach((t=>t&&t.classList.add(i.classNames.tagNoAnimation)))}else this.postUpdate();this.state.lastOriginalValueReported=i.mixMode.integrated?\"\":this.DOM.originalInput.value},cloneEvent(t){var e={};for(var i in t)\"path\"!=i&&(e[i]=t[i]);return e},loading(t){return this.state.isLoading=t,this.DOM.scope.classList[t?\"add\":\"remove\"](this.settings.classNames.scopeLoading),this},tagLoading(t,e){return t&&t.classList[e?\"add\":\"remove\"](this.settings.classNames.tagLoading),this},toggleClass(t,e){\"string\"==typeof t&&this.DOM.scope.classList.toggle(t,e)},toggleScopeValidation(t){var e=!0===t||void 0===t;!this.settings.required&&t&&t===this.TEXTS.empty&&(e=!0),this.toggleClass(this.settings.classNames.tagInvalid,!e),this.DOM.scope.title=e?\"\":t},toggleFocusClass(t){this.toggleClass(this.settings.classNames.focus,!!t)},triggerChangeEvent:function(){if(!this.settings.mixMode.integrated){var t=this.DOM.originalInput,e=this.state.lastOriginalValueReported!==t.value,i=new CustomEvent(\"change\",{bubbles:!0});e&&(this.state.lastOriginalValueReported=t.value,i.simulated=!0,t._valueTracker&&t._valueTracker.setValue(Math.random()),t.dispatchEvent(i),this.trigger(\"change\",this.state.lastOriginalValueReported),t.value=this.state.lastOriginalValueReported)}},events:_,fixFirefoxLastTagNoCaret(){},setRangeAtStartEnd(t,e){if(e){t=\"number\"==typeof t?t:!!t,e=e.lastChild||e;var i=document.getSelection();if(i.focusNode instanceof Element&&!this.DOM.input.contains(i.focusNode))return!0;try{i.rangeCount>=1&&[\"Start\",\"End\"].forEach((s=>i.getRangeAt(0)[\"set\"+s](e,t||e.length)))}catch(t){console.warn(\"Tagify: \",t)}}},insertAfterTag(t,e){if(e=e||this.settings.mixMode.insertAfterTag,t&&t.parentNode&&e)return e=\"string\"==typeof e?document.createTextNode(e):e,t.parentNode.insertBefore(e,t.nextSibling),e},editTagChangeDetected(t){var e=t.__originalData;for(var i in e)if(!this.dataProps.includes(i)&&t[i]!=e[i])return!0;return!1},getTagTextNode(t){return t.querySelector(this.settings.classNames.tagTextSelector)},setTagTextNode(t,e){this.getTagTextNode(t).innerHTML=d(e)},editTag(t,e){t=t||this.getLastTag(),e=e||{},this.dropdown.hide();var i=this.settings,s=this.getTagTextNode(t),a=this.getNodeIndex(t),n=T(t),o=this.events.callbacks,r=!0;if(s){if(!(n instanceof Object&&\"editable\"in n)||n.editable)return n=T(t,{__originalData:g({},n),__originalHTML:t.cloneNode(!0)}),T(n.__originalHTML,n.__originalData),s.setAttribute(\"contenteditable\",!0),t.classList.add(i.classNames.tagEditing),s.addEventListener(\"focus\",o.onEditTagFocus.bind(this,t)),s.addEventListener(\"blur\",o.onEditTagBlur.bind(this,this.getTagTextNode(t))),s.addEventListener(\"input\",o.onEditTagInput.bind(this,s)),s.addEventListener(\"paste\",o.onEditTagPaste.bind(this,s)),s.addEventListener(\"keydown\",(e=>o.onEditTagkeydown.call(this,e,t))),s.addEventListener(\"compositionstart\",o.onCompositionStart.bind(this)),s.addEventListener(\"compositionend\",o.onCompositionEnd.bind(this)),e.skipValidation||(r=this.editTagToggleValidity(t)),s.originalIsValid=r,this.trigger(\"edit:start\",{tag:t,index:a,data:n,isValid:r}),s.focus(),this.setRangeAtStartEnd(!1,s),this}else console.warn(\"Cannot find element in Tag template: .\",i.classNames.tagTextSelector)},editTagToggleValidity(t,e){var i;if(e=e||T(t))return(i=!(\"__isValid\"in e)||!0===e.__isValid)||this.removeTagsFromValue(t),this.update(),t.classList.toggle(this.settings.classNames.tagNotAllowed,!i),e.__isValid=i,e.__isValid;console.warn(\"tag has no data: \",t,e)},onEditTagDone(t,e){t=t||this.state.editing.scope,e=e||{};var i,s={tag:t,index:this.getNodeIndex(t),previousData:T(t),data:e},a=this.settings;this.trigger(\"edit:beforeUpdate\",s,{cloneData:!1}),this.state.editing=!1,delete e.__originalData,delete e.__originalHTML,t&&((i=e[a.tagTextProp])?i.trim()&&i:a.tagTextProp in e?void 0:e.value)?(t=this.replaceTag(t,e),this.editTagToggleValidity(t,e),a.a11y.focusableTags?t.focus():w(t)):t&&this.removeTags(t),this.trigger(\"edit:updated\",s),this.dropdown.hide(),this.settings.keepInvalidTags&&this.reCheckInvalidTags()},replaceTag(t,e){e&&e.value||(e=t.__tagifyTagData),e.__isValid&&1!=e.__isValid&&g(e,this.getInvalidTagAttrs(e,e.__isValid));var i=this.createTagElem(e);return t.parentNode.replaceChild(i,t),this.updateValueByDOMTags(),i},updateValueByDOMTags(){this.value.length=0,[].forEach.call(this.getTagElms(),(t=>{t.classList.contains(this.settings.classNames.tagNotAllowed.split(\" \")[0])||this.value.push(T(t))})),this.update()},injectAtCaret(t,e){if(!(e=e||this.state.selection?.range)&&t)return this.appendMixTags(t),this;let i=f(t,e);return this.setRangeAtStartEnd(!1,i),this.updateValueByDOMTags(),this.update(),this},input:{set(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"\",e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];var i=this.settings.dropdown.closeOnSelect;this.state.inputText=t,e&&(this.DOM.input.innerHTML=d(\"\"+t)),!t&&i&&this.dropdown.hide.bind(this),this.input.autocomplete.suggest.call(this),this.input.validate.call(this)},raw(){return this.DOM.input.textContent},validate(){var t=!this.state.inputText||!0===this.validateTag({value:this.state.inputText});return this.DOM.input.classList.toggle(this.settings.classNames.inputInvalid,!t),t},normalize(t,e){var i=t||this.DOM.input,s=[];i.childNodes.forEach((t=>3==t.nodeType&&s.push(t.nodeValue))),s=s.join(\"\\n\");try{s=s.replace(/(?:\\r\\n|\\r|\\n)/g,this.settings.delimiters.source.charAt(0))}catch(t){}return s=s.replace(/\\s/g,\" \"),e?.trim?this.trim(s):s},autocomplete:{suggest(t){if(this.settings.autoComplete.enabled){\"string\"==typeof(t=t||{value:\"\"})&&(t={value:t});var e=this.dropdown.getMappedValue(t);if(\"number\"!=typeof e){var i=e.substr(0,this.state.inputText.length).toLowerCase(),s=e.substring(this.state.inputText.length);e&&this.state.inputText&&i==this.state.inputText.toLowerCase()?(this.DOM.input.setAttribute(\"data-suggest\",s),this.state.inputSuggestion=t):(this.DOM.input.removeAttribute(\"data-suggest\"),delete this.state.inputSuggestion)}}},set(t){var e=this.DOM.input.getAttribute(\"data-suggest\"),i=t||(e?this.state.inputText+e:null);return!!i&&(\"mix\"==this.settings.mode?this.replaceTextWithNode(document.createTextNode(this.state.tag.prefix+i)):(this.input.set.call(this,i),this.setRangeAtStartEnd(!1,this.DOM.input)),this.input.autocomplete.suggest.call(this),this.dropdown.hide(),!0)}}},getTagIdx(t){return this.value.findIndex((e=>e.__tagId==(t||{}).__tagId))},getNodeIndex(t){var e=0;if(t)for(;t=t.previousElementSibling;)e++;return e},getTagElms(){for(var t=arguments.length,e=new Array(t),i=0;i{a.__tagifyTagData&&s(this.trim(a.__tagifyTagData.value),t,i)&&e.push(n)})),e},getTagElmByValue(t){var e=this.getTagIndexByValue(t)[0];return this.getTagElms()[e]},flashTag(t){t&&(t.classList.add(this.settings.classNames.tagFlash),setTimeout((()=>{t.classList.remove(this.settings.classNames.tagFlash)}),100))},isTagBlacklisted(t){return t=this.trim(t.toLowerCase()),this.settings.blacklist.filter((e=>(\"\"+e).toLowerCase()==t)).length},isTagWhitelisted(t){return!!this.getWhitelistItem(t)},getWhitelistItem(t,e,i){e=e||\"value\";var a,n=this.settings;return(i=i||n.whitelist).some((i=>{var o=\"string\"==typeof i?i:i[e]||i.value;if(s(o,t,n.dropdown.caseSensitive,n.trim))return a=\"string\"==typeof i?{value:i}:i,!0})),a||\"value\"!=e||\"value\"==n.tagTextProp||(a=this.getWhitelistItem(t,n.tagTextProp,i)),a},validateTag(t){var e=this.settings,i=\"value\"in t?\"value\":e.tagTextProp,s=this.trim(t[i]+\"\");return(t[i]+\"\").trim()?\"mix\"!=e.mode&&e.pattern&&e.pattern instanceof RegExp&&!e.pattern.test(s)?this.TEXTS.pattern:!e.duplicates&&this.isTagDuplicate(s,e.dropdown.caseSensitive,t.__tagId)?this.TEXTS.duplicate:this.isTagBlacklisted(s)||e.enforceWhitelist&&!this.isTagWhitelisted(s)?this.TEXTS.notAllowed:!e.validate||e.validate(t):this.TEXTS.empty},getInvalidTagAttrs(t,e){return{\"aria-invalid\":!0,class:`${t.class||\"\"} ${this.settings.classNames.tagNotAllowed}`.trim(),title:e}},hasMaxTags(){return this.value.length>=this.settings.maxTags&&this.TEXTS.exceed},setReadonly(t,e){var i=this.settings;document.activeElement.blur(),i[e||\"readonly\"]=t,this.DOM.scope[(t?\"set\":\"remove\")+\"Attribute\"](e||\"readonly\",!0),this.settings.userInput=!0,this.setContentEditable(!t)},setContentEditable(t){this.settings.userInput&&(this.DOM.input.contentEditable=t,this.DOM.input.tabIndex=t?0:-1)},setDisabled(t){this.setReadonly(t,\"disabled\")},normalizeTags(t){var e=this.settings,i=e.whitelist,s=e.delimiters,a=e.mode,n=e.tagTextProp,o=[],r=!!i&&i[0]instanceof Object,l=Array.isArray(t),d=l&&t[0].value,h=t=>(t+\"\").split(s).filter((t=>t)).map((t=>({[n]:this.trim(t),value:this.trim(t)})));if(\"number\"==typeof t&&(t=t.toString()),\"string\"==typeof t){if(!t.trim())return[];t=h(t)}else l&&(t=[].concat(...t.map((t=>null!=t.value?t:h(t)))));return r&&!d&&(t.forEach((t=>{var e=o.map((t=>t.value)),i=this.dropdown.filterListItems.call(this,t[n],{exact:!0});this.settings.duplicates||(i=i.filter((t=>!e.includes(t.value))));var s=i.length>1?this.getWhitelistItem(t[n],n,i):i[0];s&&s instanceof Object?o.push(s):\"mix\"!=a&&(null==t.value&&(t.value=t[n]),o.push(t))})),o.length&&(t=o)),t},parseMixTags(t){var e=this.settings,i=e.mixTagsInterpolator,s=e.duplicates,a=e.transformTag,n=e.enforceWhitelist,o=e.maxTags,r=e.tagTextProp,l=[];t=t.split(i[0]).map(((t,e)=>{var d,h,g,p=t.split(i[1]),c=p[0],u=l.length==o;try{if(c==+c)throw Error;h=JSON.parse(c)}catch(t){h=this.normalizeTags(c)[0]||{value:c}}if(a.call(this,h),u||!(p.length>1)||n&&!this.isTagWhitelisted(h.value)||!s&&this.isTagDuplicate(h.value)){if(t)return e?i[0]+t:t}else h[d=h[r]?r:\"value\"]=this.trim(h[d]),g=this.createTagElem(h),l.push(h),g.classList.add(this.settings.classNames.tagNoAnimation),p[0]=g.outerHTML,this.value.push(h);return p.join(\"\")})).join(\"\"),this.DOM.input.innerHTML=t,this.DOM.input.appendChild(document.createTextNode(\"\")),this.DOM.input.normalize();var d=this.getTagElms();return d.forEach(((t,e)=>T(t,l[e]))),this.update({withoutChangeEvent:!0}),b(d,this.state.hasFocus),t},replaceTextWithNode(t,e){if(this.state.tag||e){e=e||this.state.tag.prefix+this.state.tag.value;var i,s,a=this.state.selection||window.getSelection(),n=a.anchorNode,o=this.state.tag.delimiters?this.state.tag.delimiters.length:0;return n.splitText(a.anchorOffset-o),-1==(i=n.nodeValue.lastIndexOf(e))?!0:(s=n.splitText(i),t&&n.parentNode.replaceChild(t,s),!0)}},selectTag(t,e){var i=this.settings;if(!i.enforceWhitelist||this.isTagWhitelisted(e.value)){this.input.set.call(this,e[i.tagTextProp]||e.value,!0),this.state.actions.selectOption&&setTimeout((()=>this.setRangeAtStartEnd(!1,this.DOM.input)));var s=this.getLastTag();return s?this.replaceTag(s,e):this.appendTag(t),this.value[0]=e,this.update(),this.trigger(\"add\",{tag:t,data:e}),[t]}},addEmptyTag(t){var e=g({value:\"\"},t||{}),i=this.createTagElem(e);T(i,e),this.appendTag(i),this.editTag(i,{skipValidation:!0})},addTags(t,e,i){var s=[],a=this.settings,n=[],o=document.createDocumentFragment();if(i=i||a.skipInvalid,!t||0==t.length)return s;switch(t=this.normalizeTags(t),a.mode){case\"mix\":return this.addMixTags(t);case\"select\":e=!1,this.removeAllTags()}return this.DOM.input.removeAttribute(\"style\"),t.forEach((t=>{var e,r={},l=Object.assign({},t,{value:t.value+\"\"});if(t=Object.assign({},l),a.transformTag.call(this,t),t.__isValid=this.hasMaxTags()||this.validateTag(t),!0!==t.__isValid){if(i)return;if(g(r,this.getInvalidTagAttrs(t,t.__isValid),{__preInvalidData:l}),t.__isValid==this.TEXTS.duplicate&&this.flashTag(this.getTagElmByValue(t.value)),!a.createInvalidTags)return void n.push(t.value)}if(\"readonly\"in t&&(t.readonly?r[\"aria-readonly\"]=!0:delete t.readonly),e=this.createTagElem(t,r),s.push(e),\"select\"==a.mode)return this.selectTag(e,t);o.appendChild(e),t.__isValid&&!0===t.__isValid?(this.value.push(t),this.trigger(\"add\",{tag:e,index:this.value.length-1,data:t})):(this.trigger(\"invalid\",{data:t,index:this.value.length,tag:e,message:t.__isValid}),a.keepInvalidTags||setTimeout((()=>this.removeTags(e,!0)),1e3)),this.dropdown.position()})),this.appendTag(o),this.update(),t.length&&e&&(this.input.set.call(this,a.createInvalidTags?\"\":n.join(a._delimiters)),this.setRangeAtStartEnd(!1,this.DOM.input)),a.dropdown.enabled&&this.dropdown.refilter(),s},addMixTags(t){if((t=this.normalizeTags(t))[0].prefix||this.state.tag)return this.prefixedTextToTag(t[0]);var e=document.createDocumentFragment();return t.forEach((t=>{var i=this.createTagElem(t);e.appendChild(i)})),this.appendMixTags(e),e},appendMixTags(t){var e=!!this.state.selection;e?this.injectAtCaret(t):(this.DOM.input.focus(),(e=this.setStateSelection()).range.setStart(this.DOM.input,e.range.endOffset),e.range.setEnd(this.DOM.input,e.range.endOffset),this.DOM.input.appendChild(t),this.updateValueByDOMTags(),this.update())},prefixedTextToTag(t){var e,i=this.settings,s=this.state.tag.delimiters;if(i.transformTag.call(this,t),t.prefix=t.prefix||this.state.tag?this.state.tag.prefix:(i.pattern.source||i.pattern)[0],e=this.createTagElem(t),this.replaceTextWithNode(e)||this.DOM.input.appendChild(e),setTimeout((()=>e.classList.add(this.settings.classNames.tagNoAnimation)),300),this.value.push(t),this.update(),!s){var a=this.insertAfterTag(e)||e;setTimeout(w,0,a)}return this.state.tag=null,this.trigger(\"add\",g({},{tag:e},{data:t})),e},appendTag(t){var e=this.DOM,i=e.input;e.scope.insertBefore(t,i)},createTagElem(t,i){t.__tagId=m();var s,a=g({},t,e({value:d(t.value+\"\")},i));return function(t){for(var e,i=document.createNodeIterator(t,NodeFilter.SHOW_TEXT,null,!1);e=i.nextNode();)e.textContent.trim()||e.parentNode.removeChild(e)}(s=this.parseTemplate(\"tag\",[a,this])),T(s,t),s},reCheckInvalidTags(){var t=this.settings;this.getTagElms(t.classNames.tagNotAllowed).forEach(((e,i)=>{var s=T(e),a=this.hasMaxTags(),n=this.validateTag(s),o=!0===n&&!a;if(\"select\"==t.mode&&this.toggleScopeValidation(n),o)return s=s.__preInvalidData?s.__preInvalidData:{value:s.value},this.replaceTag(e,s);e.title=a||n}))},removeTags(t,e,i){var s,a=this.settings;if(t=t&&t instanceof HTMLElement?[t]:t instanceof Array?t:t?[t]:[this.getLastTag()],s=t.reduce(((t,e)=>{e&&\"string\"==typeof e&&(e=this.getTagElmByValue(e));var i=T(e);return e&&i&&!i.readonly&&t.push({node:e,idx:this.getTagIdx(i),data:T(e,{__removed:!0})}),t}),[]),i=\"number\"==typeof i?i:this.CSSVars.tagHideTransition,\"select\"==a.mode&&(i=0,this.input.set.call(this)),1==s.length&&\"select\"!=a.mode&&s[0].node.classList.contains(a.classNames.tagNotAllowed)&&(e=!0),s.length)return a.hooks.beforeRemoveTag(s,{tagify:this}).then((()=>{function t(t){t.node.parentNode&&(t.node.parentNode.removeChild(t.node),e?a.keepInvalidTags&&this.trigger(\"remove\",{tag:t.node,index:t.idx}):(this.trigger(\"remove\",{tag:t.node,index:t.idx,data:t.data}),this.dropdown.refilter(),this.dropdown.position(),this.DOM.input.normalize(),a.keepInvalidTags&&this.reCheckInvalidTags()))}i&&i>10&&1==s.length?function(e){e.node.style.width=parseFloat(window.getComputedStyle(e.node).width)+\"px\",document.body.clientTop,e.node.classList.add(a.classNames.tagHide),setTimeout(t.bind(this),i,e)}.call(this,s[0]):s.forEach(t.bind(this)),e||(this.removeTagsFromValue(s.map((t=>t.node))),this.update(),\"select\"==a.mode&&this.setContentEditable(!0))})).catch((t=>{}))},removeTagsFromDOM(){[].slice.call(this.getTagElms()).forEach((t=>t.parentNode.removeChild(t)))},removeTagsFromValue(t){(t=Array.isArray(t)?t:[t]).forEach((t=>{var e=T(t),i=this.getTagIdx(e);i>-1&&this.value.splice(i,1)}))},removeAllTags(t){t=t||{},this.value=[],\"mix\"==this.settings.mode?this.DOM.input.innerHTML=\"\":this.removeTagsFromDOM(),this.dropdown.refilter(),this.dropdown.position(),this.state.dropdown.visible&&setTimeout((()=>{this.DOM.input.focus()})),\"select\"==this.settings.mode&&(this.input.set.call(this),this.setContentEditable(!0)),this.update(t)},postUpdate(){this.state.blockChangeEvent=!1;var t=this.settings,e=t.classNames,i=\"mix\"==t.mode?t.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value.trim():this.value.length+this.input.raw.call(this).length;this.toggleClass(e.hasMaxTags,this.value.length>=t.maxTags),this.toggleClass(e.hasNoTags,!this.value.length),this.toggleClass(e.empty,!i),\"select\"==t.mode&&this.toggleScopeValidation(this.value?.[0]?.__isValid)},setOriginalInputValue(t){var e=this.DOM.originalInput;this.settings.mixMode.integrated||(e.value=t,e.tagifyValue=e.value,this.setPersistedData(t,\"value\"))},update(t){clearTimeout(this.debouncedUpdateTimeout),this.debouncedUpdateTimeout=setTimeout(function(){var e=this.getInputValue();this.setOriginalInputValue(e),this.settings.onChangeAfterBlur&&(t||{}).withoutChangeEvent||this.state.blockChangeEvent||this.triggerChangeEvent();this.postUpdate()}.bind(this),100)},getInputValue(){var t=this.getCleanValue();return\"mix\"==this.settings.mode?this.getMixedTagsAsString(t):t.length?this.settings.originalInputValueFormat?this.settings.originalInputValueFormat(t):JSON.stringify(t):\"\"},getCleanValue(t){return a(t||this.value,this.dataProps)},getMixedTagsAsString(){var t=\"\",e=this,i=this.settings,s=i.originalInputValueFormat||JSON.stringify,a=i.mixTagsInterpolator;return function i(o){o.childNodes.forEach((o=>{if(1==o.nodeType){const r=T(o);if(\"BR\"==o.tagName&&(t+=\"\\r\\n\"),r&&v.call(e,o)){if(r.__removed)return;t+=a[0]+s(n(r,e.dataProps))+a[1]}else o.getAttribute(\"style\")||[\"B\",\"I\",\"U\"].includes(o.tagName)?t+=o.textContent:\"DIV\"!=o.tagName&&\"P\"!=o.tagName||(t+=\"\\r\\n\",i(o))}else t+=o.textContent}))}(this.DOM.input),t}},S.prototype.removeTag=S.prototype.removeTags,S}));\n\n\n//# sourceURL=webpack://Sneat/./node_modules/@yaireo/tagify/dist/tagify.min.js?"); + +/***/ }), + +/***/ "./libs/tagify/tagify.js": +/*!*******************************!*\ + !*** ./libs/tagify/tagify.js ***! + \*******************************/ +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Tagify: function() { return /* reexport default from dynamic */ _yaireo_tagify__WEBPACK_IMPORTED_MODULE_0___default.a; }\n/* harmony export */ });\n/* harmony import */ var _yaireo_tagify__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @yaireo/tagify */ \"./node_modules/@yaireo/tagify/dist/tagify.min.js\");\n/* harmony import */ var _yaireo_tagify__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yaireo_tagify__WEBPACK_IMPORTED_MODULE_0__);\n\ntry {\n window.Tagify = (_yaireo_tagify__WEBPACK_IMPORTED_MODULE_0___default());\n} catch (e) {}\n\n\n//# sourceURL=webpack://Sneat/./libs/tagify/tagify.js?"); + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ !function() { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function() { return module['default']; } : +/******/ function() { return module; }; +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ !function() { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = function(exports, definition) { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ !function() { +/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } +/******/ }(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ !function() { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ }(); +/******/ +/************************************************************************/ +/******/ +/******/ // startup +/******/ // Load entry module and return exports +/******/ // This entry module can't be inlined because the eval devtool is used. +/******/ var __webpack_exports__ = __webpack_require__("./libs/tagify/tagify.js"); +/******/ +/******/ return __webpack_exports__; +/******/ })() +; +}); \ No newline at end of file diff --git a/src/components/RecurringExpense/ManageRecurringExpense.jsx b/src/components/RecurringExpense/ManageRecurringExpense.jsx new file mode 100644 index 00000000..e4b951c7 --- /dev/null +++ b/src/components/RecurringExpense/ManageRecurringExpense.jsx @@ -0,0 +1,537 @@ +import React, { useEffect, useState } from "react"; +import Label from "../common/Label"; +import { Controller, useForm } from "react-hook-form"; +import { + useCurrencies, + useExpenseCategory, + useRecurringStatus, +} from "../../hooks/masterHook/useMaster"; +import DatePicker from "../common/DatePicker"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { + defaultRecurringExpense, + PaymentRecurringExpense, +} from "./RecurringExpenseSchema"; +import { + FREQUENCY_FOR_RECURRING, + INR_CURRENCY_CODE, +} from "../../utils/constants"; +import { useProjectName } from "../../hooks/useProjects"; +import { + useCreateRecurringExpense, + usePayee, + useRecurringExpenseDetail, + useUpdateRecurringExpense, +} from "../../hooks/useExpense"; +import InputSuggestions from "../common/InputSuggestion"; +import { useEmployeesName } from "../../hooks/useEmployees"; +import PmsEmployeeInputTag from "../common/PmsEmployeeInputTag"; + +const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => { + const { + data, + isLoading, + isError, + error: requestError, + } = useRecurringExpenseDetail(requestToEdit); + const { data: employees } = useEmployeesName(null, null, true); + //APIs + const { + projectNames, + loading: projectLoading, + error, + isError: isProjectError, + } = useProjectName(); + const { + data: currencyData, + isLoading: currencyLoading, + isError: currencyError, + } = useCurrencies(); + const { + data: statusData, + isLoading: statusLoading, + isError: statusError, + } = useRecurringStatus(); + const { + data: Payees, + isLoading: isPayeeLoaing, + isError: isPayeeError, + error: payeeError, + } = usePayee(); + const { + expenseCategories, + loading: ExpenseLoading, + error: ExpenseError, + } = useExpenseCategory(); + + const schema = PaymentRecurringExpense(); + const { + register, + control, + watch, + handleSubmit, + setValue, + reset, + formState: { errors }, + } = useForm({ + resolver: zodResolver(schema), + defaultValues: defaultRecurringExpense, + }); + const handleClose = () => { + reset(); + closeModal(); + }; + + const { mutate: CreateRecurringExpense, isPending: createPending } = + useCreateRecurringExpense(() => { + handleClose(); + }); + const { mutate: RecurringExpenseUpdate, isPending } = + useUpdateRecurringExpense(() => handleClose()); + const handleEmailGetting = (userArray = []) => { + if (!Array.isArray(userArray) || userArray.length === 0) return []; + + return userArray + .map((empId) => { + const foundUser = employees?.data?.find((user) => user.id === empId); + return foundUser?.email || null; + }) + .filter(Boolean) + .join(","); + }; + useEffect(() => { + if (requestToEdit && data) { + reset({ + title: data.title || "", + description: data.description || "", + payee: data.payee || "", + notifyTo: data.notifyTo ? data.notifyTo.map((usr) => usr.id) : [], + currencyId: data.currency.id || "", + amount: data.amount || "", + strikeDate: data.strikeDate?.slice(0, 10) || "", + projectId: data.project.id || "", + paymentBufferDays: data.paymentBufferDays || "", + numberOfIteration: data.numberOfIteration || "", + expenseCategoryId: data.expenseCategory.id || "", + statusId: data.status.id || "", + frequency: data.frequency || "", + isVariable: data.isVariable || false, + }); + } + }, [data, reset]); + + useEffect(() => { + if (!requestToEdit && currencyData && currencyData.length > 0) { + const inrCurrency = currencyData.find((c) => c.id === INR_CURRENCY_CODE); + if (inrCurrency) { + setValue("currencyId", INR_CURRENCY_CODE, { shouldValidate: true }); + } + } + }, [currencyData, requestToEdit, setValue]); + + const onSubmit = (fromdata) => { + console.log(fromdata); + let payload = { + ...fromdata, + strikeDate: fromdata.strikeDate + ? new Date(fromdata.strikeDate).toISOString() + : null, + notifyTo: handleEmailGetting(fromdata.notifyTo), + }; + if (requestToEdit) { + const editPayload = { ...payload, id: data.id }; + RecurringExpenseUpdate({ id: data.id, payload: editPayload }); + } else { + CreateRecurringExpense(payload); + } + }; + return ( +
+
+ {requestToEdit + ? "Update Expense Recurring " + : "Create Expense Recurring"} +
+
+ {/* Project and Category */} +
+
+ + + {errors.projectId && ( + {errors.projectId.message} + )} +
+ +
+ + + {errors.expenseCategoryId && ( + + {errors.expenseCategoryId.message} + + )} +
+
+ + {/* Title and Is Variable */} +
+
+ + + {errors.title && ( + {errors.title.message} + )} +
+ + {/*
+ + + {errors.isVariable && ( + {errors.isVariable.message} + )} +
*/} + +
+ + + ( +
+
+ field.onChange(true)} + /> + +
+ +
+ field.onChange(false)} + /> + +
+
+ )} + /> + {errors.isVariable && ( + {errors.isVariable.message} + )} +
+
+ + {/* Date and Amount */} +
+
+ + + {errors.strikeDate && ( + {errors.strikeDate.message} + )} +
+ +
+ + + {errors.amount && ( + {errors.amount.message} + )} +
+
+ + {/* Payee and Currency */} +
+
+ + + setValue("payee", val, { shouldValidate: true }) + } + error={errors.payee?.message} + placeholder="Select or enter payee" + /> +
+ +
+ + + {errors.currencyId && ( + {errors.currencyId.message} + )} +
+
+ + {/* Frequency To and Status Id */} +
+
+ + + {errors.frequency && ( + {errors.frequency.message} + )} +
+
+ + + {errors.statusId && ( + {errors.statusId.message} + )} +
+
+ + {/* Payment Buffer Days and Number of Iteration */} +
+
+ + + {errors.paymentBufferDays && ( + + {errors.paymentBufferDays.message} + + )} +
+
+ + + {errors.numberOfIteration && ( + + {errors.numberOfIteration.message} + + )} +
+
+ +
+
+ + + {/* */} + + {errors.notifyTo && ( + {errors.notifyTo.message} + )} +
+
+ + {/* Description */} +
+
+ + + {errors.description && ( + + {errors.description.message} + + )} +
+
+ +
+ + +
+
+
+ ); +}; + +export default ManageRecurringExpense; diff --git a/src/components/RecurringExpense/RecurringExpenseFilterPanel.jsx b/src/components/RecurringExpense/RecurringExpenseFilterPanel.jsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/RecurringExpense/RecurringExpenseList.jsx b/src/components/RecurringExpense/RecurringExpenseList.jsx new file mode 100644 index 00000000..2b966b6a --- /dev/null +++ b/src/components/RecurringExpense/RecurringExpenseList.jsx @@ -0,0 +1,290 @@ +import React, { useState } from "react"; +import { + EXPENSE_DRAFT, + EXPENSE_REJECTEDBY, + FREQUENCY_FOR_RECURRING, + ITEMS_PER_PAGE, + PAYEE_RECURRING_EXPENSE, +} from "../../utils/constants"; +import { formatCurrency, useDebounce } from "../../utils/appUtils"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import { ExpenseTableSkeleton } from "../Expenses/ExpenseSkeleton"; +import ConfirmModal from "../common/ConfirmModal"; +import { useNavigate } from "react-router-dom"; +import { useSelector } from "react-redux"; +import Error from "../common/Error"; +import { useRecurringExpenseContext } from "../../pages/RecurringExpense/RecurringExpensePage"; +import { useRecurringExpenseList } from "../../hooks/useExpense"; +import Pagination from "../common/Pagination"; + +const RecurringExpenseList = ({ search, filterStatuses }) => { + const { setManageRequest, setVieRequest, setViewRecurring } = useRecurringExpenseContext(); + const navigate = useNavigate(); + const [IsDeleteModalOpen, setIsDeleteModalOpen,] = useState(false); + const [deletingId, setDeletingId] = useState(null); + + const SelfId = useSelector( + (store) => store?.globalVariables?.loginUser?.employeeInfo?.id + ); + + const statusColorMap = { + "da462422-13b2-45cc-a175-910a225f6fc8": "primary", // Active + "306856fb-5655-42eb-bf8b-808bb5e84725": "success", // Completed + "3ec864d2-8bf5-42fb-ba70-5090301dd816": "danger", // De-Activated + "8bfc9346-e092-4a80-acbf-515ae1ef6868": "warning", // Paused + }; + + const recurringExpenseColumns = [ + { + key: "expenseCategory", + label: "Category", + align: "text-start", + getValue: (e) => e?.expenseCategory?.name || "N/A", + }, + { + key: "title", + label: "Title", + align: "text-start", + getValue: (e) => e?.title || "N/A", + }, + { + key: "payee", + label: "Payee", + align: "text-start", + getValue: (e) => e?.payee || "N/A", + }, + { + key: "frequency", + label: "Frequency", + align: "text-start", + getValue: (e) => + e?.frequency !== undefined && e?.frequency !== null + ? FREQUENCY_FOR_RECURRING[e.frequency] || "N/A" + : "N/A", + }, + { + key: "amount", + label: "Amount", + align: "text-end", + getValue: (e) => + e?.amount + ? `${e?.currency?.symbol ? e.currency.symbol + " " : ""}${e.amount.toLocaleString()}` + : "N/A", + }, + { + key: "createdAt", + label: "Next Generation Date", + align: "text-center", + getValue: (e) => + e?.createdAt ? formatUTCToLocalTime(e.createdAt) : "N/A", + }, + { + key: "status", + label: "Status", + align: "text-center", + getValue: (e) => { + const color = statusColorMap[e?.status?.id] || "secondary"; + const label = PAYEE_RECURRING_EXPENSE.find( + (s) => s.id === e?.status?.id + )?.label; + return ( + + {label || e?.status?.name || "N/A"} + + ); + }, + }, + ]; + + const [currentPage, setCurrentPage] = useState(1); + const debouncedSearch = useDebounce(search, 500); + + const { data, isLoading, isError, error, isRefetching, refetch } = + useRecurringExpenseList( + ITEMS_PER_PAGE, + currentPage, + {}, + true, + debouncedSearch + ); + + + const paginate = (page) => { + if (page >= 1 && page <= (data?.totalPages ?? 1)) { + setCurrentPage(page); + } + }; + + if (isError) { + return ; + } + + const header = [ + "Category", + "Title", + "Amount", + "Payee", + "Frequency", + "Next Generation", + "Status", + "Action", + ]; + + if (isLoading) return ; + + const canEditExpense = (recurringExpense) => { + // return ( + // (recurringExpense?.expenseStatus?.id === EXPENSE_DRAFT || + // EXPENSE_REJECTEDBY.includes(recurringExpense?.expenseStatus.id)) && + // recurringExpense?.createdBy?.id === SelfId + // ); + }; + + const canDeleteExpense = (request) => { + return ( + request?.expenseStatus?.id === EXPENSE_DRAFT && + request?.createdBy?.id === SelfId + ); + }; + + const filteredData = data?.data?.filter((item) => + filterStatuses.includes(item?.status?.id) + ); + + const handleDelete = (id) => { + setDeletingId(id); + DeleteExpense( + { id }, + { + onSettled: () => { + setDeletingId(null); + setIsDeleteModalOpen(false); + }, + } + ); + }; + + return ( + <> + {IsDeleteModalOpen && ( + setIsDeleteModalOpen(false)} + paramData={deletingId} + /> + )} + +
+
+ + + + {recurringExpenseColumns.map((col) => ( + + ))} + + + + + + {filteredData?.length > 0 ? ( + filteredData?.map((recurringExpense) => ( + + {recurringExpenseColumns.map((col) => ( + + ))} + + + )) + ) : ( + + + + )} + +
+ {col.label} + Action
+ {col?.customRender + ? col?.customRender(recurringExpense) + : col?.getValue(recurringExpense)} + +
+ + setViewRecurring({ + recurringId: recurringExpense?.id, + view: true, + }) + } + > + +
+ +
    +
  • + setManageRequest({ + IsOpen: true, + RecurringId: recurringExpense?.id, + }) + } + > + + + Modify + +
  • + +
  • { + setIsDeleteModalOpen(true); + setDeletingId(recurringExpense.id); + }} + > + + + Delete + +
  • +
+
+
+
+

No Recurring Expense Found

+
+
+ + {/* Pagination */} + +
+ + ); +}; + +export default RecurringExpenseList; diff --git a/src/components/RecurringExpense/RecurringExpenseSchema.js b/src/components/RecurringExpense/RecurringExpenseSchema.js new file mode 100644 index 00000000..2555a9ac --- /dev/null +++ b/src/components/RecurringExpense/RecurringExpenseSchema.js @@ -0,0 +1,112 @@ +import { boolean, z } from "zod"; +import { INR_CURRENCY_CODE } from "../../utils/constants"; + +export const PaymentRecurringExpense = () => { + return z.object({ + title: z.string().min(1, { message: "Title is required" }).transform((val) => val.trim()), + description: z.string().min(1, { message: "Description is required" }).transform((val) => val.trim()), + payee: z.string().min(1, { message: "Payee name is required" }).transform((val) => val.trim()), + notifyTo: z.array(z.string()).min(1,"Please select at lest one user"), + currencyId: z + .string() + .min(1, { message: "Currency is required" }) + .transform((val) => val.trim()), + + amount: z + .number({ + required_error: "Amount is required", + invalid_type_error: "Amount must be a number", + }) + .min(1, { message: "Amount must be greater than 0" }) + .refine((val) => /^\d+(\.\d{1,2})?$/.test(val.toString()), { + message: "Amount must have at most 2 decimal places", + }), + + strikeDate: z + .string() + .min(1, { message: "Date is required" }) + .refine((val) => !isNaN(Date.parse(val)), { + message: "Invalid date format", + }) + .transform((val) => val.trim()), + + projectId: z + .string() + .min(1, { message: "Project is required" }) + .transform((val) => val.trim()), + + paymentBufferDays: z + .number({ + required_error: "Buffer days is required", + invalid_type_error: "Buffer days must be a number", + }) + .min(0, { message: "Buffer days cannot be negative" }), + + numberOfIteration: z + .number({ + required_error: "Iteration is required", + invalid_type_error: "Iteration must be a number", + }) + .min(1, { message: "Iteration must be at least 1" }), + + expenseCategoryId: z + .string() + .min(1, { message: "Expense Category is required" }) + .transform((val) => val.trim()), + + statusId: z + .string() + .min(1, { message: "Please select a status" }) + .transform((val) => val.trim()), + + frequency: z + .number({ + required_error: "Frequency is required", + invalid_type_error: "Frequency must be a number", + }) + .refine((val) => [0, 1, 2, 3, 4, 5].includes(val), { + message: "Invalid frequency selected", + }), + + isVariable: z.boolean().optional(), + }); +}; + + +export const defaultRecurringExpense = { + title: "", + description: "", + payee: "", + notifyTo: [], + currencyId: "", + amount: 0, + strikeDate: "", + projectId: "", + paymentBufferDays: 0, + numberOfIteration: 1, + expenseCategoryId: "", + statusId: "", + frequency: 1, + isVariable: true, +}; + + +export const SearchRecurringExpenseSchema = z.object({ + title: z.array(z.string()).optional(), + description: z.array(z.string()).optional(), + payee: z.array(z.string()).optional(), + notifyTo: z.array(z.string()).optional(), + currencyId: z.array(z.string()).optional(), + amount: z.array(z.string()).optional(), + strikeDate: z.string().optional(), + projectId: z.string().optional(), + paymentBufferDays: z.string().optional(), + numberOfIteration: z.string().optional(), + expenseCategoryId: z.string().optional(), + statusId: z.string().optional(), + frequency: z.string().optional(), + isVariable: z.string().optional(), +}); + + + diff --git a/src/components/RecurringExpense/RecurringRexpenseList.jsx b/src/components/RecurringExpense/RecurringRexpenseList.jsx new file mode 100644 index 00000000..ef88ae00 --- /dev/null +++ b/src/components/RecurringExpense/RecurringRexpenseList.jsx @@ -0,0 +1,342 @@ +import React, { useState } from "react"; +import { + EXPENSE_DRAFT, + EXPENSE_REJECTEDBY, + ITEMS_PER_PAGE, +} from "../../utils/constants"; +import { + formatCurrency, + getColorNameFromHex, + useDebounce, +} from "../../utils/appUtils"; +import { usePaymentRequestList } from "../../hooks/useExpense"; +import { formatDate, formatUTCToLocalTime } from "../../utils/dateUtils"; +import Avatar from "../../components/common/Avatar"; +import { ExpenseTableSkeleton } from "../Expenses/ExpenseSkeleton"; +import ConfirmModal from "../common/ConfirmModal"; +import { useNavigate } from "react-router-dom"; +import { useSelector } from "react-redux"; +import Error from "../common/Error"; +import { useRecurringExpenseContext } from "../../pages/RecurringExpense/RecurringExpensePage"; + +const RecurringExpenseList = ({ filters, groupBy = "submittedBy", search }) => { + const { setManageRequest, setVieRequest } = useRecurringExpenseContext(); + const navigate = useNavigate(); + const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [deletingId, setDeletingId] = useState(null); + const SelfId = useSelector( + (store) => store?.globalVariables?.loginUser?.employeeInfo?.id + ); + const groupByField = (items, field) => { + return items.reduce((acc, item) => { + let key; + let displayField; + + switch (field) { + case "transactionDate": + key = item?.transactionDate?.split("T")[0]; + displayField = "Transaction Date"; + break; + case "status": + key = item?.status?.displayName || "Unknown"; + displayField = "Status"; + break; + case "submittedBy": + key = `${item?.createdBy?.firstName ?? ""} ${item.createdBy?.lastName ?? "" + }`.trim(); + displayField = "Submitted By"; + break; + case "project": + key = item?.project?.name || "Unknown Project"; + displayField = "Project"; + break; + case "paymentMode": + key = item?.paymentMode?.name || "Unknown Mode"; + displayField = "Payment Mode"; + break; + case "expensesType": + key = item?.expensesType?.name || "Unknown Type"; + displayField = "Expense Category"; + break; + case "createdAt": + key = item?.createdAt?.split("T")[0] || "Unknown Date"; + displayField = "Created Date"; + break; + default: + key = "Others"; + displayField = "Others"; + } + + const groupKey = `${field}_${key}`; // unique key for object property + if (!acc[groupKey]) { + acc[groupKey] = { key, displayField, items: [] }; + } + + acc[groupKey].items.push(item); + return acc; + }, {}); + }; + + const paymentRequestColumns = [ + { + key: "paymentRequestUID", + label: "Template Name", + align: "text-start mx-2", + getValue: (e) => e.paymentRequestUID || "N/A", + }, + { + key: "title", + label: "Frequency", + align: "text-start", + getValue: (e) => e.title || "N/A", + }, + { + key: "createdAt", + label: "Next Generation Date", + align: "text-start", + getValue: (e) => formatUTCToLocalTime(e?.createdAt), + }, + { + key: "createdAt", + label: "Status", + align: "text-start", + getValue: (e) => formatUTCToLocalTime(e?.createdAt), + }, + + ]; + + const [currentPage, setCurrentPage] = useState(1); + const debouncedSearch = useDebounce(search, 500); + + const { data, isLoading, isError, error, isRefetching, refetch } = + usePaymentRequestList( + ITEMS_PER_PAGE, + currentPage, + filters, + true, + debouncedSearch + ); + + const paymentRequestData = data?.data || []; + const totalPages = data?.data?.totalPages || 1; + + if (isError) { + return ; + } + const header = [ + "Request ID", + "Request Title", + "Submitted By", + "Submitted On", + "Amount", + "Status", + "Action", + ]; + if (isLoading) return ; + + const grouped = groupBy + ? groupByField(data?.data ?? [], groupBy) + : { All: data?.data ?? [] }; + const IsGroupedByDate = [ + { key: "transactionDate", displayField: "Transaction Date" }, + { key: "createdAt", displayField: "created Date" }, + ]?.includes(groupBy); + + const canEditExpense = (paymentRequest) => { + return ( + (paymentRequest?.expenseStatus?.id === EXPENSE_DRAFT || + EXPENSE_REJECTEDBY.includes(paymentRequest?.expenseStatus.id)) && + paymentRequest?.createdBy?.id === SelfId + ); + }; + const canDetetExpense = (request) => { + return ( + request?.expenseStatus?.id === EXPENSE_DRAFT && + request?.createdBy?.id === SelfId + ); + }; + + const handleDelete = (id) => { + setDeletingId(id); + DeleteExpense( + { id }, + { + onSettled: () => { + setDeletingId(null); + setIsDeleteModalOpen(false); + }, + } + ); + }; + + return ( + <> + {IsDeleteModalOpen && ( + setIsDeleteModalOpen(false)} + // loading={isPending} + paramData={deletingId} + /> + )} +
+
+ + + + {paymentRequestColumns.map((col) => ( + + ))} + + + + + + {Object.keys(grouped).length > 0 ? ( + Object.values(grouped).map(({ key, displayField, items }) => ( + + + + + {items?.map((paymentRequest) => ( + + {paymentRequestColumns.map( + (col) => + (col.isAlwaysVisible || groupBy !== col.key) && ( + + ) + )} + + + ))} + + )) + ) : ( + + + + )} + +
+ {col.label} + Action
+
+ {" "} + + {displayField} :{" "} + {" "} + + {IsGroupedByDate ? formatUTCToLocalTime(key) : key} + +
+
+ {col?.customRender + ? col?.customRender(paymentRequest) + : col?.getValue(paymentRequest)} + +
+ + setVieRequest({ + requestId: paymentRequest.id, + view: true, + }) + } + > + {canEditExpense(paymentRequest) && ( +
+ +
    +
  • + setManageRequest({ + IsOpen: true, + RequestId: paymentRequest.id, + }) + } + > + + + + Modify + + +
  • + + {canDetetExpense(paymentRequest) && ( +
  • { + setIsDeleteModalOpen(true); + setDeletingId(paymentRequest.id); + }} + > + + + + Delete + + +
  • + )} +
+
+ )} +
+
+
+

No Request Found

+
+
+
+ + {/* Pagination */} + {totalPages > 1 && ( +
+ +
+ )} +
+ + ); +}; + +export default RecurringExpenseList; diff --git a/src/components/RecurringExpense/ViewRecurringExpense.jsx b/src/components/RecurringExpense/ViewRecurringExpense.jsx new file mode 100644 index 00000000..0c7a5327 --- /dev/null +++ b/src/components/RecurringExpense/ViewRecurringExpense.jsx @@ -0,0 +1,307 @@ +import React from 'react' +import { useRecurringExpenseDetail } from '../../hooks/useExpense'; +import { formatUTCToLocalTime } from '../../utils/dateUtils'; +import { formatFigure, getColorNameFromHex } from '../../utils/appUtils'; +import Avatar from '../common/Avatar'; +import { FREQUENCY_FOR_RECURRING } from '../../utils/constants'; +import { ExpenseDetailsSkeleton } from '../Expenses/ExpenseSkeleton'; + +const ViewRecurringExpense = ({ RecurringId }) => { + const { data, isLoading, isError, error, isFetching } = useRecurringExpenseDetail(RecurringId); + + const statusColorMap = { + "da462422-13b2-45cc-a175-910a225f6fc8": "primary", // Active + "306856fb-5655-42eb-bf8b-808bb5e84725": "success", // Completed + "3ec864d2-8bf5-42fb-ba70-5090301dd816": "danger", // De-Activated + "8bfc9346-e092-4a80-acbf-515ae1ef6868": "warning", // Paused + }; + if (isLoading) return ; + + return ( + +
+
+
Recurring Payment Details
+
+
+ {/*
*/} +
+ + {/* Row 1 Recurring Id and Status */} + +
+ {data?.recurringPaymentUID} + + {data?.status?.name || "N/A"} + +
+ + {/* Row 2 Category*/} + +
+
+ +
{data?.expenseCategory?.name || "N/A"}
+
+
+ + + + {/* Row 3 Amount and Project */} + +
+
+ +
+ {data?.amount != null + ? `${data?.currency?.symbol ?? "¥"} ${Number(data.amount).toFixed(2)} ${data?.currency?.currencyCode ?? "CN"}` + : "N/A"} +
+
+
+ +
+
+ +
{data?.project?.name || "N/A"}
+
+
+ + {/* Row 4 Created At and Title*/} + +
+
+ + {/*
+ {formatUTCToLocalTime(data?.createdAt, true)} +
*/} + +
+ {data?.createdAt + ? formatUTCToLocalTime(data.createdAt, true) + : "N/A"} +
+
+
+ +
+
+ +
{data?.title || "N/A"}
+
+
+ + {/* Row 5 Payee and Notify*/} + +
+
+ +
{data?.payee || "N/A"}
+
+
+ +
+
+ + +
+ {data?.notifyTo?.length > 0 + ? data.notifyTo?.map((user, index) => ( + + {user.email} + {index < data?.notifyTo?.length - 1 && ", "} + + )) + : "N/A"} +
+
+
+ + {/* Row 6 Strike Date*/} + +
+
+ + {/*
+ {formatUTCToLocalTime(data?.strikeDate)} +
*/} +
+ {data?.strikeDate + ? formatUTCToLocalTime(data.strikeDate, true) + : "N/A"} +
+
+
+ + {/* Row 7 Frequency and Buffer Days*/} + +
+
+ +
+ {data?.frequency !== undefined + ? FREQUENCY_FOR_RECURRING[data.frequency] + : "N/A"} +
+
+
+ +
+
+ +
{data?.paymentBufferDays || "N/A"}
+
+
+ + {/* Row 8 Updated At and Number of Iteration*/} + +
+
+ +
+ {data?.updatedAt + ? formatUTCToLocalTime(data.updatedAt, true) + : "N/A"} +
+
+
+ +
+
+ +
{data?.numberOfIteration || "N/A"}
+
+
+ + + {/* Row 9 Created By and Updated By*/} + +
+
+ + + + {`${data?.createdBy?.firstName ?? ""} ${data?.createdBy?.lastName ?? ""}`.trim() || "N/A"} + +
+
+
+
+ + + {data?.updatedBy ? ( + <> + + + {`${data.updatedBy.firstName ?? ""} ${data.updatedBy.lastName ?? ""}`.trim() || "N/A"} + + + ) : ( + N/A + )} +
+
+ + {/* Row 10 Description */} + +
+ +
+ {data?.description || "N/A"} +
+
+
+ {/*
*/} +
+
+ ) +} + +export default ViewRecurringExpense \ No newline at end of file diff --git a/src/components/common/PmsEmployeeInputTag.jsx b/src/components/common/PmsEmployeeInputTag.jsx new file mode 100644 index 00000000..8cad7aac --- /dev/null +++ b/src/components/common/PmsEmployeeInputTag.jsx @@ -0,0 +1,295 @@ +import { useState, useEffect, useRef, useMemo } from "react"; +import { useController } from "react-hook-form"; +import { useDebounce } from "../../utils/appUtils"; +import { useEmployeesName } from "../../hooks/useEmployees"; +import Avatar from "./Avatar"; + +const PmsEmployeeInputTag = ({ + control, + name, + placeholder, + projectId, + forAll, + isApplicationUser = false, +}) => { + const { + field: { value = [], onChange }, + } = useController({ name, control }); + + const [search, setSearch] = useState(""); + const [showDropdown, setShowDropdown] = useState(false); + const [filteredUsers, setFilteredUsers] = useState([]); + const [userCache, setUserCache] = useState({}); + const dropdownRef = useRef(null); + const inputRef = useRef(null); + const activeIndexRef = useRef(-1); + + const debouncedSearch = useDebounce(search, 300); + const { data: employees, isLoading } = useEmployeesName( + projectId, + debouncedSearch, + forAll + ); + + // Keep both filtered list and cache updated + useEffect(() => { + if (employees?.data?.length) { + setFilteredUsers(employees.data); + activeIndexRef.current = -1; + + // cache all fetched users by id + setUserCache((prev) => { + const updated = { ...prev }; + employees.data.forEach((u) => { + updated[u.id] = u; + }); + return updated; + }); + } else { + setFilteredUsers([]); + } + }, [employees]); + + // close dropdown when clicking outside + useEffect(() => { + const onDocClick = (e) => { + if ( + dropdownRef.current && + !dropdownRef.current.contains(e.target) && + !inputRef.current.contains(e.target) + ) { + setShowDropdown(false); + } + }; + document.addEventListener("mousedown", onDocClick); + return () => document.removeEventListener("mousedown", onDocClick); + }, []); + + // select a user + const handleSelect = (user) => { + if (value.includes(user.id)) return; + const updated = [...value, user.id]; + onChange(updated); + setSearch(""); + setShowDropdown(false); + setTimeout(() => inputRef.current?.focus(), 0); + }; + + // remove selected user + const handleRemove = (id) => { + const updated = value.filter((uid) => uid !== id); + onChange(updated); + }; + + // keyboard navigation + const onInputKeyDown = (e) => { + if (!showDropdown) return; + const max = Math.max(0, filteredUsers.length - 1); + if (e.key === "ArrowDown") { + e.preventDefault(); + activeIndexRef.current = Math.min(max, activeIndexRef.current + 1); + scrollToActive(); + } else if (e.key === "ArrowUp") { + e.preventDefault(); + activeIndexRef.current = Math.max(0, activeIndexRef.current - 1); + scrollToActive(); + } else if (e.key === "Enter") { + e.preventDefault(); + const idx = activeIndexRef.current; + if (idx >= 0 && filteredUsers[idx]) handleSelect(filteredUsers[idx]); + } else if (e.key === "Escape") { + setShowDropdown(false); + } + }; + + // scroll active dropdown item into view + const scrollToActive = () => { + const wrapper = dropdownRef.current?.querySelector( + ".tagify__dropdown__wrapper" + ); + const items = wrapper?.querySelectorAll(".tagify__dropdown__item"); + const idx = activeIndexRef.current; + if (items && items[idx]) { + const item = items[idx]; + const itemTop = item.offsetTop; + const itemBottom = itemTop + item.offsetHeight; + if (wrapper.scrollTop > itemTop) wrapper.scrollTop = itemTop; + else if (wrapper.scrollTop + wrapper.clientHeight < itemBottom) + wrapper.scrollTop = itemBottom - wrapper.clientHeight; + } + }; + + // resolve user details by ID (for rendering tags) + const resolveUserById = (id) => { + return userCache[id] || filteredUsers.find((u) => u.id === id); + }; + + // main visible users list (memoized) + const visibleUsers = useMemo(() => { + const baseList = isApplicationUser + ? (filteredUsers || []).filter((u) => u?.email) + : filteredUsers || []; + + // also include selected users even if missing from current API + const selectedUsers = + Array.isArray(value) && value.length + ? value.map((uid) => userCache[uid]).filter(Boolean) + : []; + + // merge unique + const merged = [ + ...selectedUsers, + ...baseList.filter((u) => !selectedUsers.some((s) => s.id === u.id)), + ]; + + return merged; + }, [filteredUsers, isApplicationUser, value, userCache]); + + return ( +
+ {/* Selected tags (chips) */} + {value.map((id) => { + const u = resolveUserById(id); + if (!u) return null; + return ( + +
+ {u.photo ? ( + + {`${u.firstName + + ) : ( +
+ + {u.firstName?.[0] || ""} + {u.lastName?.[0] || ""} + +
+ )} + +
+ + {u.firstName} {u.lastName} + +
+
+ +
+ ); +}; + +export default PmsEmployeeInputTag; diff --git a/src/hooks/masterHook/useMaster.js b/src/hooks/masterHook/useMaster.js index 2903fde9..5e786adb 100644 --- a/src/hooks/masterHook/useMaster.js +++ b/src/hooks/masterHook/useMaster.js @@ -10,6 +10,16 @@ import { } from "@tanstack/react-query"; import showToast from "../../services/toastService"; + +export const useRecurringStatus = ()=>{ + return useQuery({ + queryKey:["RecurringStatus"], + queryFn:async()=>{ + const resp = await MasterRespository.getRecurringStatus(); + return resp.data + } + }) +} export const useCurrencies = () => { return useQuery({ queryKey: ["currencies"], diff --git a/src/pages/RecurringExpense/RecurringExpensePage.jsx b/src/pages/RecurringExpense/RecurringExpensePage.jsx new file mode 100644 index 00000000..cffccaec --- /dev/null +++ b/src/pages/RecurringExpense/RecurringExpensePage.jsx @@ -0,0 +1,165 @@ +import React, { createContext, useState, useEffect, useContext } from "react"; +import Breadcrumb from "../../components/common/Breadcrumb"; +import GlobalModel from "../../components/common/GlobalModel"; +import { useFab } from "../../Context/FabContext"; +import ManageRecurringExpense from "../../components/RecurringExpense/ManageRecurringExpense"; +import RecurringExpenseList from "../../components/RecurringExpense/RecurringExpenseList"; +import { PAYEE_RECURRING_EXPENSE } from "../../utils/constants"; +import { SearchRecurringExpenseSchema } from "../../components/RecurringExpense/RecurringExpenseSchema"; +import ViewRecurringExpense from "../../components/RecurringExpense/ViewRecurringExpense"; + +export const RecurringExpenseContext = createContext(); +export const useRecurringExpenseContext = () => { + const context = useContext(RecurringExpenseContext); + if (!context) { + throw new Error("useRecurringExpenseContext must be used within an ExpenseProvider"); + } + return context; +}; +const RecurringExpensePage = () => { + const [ManageRequest, setManageRequest] = useState({ + IsOpen: null, + RecurringId: null, + }); + const [viewRecurring, setViewRecurring] = useState({ view: false, recurringId: null }) + + const [selectedStatuses, setSelectedStatuses] = useState( + PAYEE_RECURRING_EXPENSE.map((s) => s.id) + ); + + const [search, setSearch] = useState(""); + + const contextValue = { + setManageRequest, + setViewRecurring + }; + + const handleStatusChange = (id) => { + setSelectedStatuses((prev) => + prev.includes(id) + ? prev.filter((s) => s !== id) + : [...prev, id] + ); + }; + return ( + +
+ {/* Breadcrumb */} + + + {/* Top Bar */} +
+
+
+ {/* Left Column: Search + Filter */} +
+
+ setSearch(e.target.value)} + /> + +
+ +
    + {PAYEE_RECURRING_EXPENSE.map(({ id, label }) => ( +
  • +
    + handleStatusChange(id)} + /> + +
    +
  • + ))} +
+
+
+
+ + {/* Right Column: Add Button */} +
+ +
+
+ +
+
+ + + + {ManageRequest.IsOpen && ( + + setManageRequest({ IsOpen: null, expenseId: null }) + } + > + + setManageRequest({ IsOpen: null, RecurringId: null }) + } + requestToEdit={ManageRequest.RecurringId} + /> + + )} + {viewRecurring.view && ( + + setViewRecurring({ IsOpen: null, recurringId: null }) + } + > + {/* + setViewRecurring({ IsOpen: null, recurringId: null }) + } + RecurringId={viewRecurring.recurringId} + /> */} + + + )} + + +
+
+ ); +}; + +export default RecurringExpensePage; diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index 837ecdcd..9542300d 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -148,4 +148,6 @@ export const MasterRespository = { api.put(`/api/Master/payment-adjustment-head/edit/${id}`, data), getCurrencies: () => api.get(`/api/Master/currencies/list`), + + getRecurringStatus:()=>api.get(`/api/Master/recurring-status/list`) }; diff --git a/src/router/AppRoutes.jsx b/src/router/AppRoutes.jsx index 4db795ba..5a15d60c 100644 --- a/src/router/AppRoutes.jsx +++ b/src/router/AppRoutes.jsx @@ -57,6 +57,7 @@ import CollectionPage from "../pages/collections/CollectionPage"; import SubscriptionSummary from "../pages/Home/SubscriptionSummary"; import MakeSubscription from "../pages/Home/MakeSubscription"; import PaymentRequestPage from "../pages/PaymentRequest/PaymentRequestPage"; +import RecurringExpensePage from "../pages/RecurringExpense/RecurringExpensePage"; const router = createBrowserRouter( [ { @@ -108,6 +109,7 @@ const router = createBrowserRouter( { path: "/expenses/:status?/:project?", element: }, { path: "/expenses", element: }, { path: "/payment-request", element: }, + { path: "/recurring-payment", element: }, { path: "/collection", element: }, { path: "/masters", element: }, { path: "/tenants", element: },