diff --git a/packages/admin/composer.json b/packages/admin/composer.json index 3eddd0f7b..ed7d1e6b7 100755 --- a/packages/admin/composer.json +++ b/packages/admin/composer.json @@ -21,6 +21,8 @@ "bacon/bacon-qr-code": "^2.0", "danharrin/livewire-rate-limiting": "^0.3|^1.0", "filament/forms": "^3.2", + "filament/spatie-laravel-media-library-plugin": "^3.2", + "filament/tables": "^3.2", "gehrisandro/tailwind-merge-laravel": "^1.2", "illuminate/console": "^10.0|^11.0", "illuminate/contracts": "^10.0|^11.0", diff --git a/packages/admin/config/components/brand.php b/packages/admin/config/components/brand.php index e8c374509..5ee96276a 100644 --- a/packages/admin/config/components/brand.php +++ b/packages/admin/config/components/brand.php @@ -2,8 +2,7 @@ declare(strict_types=1); -use Shopper\Livewire\Components; -use Shopper\Livewire\Pages; +use Shopper\Livewire; return [ @@ -13,7 +12,9 @@ |-------------------------------------------------------------------------- */ - 'pages' => [], + 'pages' => [ + 'index' => Livewire\Pages\Brand\Index::class, + ], /* |-------------------------------------------------------------------------- @@ -22,9 +23,7 @@ */ 'components' => [ - 'brands.browse' => Components\Brands\Browse::class, - 'brands.create' => Components\Brands\Create::class, - 'brands.edit' => Components\Brands\Edit::class, + 'slide-overs.brand-form' => Livewire\SlideOvers\BrandForm::class, ], ]; diff --git a/packages/admin/config/components/dashboard.php b/packages/admin/config/components/dashboard.php index ce077bcbb..e59c2cbc4 100644 --- a/packages/admin/config/components/dashboard.php +++ b/packages/admin/config/components/dashboard.php @@ -25,7 +25,7 @@ 'components' => [ // 'search' => Components\Search::class, - 'side-panel' => Components\SidePanel::class, + 'side-panel' => Components\SlideOverPanel::class, ], ]; diff --git a/packages/admin/public/shopper.css b/packages/admin/public/shopper.css index d17273c3d..9b92fbd06 100755 --- a/packages/admin/public/shopper.css +++ b/packages/admin/public/shopper.css @@ -3598,6 +3598,14 @@ html { right: 3rem; } +.right-2 { + right: 0.5rem; +} + +.right-2\.5 { + right: 0.625rem; +} + .right-6 { right: 1.5rem; } @@ -3694,6 +3702,10 @@ html { grid-column: span 6 / span 6; } +.col-span-full { + grid-column: 1 / -1; +} + .col-start-2 { grid-column-start: 2; } @@ -3786,6 +3798,16 @@ html { margin-bottom: -0.5rem; } +.mx-1 { + margin-left: 0.25rem; + margin-right: 0.25rem; +} + +.mx-3 { + margin-left: 0.75rem; + margin-right: 0.75rem; +} + .mx-auto { margin-left: auto; margin-right: auto; @@ -3801,6 +3823,11 @@ html { margin-bottom: 0.5rem; } +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} + .my-8 { margin-top: 2rem; margin-bottom: 2rem; @@ -3810,6 +3837,10 @@ html { margin-top: 0px !important; } +.-mb-4 { + margin-bottom: -1rem; +} + .-mb-6 { margin-bottom: -1.5rem; } @@ -3874,6 +3905,10 @@ html { margin-top: -1.5rem; } +.-mt-7 { + margin-top: -1.75rem; +} + .-mt-px { margin-top: -1px; } @@ -4325,6 +4360,10 @@ html { width: 69.25rem; } +.w-\[calc\(100\%\+2rem\)\] { + width: calc(100% + 2rem); +} + .w-auto { width: auto; } @@ -4938,6 +4977,10 @@ html { row-gap: 0.25rem; } +.gap-y-1\.5 { + row-gap: 0.375rem; +} + .gap-y-2 { row-gap: 0.5rem; } @@ -4958,6 +5001,10 @@ html { row-gap: 1.5rem; } +.gap-y-px { + row-gap: 1px; +} + .-space-x-1 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(-0.25rem * var(--tw-space-x-reverse)); @@ -5203,6 +5250,10 @@ html { white-space: nowrap; } +.whitespace-normal { + white-space: normal; +} + .whitespace-nowrap { white-space: nowrap; } @@ -5336,6 +5387,15 @@ html { border-right-width: 0.5px; } +.border-y { + border-top-width: 1px; + border-bottom-width: 1px; +} + +.\!border-t-0 { + border-top-width: 0px !important; +} + .border-b { border-bottom-width: 1px; } @@ -5506,17 +5566,17 @@ html { .bg-custom-100 { --tw-bg-opacity: 1; - background-color: rgb(219 234 254 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-100), var(--tw-bg-opacity)); } .bg-custom-50 { --tw-bg-opacity: 1; - background-color: rgb(239 246 255 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-50), var(--tw-bg-opacity)); } .bg-custom-600 { --tw-bg-opacity: 1; - background-color: rgb(37 99 235 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-600), var(--tw-bg-opacity)); } .bg-danger-100 { @@ -5543,6 +5603,10 @@ html { background-color: rgba(var(--gray-100), 0.5); } +.bg-gray-100\/75 { + background-color: rgba(var(--gray-100), 0.75); +} + .bg-gray-200 { --tw-bg-opacity: 1; background-color: rgba(var(--gray-200), var(--tw-bg-opacity)); @@ -5692,10 +5756,6 @@ html { background-color: rgb(255 255 255 / 0.05); } -.bg-white\/60 { - background-color: rgb(255 255 255 / 0.6); -} - .bg-white\/75 { background-color: rgb(255 255 255 / 0.75); } @@ -5978,6 +6038,11 @@ html { padding-bottom: 0.75rem; } +.py-3\.5 { + padding-top: 0.875rem; + padding-bottom: 0.875rem; +} + .py-4 { padding-top: 1rem; padding-bottom: 1rem; @@ -6054,10 +6119,6 @@ html { padding-left: 2.5rem; } -.pl-16 { - padding-left: 4rem; -} - .pl-18 { padding-left: 4.5rem; } @@ -6134,10 +6195,18 @@ html { padding-inline-start: 0.75rem; } +.ps-4 { + padding-inline-start: 1rem; +} + .ps-\[5\.25rem\] { padding-inline-start: 5.25rem; } +.pt-0 { + padding-top: 0px; +} + .pt-1 { padding-top: 0.25rem; } @@ -6174,10 +6243,6 @@ html { padding-top: 1.5rem; } -.pt-8 { - padding-top: 2rem; -} - .text-left { text-align: left; } @@ -6202,6 +6267,10 @@ html { text-align: end; } +.align-top { + vertical-align: top; +} + .align-middle { vertical-align: middle; } @@ -6356,21 +6425,21 @@ html { .text-custom-400 { --tw-text-opacity: 1; - color: rgb(96 165 250 / var(--tw-text-opacity)); + color: rgba(var(--c-400), var(--tw-text-opacity)); } .text-custom-500 { --tw-text-opacity: 1; - color: rgb(59 130 246 / var(--tw-text-opacity)); + color: rgba(var(--c-500), var(--tw-text-opacity)); } .text-custom-600 { --tw-text-opacity: 1; - color: rgb(37 99 235 / var(--tw-text-opacity)); + color: rgba(var(--c-600), var(--tw-text-opacity)); } .text-custom-700\/50 { - color: rgb(29 78 216 / 0.5); + color: rgba(var(--c-700), 0.5); } .text-danger-400 { @@ -6700,15 +6769,15 @@ html { .ring-custom-600 { --tw-ring-opacity: 1; - --tw-ring-color: rgb(37 99 235 / var(--tw-ring-opacity)); + --tw-ring-color: rgba(var(--c-600), var(--tw-ring-opacity)); } .ring-custom-600\/10 { - --tw-ring-color: rgb(37 99 235 / 0.1); + --tw-ring-color: rgba(var(--c-600), 0.1); } .ring-custom-600\/20 { - --tw-ring-color: rgb(37 99 235 / 0.2); + --tw-ring-color: rgba(var(--c-600), 0.2); } .ring-danger-500\/10 { @@ -6772,6 +6841,11 @@ html { --tw-ring-opacity: 0.05; } +.blur { + --tw-blur: blur(8px); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + .blur-3xl { --tw-blur: blur(64px); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); @@ -6994,6 +7068,39 @@ input[type='number'] { display: block; } +.fi-modal-close-overlay { + --tw-backdrop-blur: blur(4px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} + +@keyframes shaking { + 0% { + transform: translateX(0) + } + + 25% { + transform: translateX(5px) + } + + 50% { + transform: translateX(-5px) + } + + 75% { + transform: translateX(5px) + } + + 100% { + transform: translateX(0) + } +} + +.horizontal-shake { + animation: shaking 0.35s normal; + animation-iteration-count: 1 +} + .iti { position: relative; display: block; @@ -8557,6 +8664,12 @@ input[type='number'] { position: absolute; } +.before\:inset-y-0::before { + content: var(--tw-content); + top: 0px; + bottom: 0px; +} + .before\:start-0::before { content: var(--tw-content); inset-inline-start: 0px; @@ -8587,10 +8700,22 @@ input[type='number'] { border-inline-start-width: 0px; } +.first\:border-t-0:first-child { + border-top-width: 0px; +} + .last\:border-e-0:last-child { border-inline-end-width: 0px; } +.first-of-type\:ps-1:first-of-type { + padding-inline-start: 0.25rem; +} + +.last-of-type\:pe-1:last-of-type { + padding-inline-end: 0.25rem; +} + .checked\:ring-0:checked { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); @@ -8621,17 +8746,17 @@ input[type='number'] { } .hover\:bg-custom-400\/10:hover { - background-color: rgb(96 165 250 / 0.1); + background-color: rgba(var(--c-400), 0.1); } .hover\:bg-custom-50:hover { --tw-bg-opacity: 1; - background-color: rgb(239 246 255 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-50), var(--tw-bg-opacity)); } .hover\:bg-custom-500:hover { --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-500), var(--tw-bg-opacity)); } .hover\:bg-danger-100:hover { @@ -8683,11 +8808,11 @@ input[type='number'] { .hover\:text-custom-600:hover { --tw-text-opacity: 1; - color: rgb(37 99 235 / var(--tw-text-opacity)); + color: rgba(var(--c-600), var(--tw-text-opacity)); } .hover\:text-custom-700\/75:hover { - color: rgb(29 78 216 / 0.75); + color: rgba(var(--c-700), 0.75); } .hover\:text-gray-100:hover { @@ -8911,16 +9036,6 @@ input[type='number'] { --tw-ring-color: rgb(220 38 38 / var(--tw-ring-opacity)); } -.focus\:ring-gray-500:focus { - --tw-ring-opacity: 1; - --tw-ring-color: rgba(var(--gray-500), var(--tw-ring-opacity)); -} - -.focus\:ring-indigo-500:focus { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity)); -} - .focus\:ring-primary-200:focus { --tw-ring-opacity: 1; --tw-ring-color: rgb(191 219 254 / var(--tw-ring-opacity)); @@ -8952,10 +9067,6 @@ input[type='number'] { --tw-ring-offset-color: #eff6ff; } -.focus\:ring-offset-primary-800:focus { - --tw-ring-offset-color: #1e40af; -} - .checked\:focus\:ring-danger-500\/50:focus:checked { --tw-ring-color: rgb(239 68 68 / 0.5); } @@ -8975,7 +9086,7 @@ input[type='number'] { .focus-visible\:bg-custom-50:focus-visible { --tw-bg-opacity: 1; - background-color: rgb(239 246 255 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-50), var(--tw-bg-opacity)); } .focus-visible\:bg-gray-50:focus-visible { @@ -8984,7 +9095,12 @@ input[type='number'] { } .focus-visible\:text-custom-700\/75:focus-visible { - color: rgb(29 78 216 / 0.75); + color: rgba(var(--c-700), 0.75); +} + +.focus-visible\:text-gray-500:focus-visible { + --tw-text-opacity: 1; + color: rgba(var(--gray-500), var(--tw-text-opacity)); } .focus-visible\:text-gray-700\/75:focus-visible { @@ -9013,12 +9129,12 @@ input[type='number'] { } .focus-visible\:ring-custom-500\/50:focus-visible { - --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-color: rgba(var(--c-500), 0.5); } .focus-visible\:ring-custom-600:focus-visible { --tw-ring-opacity: 1; - --tw-ring-color: rgb(37 99 235 / var(--tw-ring-opacity)); + --tw-ring-color: rgba(var(--c-600), var(--tw-ring-opacity)); } .focus-visible\:ring-gray-400\/40:focus-visible { @@ -9159,6 +9275,11 @@ input[type='number'] { border-color: rgb(147 197 253 / var(--tw-border-opacity)); } +.group:focus-visible .group-focus-visible\:text-gray-500 { + --tw-text-opacity: 1; + color: rgba(var(--gray-500), var(--tw-text-opacity)); +} + .group:focus-visible .group-focus-visible\:text-gray-700 { --tw-text-opacity: 1; color: rgba(var(--gray-700), var(--tw-text-opacity)); @@ -9172,19 +9293,6 @@ input[type='number'] { text-decoration-line: underline; } -:is(.dark .dark\:flex) { - display: flex; -} - -:is(.dark .dark\:hidden) { - display: none; -} - -:is(.dark .dark\:translate-x-5) { - --tw-translate-x: 1.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - :is(.dark .dark\:divide-gray-600) > :not([hidden]) ~ :not([hidden]) { --tw-divide-opacity: 1; border-color: rgba(var(--gray-600), var(--tw-divide-opacity)); @@ -9235,6 +9343,10 @@ input[type='number'] { border-color: rgb(255 255 255 / 0.1); } +:is(.dark .dark\:border-white\/5) { + border-color: rgb(255 255 255 / 0.05); +} + :is(.dark .dark\:border-t-white\/10) { border-top-color: rgb(255 255 255 / 0.1); } @@ -9245,16 +9357,16 @@ input[type='number'] { } :is(.dark .dark\:bg-custom-400\/10) { - background-color: rgb(96 165 250 / 0.1); + background-color: rgba(var(--c-400), 0.1); } :is(.dark .dark\:bg-custom-500) { --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-500), var(--tw-bg-opacity)); } :is(.dark .dark\:bg-custom-500\/20) { - background-color: rgb(59 130 246 / 0.2); + background-color: rgba(var(--c-500), 0.2); } :is(.dark .dark\:bg-danger-400\/10) { @@ -9322,10 +9434,6 @@ input[type='number'] { background-color: rgba(var(--gray-900), 0.5); } -:is(.dark .dark\:bg-gray-900\/80) { - background-color: rgba(var(--gray-900), 0.8); -} - :is(.dark .dark\:bg-gray-950) { --tw-bg-opacity: 1; background-color: rgba(var(--gray-950), var(--tw-bg-opacity)); @@ -9356,6 +9464,10 @@ input[type='number'] { background-color: transparent; } +:is(.dark .dark\:bg-white\/10) { + background-color: rgb(255 255 255 / 0.1); +} + :is(.dark .dark\:bg-white\/5) { background-color: rgb(255 255 255 / 0.05); } @@ -9413,12 +9525,12 @@ input[type='number'] { } :is(.dark .dark\:text-custom-300\/50) { - color: rgb(147 197 253 / 0.5); + color: rgba(var(--c-300), 0.5); } :is(.dark .dark\:text-custom-400) { --tw-text-opacity: 1; - color: rgb(96 165 250 / var(--tw-text-opacity)); + color: rgba(var(--c-400), var(--tw-text-opacity)); } :is(.dark .dark\:text-danger-400) { @@ -9517,23 +9629,17 @@ input[type='number'] { color: rgba(var(--gray-400), var(--tw-placeholder-opacity)); } -:is(.dark .dark\:shadow-lg) { - --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - :is(.dark .dark\:ring-black\/10) { --tw-ring-color: rgb(0 0 0 / 0.1); } :is(.dark .dark\:ring-custom-400\/30) { - --tw-ring-color: rgb(96 165 250 / 0.3); + --tw-ring-color: rgba(var(--c-400), 0.3); } :is(.dark .dark\:ring-custom-500) { --tw-ring-opacity: 1; - --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); + --tw-ring-color: rgba(var(--c-500), var(--tw-ring-opacity)); } :is(.dark .dark\:ring-danger-500) { @@ -9585,6 +9691,14 @@ input[type='number'] { --tw-ring-color: rgb(255 255 255 / 0.2); } +:is(.dark .dark\:ring-white\/5) { + --tw-ring-color: rgb(255 255 255 / 0.05); +} + +:is(.dark .dark\:ring-offset-gray-900) { + --tw-ring-offset-color: rgba(var(--gray-900), 1); +} + :is(.dark .dark\:placeholder\:text-gray-500)::-moz-placeholder { --tw-text-opacity: 1; color: rgba(var(--gray-500), var(--tw-text-opacity)); @@ -9633,11 +9747,11 @@ input[type='number'] { :is(.dark .dark\:hover\:bg-custom-400:hover) { --tw-bg-opacity: 1; - background-color: rgb(96 165 250 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-400), var(--tw-bg-opacity)); } :is(.dark .dark\:hover\:bg-custom-400\/10:hover) { - background-color: rgb(96 165 250 / 0.1); + background-color: rgba(var(--c-400), 0.1); } :is(.dark .dark\:hover\:bg-danger-400\/20:hover) { @@ -9682,11 +9796,11 @@ input[type='number'] { :is(.dark .dark\:hover\:text-custom-300:hover) { --tw-text-opacity: 1; - color: rgb(147 197 253 / var(--tw-text-opacity)); + color: rgba(var(--c-300), var(--tw-text-opacity)); } :is(.dark .dark\:hover\:text-custom-300\/75:hover) { - color: rgb(147 197 253 / 0.75); + color: rgba(var(--c-300), 0.75); } :is(.dark .dark\:hover\:text-gray-200:hover) { @@ -9728,6 +9842,10 @@ input[type='number'] { color: rgb(255 255 255 / var(--tw-text-opacity)); } +:is(.dark .dark\:hover\:ring-white\/20:hover) { + --tw-ring-color: rgb(255 255 255 / 0.2); +} + :is(.dark .dark\:focus\:border-primary-500:focus) { --tw-border-opacity: 1; border-color: rgb(59 130 246 / var(--tw-border-opacity)); @@ -9785,7 +9903,7 @@ input[type='number'] { } :is(.dark .dark\:focus-visible\:bg-custom-400\/10:focus-visible) { - background-color: rgb(96 165 250 / 0.1); + background-color: rgba(var(--c-400), 0.1); } :is(.dark .dark\:focus-visible\:bg-white\/5:focus-visible) { @@ -9793,20 +9911,25 @@ input[type='number'] { } :is(.dark .dark\:focus-visible\:text-custom-300\/75:focus-visible) { - color: rgb(147 197 253 / 0.75); + color: rgba(var(--c-300), 0.75); } :is(.dark .dark\:focus-visible\:text-gray-300\/75:focus-visible) { color: rgba(var(--gray-300), 0.75); } +:is(.dark .dark\:focus-visible\:text-gray-400:focus-visible) { + --tw-text-opacity: 1; + color: rgba(var(--gray-400), var(--tw-text-opacity)); +} + :is(.dark .dark\:focus-visible\:ring-custom-400\/50:focus-visible) { - --tw-ring-color: rgb(96 165 250 / 0.5); + --tw-ring-color: rgba(var(--c-400), 0.5); } :is(.dark .dark\:focus-visible\:ring-custom-500:focus-visible) { --tw-ring-opacity: 1; - --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); + --tw-ring-color: rgba(var(--c-500), var(--tw-ring-opacity)); } :is(.dark .dark\:focus-visible\:ring-primary-500:focus-visible) { @@ -9874,6 +9997,11 @@ input[type='number'] { color: rgba(var(--gray-200), var(--tw-text-opacity)); } +:is(.dark .group:focus-visible .dark\:group-focus-visible\:text-gray-400) { + --tw-text-opacity: 1; + color: rgba(var(--gray-400), var(--tw-text-opacity)); +} + @media (min-width: 640px) { .sm\:col-\[--col-span-sm\] { grid-column: var(--col-span-sm); @@ -9930,6 +10058,10 @@ input[type='number'] { margin-left: auto; } + .sm\:ms-auto { + margin-inline-start: auto; + } + .sm\:mt-0 { margin-top: 0px; } @@ -9946,6 +10078,10 @@ input[type='number'] { display: flex; } + .sm\:table-cell { + display: table-cell; + } + .sm\:grid { display: grid; } @@ -9982,6 +10118,10 @@ input[type='number'] { width: 16rem; } + .sm\:w-\[calc\(100\%\+3rem\)\] { + width: calc(100% + 3rem); + } + .sm\:w-auto { width: auto; } @@ -10096,6 +10236,10 @@ input[type='number'] { flex-direction: row-reverse; } + .sm\:flex-nowrap { + flex-wrap: nowrap; + } + .sm\:items-start { align-items: flex-start; } @@ -10112,6 +10256,14 @@ input[type='number'] { justify-content: space-between; } + .sm\:gap-1 { + gap: 0.25rem; + } + + .sm\:gap-3 { + gap: 0.75rem; + } + .sm\:gap-4 { gap: 1rem; } @@ -10194,11 +10346,6 @@ input[type='number'] { padding-right: 0px; } - .sm\:px-4 { - padding-left: 1rem; - padding-right: 1rem; - } - .sm\:px-5 { padding-left: 1.25rem; padding-right: 1.25rem; @@ -10209,6 +10356,16 @@ input[type='number'] { padding-right: 1.5rem; } + .sm\:py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; + } + + .sm\:py-1\.5 { + padding-top: 0.375rem; + padding-bottom: 0.375rem; + } + .sm\:py-12 { padding-top: 3rem; padding-bottom: 3rem; @@ -10234,6 +10391,14 @@ input[type='number'] { padding-bottom: 1.5rem; } + .sm\:pe-3 { + padding-inline-end: 0.75rem; + } + + .sm\:pe-6 { + padding-inline-end: 1.5rem; + } + .sm\:pl-8 { padding-left: 2rem; } @@ -10242,6 +10407,14 @@ input[type='number'] { padding-right: 2rem; } + .sm\:ps-3 { + padding-inline-start: 0.75rem; + } + + .sm\:ps-6 { + padding-inline-start: 1.5rem; + } + .sm\:pt-1 { padding-top: 0.25rem; } @@ -10293,6 +10466,22 @@ input[type='number'] { line-height: 2.25rem; } + .sm\:first-of-type\:ps-3:first-of-type { + padding-inline-start: 0.75rem; + } + + .sm\:first-of-type\:ps-6:first-of-type { + padding-inline-start: 1.5rem; + } + + .sm\:last-of-type\:pe-3:last-of-type { + padding-inline-end: 0.75rem; + } + + .sm\:last-of-type\:pe-6:last-of-type { + padding-inline-end: 1.5rem; + } + :is(.dark .sm\:dark\:border-gray-700) { --tw-border-opacity: 1; border-color: rgba(var(--gray-700), var(--tw-border-opacity)); @@ -10328,6 +10517,10 @@ input[type='number'] { display: flex; } + .md\:table-cell { + display: table-cell; + } + .md\:inline-grid { display: inline-grid; } @@ -10410,10 +10603,22 @@ input[type='number'] { align-items: center; } + .md\:justify-end { + justify-content: flex-end; + } + .md\:justify-between { justify-content: space-between; } + .md\:gap-1 { + gap: 0.25rem; + } + + .md\:gap-3 { + gap: 0.75rem; + } + .md\:gap-x-12 { -moz-column-gap: 3rem; column-gap: 3rem; @@ -10448,6 +10653,10 @@ input[type='number'] { .md\:p-20 { padding: 5rem; } + + .md\:ps-3 { + padding-inline-start: 0.75rem; + } } @media (min-width: 1024px) { @@ -10545,26 +10754,6 @@ input[type='number'] { height: 100%; } - .lg\:h-\[650px\] { - height: 650px; - } - - .lg\:max-h-\[650px\] { - max-height: 650px; - } - - .lg\:max-h-\[550px\] { - max-height: 550px; - } - - .lg\:max-h-\[600px\] { - max-height: 600px; - } - - .lg\:max-h-\[620px\] { - max-height: 620px; - } - .lg\:max-h-\[580px\] { max-height: 580px; } @@ -10674,6 +10863,14 @@ input[type='number'] { justify-content: space-between; } + .lg\:gap-1 { + gap: 0.25rem; + } + + .lg\:gap-3 { + gap: 0.75rem; + } + .lg\:gap-4 { gap: 1rem; } @@ -10827,6 +11024,10 @@ input[type='number'] { padding-top: 1.25rem; } + .lg\:pt-6 { + padding-top: 1.5rem; + } + .lg\:text-left { text-align: left; } @@ -10870,6 +11071,14 @@ input[type='number'] { grid-column-start: var(--col-start-xl); } + .xl\:block { + display: block; + } + + .xl\:table-cell { + display: table-cell; + } + .xl\:inline-grid { display: inline-grid; } @@ -10934,6 +11143,14 @@ input[type='number'] { .xl\:items-center { align-items: center; } + + .xl\:gap-1 { + gap: 0.25rem; + } + + .xl\:gap-3 { + gap: 0.75rem; + } } @media (min-width: 1536px) { @@ -10945,6 +11162,14 @@ input[type='number'] { grid-column-start: var(--col-start-2xl); } + .\32xl\:block { + display: block; + } + + .\32xl\:table-cell { + display: table-cell; + } + .\32xl\:inline-grid { display: inline-grid; } @@ -11006,6 +11231,14 @@ input[type='number'] { align-items: center; } + .\32xl\:gap-1 { + gap: 0.25rem; + } + + .\32xl\:gap-3 { + gap: 0.75rem; + } + .\32xl\:px-6 { padding-left: 1.5rem; padding-right: 1.5rem; @@ -11071,6 +11304,10 @@ input[type='number'] { display: none; } +.\[\&\:not\(\:first-of-type\)\]\:border-s:not(:first-of-type) { + border-inline-start-width: 1px; +} + .\[\&\:not\(\:has\(\.fi-ac-action\:focus\)\)\]\:focus-within\:ring-2:focus-within:not(:has(.fi-ac-action:focus)) { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); @@ -11097,6 +11334,10 @@ input[type='number'] { --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); } +.\[\&\:not\(\:last-of-type\)\]\:border-e:not(:last-of-type) { + border-inline-end-width: 1px; +} + .\[\&\:not\(\:nth-child\(1_of_\.fi-btn\)\)\]\:shadow-\[-1px_0_0_0_theme\(colors\.gray\.200\)\]:not(:nth-child(1 of .fi-btn)) { --tw-shadow: -1px 0 0 0 rgba(var(--gray-200), 1); --tw-shadow-colored: -1px 0 0 0 var(--tw-shadow-color); @@ -11123,10 +11364,47 @@ input[type='number'] { border-end-end-radius: 0.5rem; } +.\[\&\>\*\:first-child\]\:relative>*:first-child { + position: relative; +} + .\[\&\>\*\:first-child\]\:mt-0>*:first-child { margin-top: 0px; } +.\[\&\>\*\:first-child\]\:before\:absolute>*:first-child::before { + content: var(--tw-content); + position: absolute; +} + +.\[\&\>\*\:first-child\]\:before\:inset-y-0>*:first-child::before { + content: var(--tw-content); + top: 0px; + bottom: 0px; +} + +.\[\&\>\*\:first-child\]\:before\:start-0>*:first-child::before { + content: var(--tw-content); + inset-inline-start: 0px; +} + +.\[\&\>\*\:first-child\]\:before\:w-0\.5>*:first-child::before { + content: var(--tw-content); + width: 0.125rem; +} + +.\[\&\>\*\:first-child\]\:before\:bg-primary-600>*:first-child::before { + content: var(--tw-content); + --tw-bg-opacity: 1; + background-color: rgb(37 99 235 / var(--tw-bg-opacity)); +} + +:is(.dark .\[\&\>\*\:first-child\]\:dark\:before\:bg-primary-500)>*:first-child::before { + content: var(--tw-content); + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); +} + .\[\&\>\*\:last-child\]\:mb-0>*:last-child { margin-bottom: 0px; } @@ -11168,9 +11446,23 @@ input[type='number'] { color: rgb(255 255 255 / var(--tw-text-opacity)); } +@media(hover:hover) { + .\[\@media\(hover\:hover\)\]\:transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + } + + .\[\@media\(hover\:hover\)\]\:duration-75 { + transition-duration: 75ms; + } +} + input:checked+.\[input\:checked\+\&\]\:bg-custom-600 { --tw-bg-opacity: 1; - background-color: rgb(37 99 235 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-600), var(--tw-bg-opacity)); } input:checked+.\[input\:checked\+\&\]\:text-white { @@ -11186,25 +11478,25 @@ input:checked+.\[input\:checked\+\&\]\:ring-0 { input:checked+.\[input\:checked\+\&\]\:hover\:bg-custom-500:hover { --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-500), var(--tw-bg-opacity)); } :is(.dark input:checked+.dark\:\[input\:checked\+\&\]\:bg-custom-500) { --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-500), var(--tw-bg-opacity)); } :is(.dark input:checked+.dark\:\[input\:checked\+\&\]\:hover\:bg-custom-400:hover) { --tw-bg-opacity: 1; - background-color: rgb(96 165 250 / var(--tw-bg-opacity)); + background-color: rgba(var(--c-400), var(--tw-bg-opacity)); } input:checked:focus-visible+.\[input\:checked\:focus-visible\+\&\]\:ring-custom-500\/50 { - --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-color: rgba(var(--c-500), 0.5); } :is(.dark input:checked:focus-visible+.dark\:\[input\:checked\:focus-visible\+\&\]\:ring-custom-400\/50) { - --tw-ring-color: rgb(96 165 250 / 0.5); + --tw-ring-color: rgba(var(--c-400), 0.5); } input:focus-visible+.\[input\:focus-visible\+\&\]\:z-10 { diff --git a/packages/admin/public/shopper.js b/packages/admin/public/shopper.js index 1627bc24c..12812203d 100755 --- a/packages/admin/public/shopper.js +++ b/packages/admin/public/shopper.js @@ -1,3 +1,174 @@ (() => { + // resources/js/components/panel.js + var SlideOverPanel = () => { + return { + open: false, + showActiveComponent: true, + activeComponent: false, + componentHistory: [], + panelWidth: null, + listeners: [], + getActiveComponentPanelAttribute(key) { + if (this.$wire.get("components")[this.activeComponent] !== void 0) { + return this.$wire.get("components")[this.activeComponent]["panelAttributes"][key]; + } + }, + closePanelOnEscape(trigger) { + if (this.getActiveComponentPanelAttribute("closeOnEscape") === false) { + return; + } + let force = this.getActiveComponentPanelAttribute("closeOnEscapeIsForceful") === true; + this.closePanel(force); + }, + closePanelOnClickAway(trigger) { + if (this.getActiveComponentPanelAttribute("closeOnClickAway") === false) { + return; + } + this.closePanel(true); + }, + closePanel(force = false, skipPreviousPanels = 0, destroySkipped = false) { + if (this.show === false) { + return; + } + if (this.getActiveComponentPanelAttribute("dispatchCloseEvent") === true) { + const componentName = this.$wire.get("components")[this.activeComponent].name; + Livewire.dispatch("panelClosed", { name: componentName }); + } + if (this.getActiveComponentPanelAttribute("destroyOnClose") === true) { + Livewire.dispatch("destroyComponent", { id: this.activeComponent }); + } + if (skipPreviousPanels > 0) { + for (let i = 0; i < skipPreviousPanels; i++) { + if (destroySkipped) { + const id2 = this.componentHistory[this.componentHistory.length - 1]; + Livewire.dispatch("destroyComponent", { id: id2 }); + } + this.componentHistory.pop(); + } + } + const id = this.componentHistory.pop(); + if (id && !force) { + if (id) { + this.setActivePanelComponent(id, true); + } else { + this.setShowPropertyTo(false); + } + } else { + this.setShowPropertyTo(false); + } + }, + setActivePanelComponent(id, skip = false) { + this.setShowPropertyTo(true); + if (this.activeComponent === id) { + return; + } + if (this.activeComponent !== false && skip === false) { + this.componentHistory.push(this.activeComponent); + } + let focusableTimeout = 50; + if (this.activeComponent === false) { + this.activeComponent = id; + this.showActiveComponent = true; + this.panelWidth = this.getActiveComponentPanelAttribute("maxWidthClass"); + } else { + this.showActiveComponent = false; + focusableTimeout = 400; + setTimeout(() => { + this.activeComponent = id; + this.showActiveComponent = true; + this.panelWidth = this.getActiveComponentPanelAttribute("maxWidthClass"); + }, 300); + } + this.$nextTick(() => { + let focusable = this.$refs[id]?.querySelector("[autofocus]"); + if (focusable) { + setTimeout(() => { + focusable.focus(); + }, focusableTimeout); + } + }); + }, + focusables() { + let selector = "a, button, input:not([type='hidden'], textarea, select, details, [tabindex]:not([tabindex='-1'])"; + return [...this.$el.querySelectorAll(selector)].filter((el) => !el.hasAttribute("disabled")); + }, + firstFocusable() { + return this.focusables()[0]; + }, + lastFocusable() { + return this.focusables().slice(-1)[0]; + }, + nextFocusable() { + return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable(); + }, + prevFocusable() { + return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable(); + }, + nextFocusableIndex() { + return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1); + }, + prevFocusableIndex() { + return Math.max(0, this.focusables().indexOf(document.activeElement)) - 1; + }, + setShowPropertyTo(open) { + this.open = open; + if (open) { + document.body.classList.add("overflow-y-hidden"); + } else { + document.body.classList.remove("overflow-y-hidden"); + setTimeout(() => { + this.activeComponent = false; + this.$wire.resetState(); + }, 300); + } + }, + init() { + this.panelWidth = this.getActiveComponentPanelAttribute("maxWidthClass"); + this.listeners.push( + Livewire.on("closePanel", (data) => { + this.closePanel(data?.force ?? false, data?.skipPreviousPanels ?? 0, data?.destroySkipped ?? false); + }) + ); + this.listeners.push( + Livewire.on("activePanelComponentChanged", ({ id }) => { + this.setActivePanelComponent(id); + }) + ); + }, + destroy() { + this.listeners.forEach((listener) => { + listener(); + }); + } + }; + }; + var panel_default = SlideOverPanel; + + // resources/js/index.js + window.SlideOverPanel = panel_default; + document.addEventListener("alpine:init", () => { + const theme = localStorage.getItem("theme") ?? "light"; + window.Alpine.store( + "theme", + theme === "dark" || theme === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" + ); + window.addEventListener("theme-changed", (event) => { + let theme2 = event.detail; + localStorage.setItem("theme", theme2); + if (theme2 === "system") { + theme2 = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + } + window.Alpine.store("theme", theme2); + }); + window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => { + if (localStorage.getItem("theme") === "system") { + window.Alpine.store("theme", event.matches ? "dark" : "light"); + } + }); + window.Alpine.effect(() => { + const theme2 = window.Alpine.store("theme"); + theme2 === "dark" ? document.documentElement.classList.add("dark") : document.documentElement.classList.remove("dark"); + }); + }); })(); -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFtdLAogICJzb3VyY2VzQ29udGVudCI6IFtdLAogICJtYXBwaW5ncyI6ICIiLAogICJuYW1lcyI6IFtdCn0K +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vcmVzb3VyY2VzL2pzL2NvbXBvbmVudHMvcGFuZWwuanMiLCAiLi4vcmVzb3VyY2VzL2pzL2luZGV4LmpzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBTbGlkZU92ZXJQYW5lbCA9ICgpID0+IHtcbiAgcmV0dXJuIHtcbiAgICBvcGVuOiBmYWxzZSxcbiAgICBzaG93QWN0aXZlQ29tcG9uZW50OiB0cnVlLFxuICAgIGFjdGl2ZUNvbXBvbmVudDogZmFsc2UsXG4gICAgY29tcG9uZW50SGlzdG9yeTogW10sXG4gICAgcGFuZWxXaWR0aDogbnVsbCxcbiAgICBsaXN0ZW5lcnM6IFtdLFxuICAgIGdldEFjdGl2ZUNvbXBvbmVudFBhbmVsQXR0cmlidXRlKGtleSkge1xuICAgICAgaWYgKHRoaXMuJHdpcmUuZ2V0KCdjb21wb25lbnRzJylbdGhpcy5hY3RpdmVDb21wb25lbnRdICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuJHdpcmUuZ2V0KCdjb21wb25lbnRzJylbdGhpcy5hY3RpdmVDb21wb25lbnRdWydwYW5lbEF0dHJpYnV0ZXMnXVtrZXldO1xuICAgICAgfVxuICAgIH0sXG4gICAgY2xvc2VQYW5lbE9uRXNjYXBlKHRyaWdnZXIpIHtcbiAgICAgIGlmICh0aGlzLmdldEFjdGl2ZUNvbXBvbmVudFBhbmVsQXR0cmlidXRlKCdjbG9zZU9uRXNjYXBlJykgPT09IGZhbHNlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgbGV0IGZvcmNlID0gdGhpcy5nZXRBY3RpdmVDb21wb25lbnRQYW5lbEF0dHJpYnV0ZSgnY2xvc2VPbkVzY2FwZUlzRm9yY2VmdWwnKSA9PT0gdHJ1ZTtcbiAgICAgIHRoaXMuY2xvc2VQYW5lbChmb3JjZSk7XG4gICAgfSxcbiAgICBjbG9zZVBhbmVsT25DbGlja0F3YXkodHJpZ2dlcikge1xuICAgICAgaWYgKHRoaXMuZ2V0QWN0aXZlQ29tcG9uZW50UGFuZWxBdHRyaWJ1dGUoJ2Nsb3NlT25DbGlja0F3YXknKSA9PT0gZmFsc2UpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmNsb3NlUGFuZWwodHJ1ZSk7XG4gICAgfSxcbiAgICBjbG9zZVBhbmVsKGZvcmNlID0gZmFsc2UsIHNraXBQcmV2aW91c1BhbmVscyA9IDAsIGRlc3Ryb3lTa2lwcGVkID0gZmFsc2UpIHtcbiAgICAgIGlmKHRoaXMuc2hvdyA9PT0gZmFsc2UpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5nZXRBY3RpdmVDb21wb25lbnRQYW5lbEF0dHJpYnV0ZSgnZGlzcGF0Y2hDbG9zZUV2ZW50JykgPT09IHRydWUpIHtcbiAgICAgICAgY29uc3QgY29tcG9uZW50TmFtZSA9IHRoaXMuJHdpcmUuZ2V0KCdjb21wb25lbnRzJylbdGhpcy5hY3RpdmVDb21wb25lbnRdLm5hbWU7XG4gICAgICAgIExpdmV3aXJlLmRpc3BhdGNoKCdwYW5lbENsb3NlZCcsIHsgbmFtZTogY29tcG9uZW50TmFtZSB9KTtcbiAgICAgIH1cblxuICAgICAgaWYgKHRoaXMuZ2V0QWN0aXZlQ29tcG9uZW50UGFuZWxBdHRyaWJ1dGUoJ2Rlc3Ryb3lPbkNsb3NlJykgPT09IHRydWUpIHtcbiAgICAgICAgTGl2ZXdpcmUuZGlzcGF0Y2goJ2Rlc3Ryb3lDb21wb25lbnQnLCB7IGlkOiB0aGlzLmFjdGl2ZUNvbXBvbmVudCB9KTtcbiAgICAgIH1cblxuICAgICAgaWYgKHNraXBQcmV2aW91c1BhbmVscyA+IDApIHtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBza2lwUHJldmlvdXNQYW5lbHM7IGkrKykge1xuICAgICAgICAgIGlmIChkZXN0cm95U2tpcHBlZCkge1xuICAgICAgICAgICAgY29uc3QgaWQgPSB0aGlzLmNvbXBvbmVudEhpc3RvcnlbdGhpcy5jb21wb25lbnRIaXN0b3J5Lmxlbmd0aCAtIDFdO1xuICAgICAgICAgICAgTGl2ZXdpcmUuZGlzcGF0Y2goJ2Rlc3Ryb3lDb21wb25lbnQnLCB7IGlkOiBpZCB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhpcy5jb21wb25lbnRIaXN0b3J5LnBvcCgpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGlkID0gdGhpcy5jb21wb25lbnRIaXN0b3J5LnBvcCgpO1xuXG4gICAgICBpZiAoaWQgJiYgIWZvcmNlKSB7XG4gICAgICAgIGlmIChpZCkge1xuICAgICAgICAgIHRoaXMuc2V0QWN0aXZlUGFuZWxDb21wb25lbnQoaWQsIHRydWUpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRoaXMuc2V0U2hvd1Byb3BlcnR5VG8oZmFsc2UpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnNldFNob3dQcm9wZXJ0eVRvKGZhbHNlKTtcbiAgICAgIH1cbiAgICB9LFxuICAgIHNldEFjdGl2ZVBhbmVsQ29tcG9uZW50KGlkLCBza2lwID0gZmFsc2UpIHtcbiAgICAgIHRoaXMuc2V0U2hvd1Byb3BlcnR5VG8odHJ1ZSk7XG5cbiAgICAgIGlmICh0aGlzLmFjdGl2ZUNvbXBvbmVudCA9PT0gaWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5hY3RpdmVDb21wb25lbnQgIT09IGZhbHNlICYmIHNraXAgPT09IGZhbHNlKSB7XG4gICAgICAgIHRoaXMuY29tcG9uZW50SGlzdG9yeS5wdXNoKHRoaXMuYWN0aXZlQ29tcG9uZW50KTtcbiAgICAgIH1cblxuICAgICAgbGV0IGZvY3VzYWJsZVRpbWVvdXQgPSA1MDtcblxuICAgICAgaWYgKHRoaXMuYWN0aXZlQ29tcG9uZW50ID09PSBmYWxzZSkge1xuICAgICAgICB0aGlzLmFjdGl2ZUNvbXBvbmVudCA9IGlkXG4gICAgICAgIHRoaXMuc2hvd0FjdGl2ZUNvbXBvbmVudCA9IHRydWU7XG4gICAgICAgIHRoaXMucGFuZWxXaWR0aCA9IHRoaXMuZ2V0QWN0aXZlQ29tcG9uZW50UGFuZWxBdHRyaWJ1dGUoJ21heFdpZHRoQ2xhc3MnKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuc2hvd0FjdGl2ZUNvbXBvbmVudCA9IGZhbHNlO1xuXG4gICAgICAgIGZvY3VzYWJsZVRpbWVvdXQgPSA0MDA7XG5cbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgdGhpcy5hY3RpdmVDb21wb25lbnQgPSBpZDtcbiAgICAgICAgICB0aGlzLnNob3dBY3RpdmVDb21wb25lbnQgPSB0cnVlO1xuICAgICAgICAgIHRoaXMucGFuZWxXaWR0aCA9IHRoaXMuZ2V0QWN0aXZlQ29tcG9uZW50UGFuZWxBdHRyaWJ1dGUoJ21heFdpZHRoQ2xhc3MnKTtcbiAgICAgICAgfSwgMzAwKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy4kbmV4dFRpY2soKCkgPT4ge1xuICAgICAgICBsZXQgZm9jdXNhYmxlID0gdGhpcy4kcmVmc1tpZF0/LnF1ZXJ5U2VsZWN0b3IoJ1thdXRvZm9jdXNdJyk7XG4gICAgICAgIGlmIChmb2N1c2FibGUpIHtcbiAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIGZvY3VzYWJsZS5mb2N1cygpO1xuICAgICAgICAgIH0sIGZvY3VzYWJsZVRpbWVvdXQpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9LFxuICAgIGZvY3VzYWJsZXMoKSB7XG4gICAgICBsZXQgc2VsZWN0b3IgPSAnYSwgYnV0dG9uLCBpbnB1dDpub3QoW3R5cGU9XFwnaGlkZGVuXFwnXSwgdGV4dGFyZWEsIHNlbGVjdCwgZGV0YWlscywgW3RhYmluZGV4XTpub3QoW3RhYmluZGV4PVxcJy0xXFwnXSknXG5cbiAgICAgIHJldHVybiBbLi4udGhpcy4kZWwucXVlcnlTZWxlY3RvckFsbChzZWxlY3RvcildXG4gICAgICAgIC5maWx0ZXIoZWwgPT4gIWVsLmhhc0F0dHJpYnV0ZSgnZGlzYWJsZWQnKSlcbiAgICB9LFxuICAgIGZpcnN0Rm9jdXNhYmxlKCkge1xuICAgICAgcmV0dXJuIHRoaXMuZm9jdXNhYmxlcygpWzBdXG4gICAgfSxcbiAgICBsYXN0Rm9jdXNhYmxlKCkge1xuICAgICAgcmV0dXJuIHRoaXMuZm9jdXNhYmxlcygpLnNsaWNlKC0xKVswXVxuICAgIH0sXG4gICAgbmV4dEZvY3VzYWJsZSgpIHtcbiAgICAgIHJldHVybiB0aGlzLmZvY3VzYWJsZXMoKVt0aGlzLm5leHRGb2N1c2FibGVJbmRleCgpXSB8fCB0aGlzLmZpcnN0Rm9jdXNhYmxlKClcbiAgICB9LFxuICAgIHByZXZGb2N1c2FibGUoKSB7XG4gICAgICByZXR1cm4gdGhpcy5mb2N1c2FibGVzKClbdGhpcy5wcmV2Rm9jdXNhYmxlSW5kZXgoKV0gfHwgdGhpcy5sYXN0Rm9jdXNhYmxlKClcbiAgICB9LFxuICAgIG5leHRGb2N1c2FibGVJbmRleCgpIHtcbiAgICAgIHJldHVybiAodGhpcy5mb2N1c2FibGVzKCkuaW5kZXhPZihkb2N1bWVudC5hY3RpdmVFbGVtZW50KSArIDEpICUgKHRoaXMuZm9jdXNhYmxlcygpLmxlbmd0aCArIDEpXG4gICAgfSxcbiAgICBwcmV2Rm9jdXNhYmxlSW5kZXgoKSB7XG4gICAgICByZXR1cm4gTWF0aC5tYXgoMCwgdGhpcy5mb2N1c2FibGVzKCkuaW5kZXhPZihkb2N1bWVudC5hY3RpdmVFbGVtZW50KSkgLSAxXG4gICAgfSxcbiAgICBzZXRTaG93UHJvcGVydHlUbyhvcGVuKSB7XG4gICAgICB0aGlzLm9wZW4gPSBvcGVuO1xuXG4gICAgICBpZiAob3Blbikge1xuICAgICAgICBkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5hZGQoJ292ZXJmbG93LXktaGlkZGVuJyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5yZW1vdmUoJ292ZXJmbG93LXktaGlkZGVuJyk7XG5cbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgdGhpcy5hY3RpdmVDb21wb25lbnQgPSBmYWxzZTtcbiAgICAgICAgICB0aGlzLiR3aXJlLnJlc2V0U3RhdGUoKTtcbiAgICAgICAgfSwgMzAwKTtcbiAgICAgIH1cbiAgICB9LFxuICAgIGluaXQoKSB7XG4gICAgICB0aGlzLnBhbmVsV2lkdGggPSB0aGlzLmdldEFjdGl2ZUNvbXBvbmVudFBhbmVsQXR0cmlidXRlKCdtYXhXaWR0aENsYXNzJyk7XG5cbiAgICAgIHRoaXMubGlzdGVuZXJzLnB1c2goXG4gICAgICAgIExpdmV3aXJlLm9uKCdjbG9zZVBhbmVsJywgKGRhdGEpID0+IHtcbiAgICAgICAgICB0aGlzLmNsb3NlUGFuZWwoZGF0YT8uZm9yY2UgPz8gZmFsc2UsIGRhdGE/LnNraXBQcmV2aW91c1BhbmVscyA/PyAwLCBkYXRhPy5kZXN0cm95U2tpcHBlZCA/PyBmYWxzZSk7XG4gICAgICAgIH0pXG4gICAgICApO1xuXG4gICAgICB0aGlzLmxpc3RlbmVycy5wdXNoKFxuICAgICAgICBMaXZld2lyZS5vbignYWN0aXZlUGFuZWxDb21wb25lbnRDaGFuZ2VkJywgKHsgaWQgfSkgPT4ge1xuICAgICAgICAgIHRoaXMuc2V0QWN0aXZlUGFuZWxDb21wb25lbnQoaWQpO1xuICAgICAgICB9KVxuICAgICAgKTtcbiAgICB9LFxuICAgIGRlc3Ryb3koKSB7XG4gICAgICB0aGlzLmxpc3RlbmVycy5mb3JFYWNoKChsaXN0ZW5lcikgPT4ge1xuICAgICAgICBsaXN0ZW5lcigpO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBkZWZhdWx0IFNsaWRlT3ZlclBhbmVsXG4iLCAiLypcbmltcG9ydCB7IEFscGluZSwgTGl2ZXdpcmUgfSBmcm9tICcuLi8uLi92ZW5kb3IvbGl2ZXdpcmUvbGl2ZXdpcmUvZGlzdC9saXZld2lyZS5lc20nXG5cbmltcG9ydCBpbnRlcm5hdGlvbmFsTnVtYmVyIGZyb20gJy4vcGx1Z2lucy9pbnRlcm5hdGlvbmFsTnVtYmVyJ1xuaW1wb3J0IEtleVByZXNzIGZyb20gJy4vcGx1Z2lucy9rZXlQcmVzcydcbmltcG9ydCAnLi9oZWxwZXJzL3dpbmRvdydcbmltcG9ydCAnLi9oZWxwZXJzL3RyaXgnXG5cbkFscGluZS5wbHVnaW4oS2V5UHJlc3MpXG5BbHBpbmUuZGF0YSgnaW50ZXJuYXRpb25hbE51bWJlcicsIGludGVybmF0aW9uYWxOdW1iZXIpXG5cbndpbmRvdy5pbnRlcm5hdGlvbmFsTnVtYmVyID0gaW50ZXJuYXRpb25hbE51bWJlclxuXG5jb25zdCB0aGVtZSA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKCd0aGVtZScpID8/ICdzeXN0ZW0nXG5cbndpbmRvdy5BbHBpbmUuc3RvcmUoXG4gICd0aGVtZScsXG4gIHRoZW1lID09PSAnZGFyaycgfHxcbiAgICAodGhlbWUgPT09ICdzeXN0ZW0nICYmXG4gICAgICB3aW5kb3cubWF0Y2hNZWRpYSgnKHByZWZlcnMtY29sb3Itc2NoZW1lOiBkYXJrKScpLm1hdGNoZXMpXG4gICAgPyAnZGFyaydcbiAgICA6ICdsaWdodCcsXG4pXG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCd0aGVtZS1jaGFuZ2VkJywgKGV2ZW50KSA9PiB7XG4gIGxldCB0aGVtZSA9IGV2ZW50LmRldGFpbFxuXG4gIGxvY2FsU3RvcmFnZS5zZXRJdGVtKCd0aGVtZScsIHRoZW1lKVxuXG4gIGlmICh0aGVtZSA9PT0gJ3N5c3RlbScpIHtcbiAgICB0aGVtZSA9IHdpbmRvdy5tYXRjaE1lZGlhKCcocHJlZmVycy1jb2xvci1zY2hlbWU6IGRhcmspJykubWF0Y2hlc1xuICAgICAgPyAnZGFyaydcbiAgICAgIDogJ2xpZ2h0J1xuICB9XG5cbiAgd2luZG93LkFscGluZS5zdG9yZSgndGhlbWUnLCB0aGVtZSlcbn0pXG5cbndpbmRvd1xuICAubWF0Y2hNZWRpYSgnKHByZWZlcnMtY29sb3Itc2NoZW1lOiBkYXJrKScpXG4gIC5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCAoZXZlbnQpID0+IHtcbiAgICBpZiAobG9jYWxTdG9yYWdlLmdldEl0ZW0oJ3RoZW1lJykgPT09ICdzeXN0ZW0nKSB7XG4gICAgICB3aW5kb3cuQWxwaW5lLnN0b3JlKCd0aGVtZScsIGV2ZW50Lm1hdGNoZXMgPyAnZGFyaycgOiAnbGlnaHQnKVxuICAgIH1cbiAgfSlcblxud2luZG93LkFscGluZS5lZmZlY3QoKCkgPT4ge1xuICBjb25zdCB0aGVtZSA9IHdpbmRvdy5BbHBpbmUuc3RvcmUoJ3RoZW1lJylcblxuICB0aGVtZSA9PT0gJ2RhcmsnXG4gICAgPyBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuY2xhc3NMaXN0LmFkZCgnZGFyaycpXG4gICAgOiBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgnZGFyaycpXG59KVxuXG5MaXZld2lyZS5zdGFydCgpXG4qL1xuaW1wb3J0IFNsaWRlT3ZlclBhbmVsIGZyb20gJy4vY29tcG9uZW50cy9wYW5lbCdcblxud2luZG93LlNsaWRlT3ZlclBhbmVsID0gU2xpZGVPdmVyUGFuZWxcblxuZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignYWxwaW5lOmluaXQnLCAoKSA9PiB7XG4gIGNvbnN0IHRoZW1lID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oJ3RoZW1lJykgPz8gJ2xpZ2h0J1xuXG4gIHdpbmRvdy5BbHBpbmUuc3RvcmUoXG4gICAgJ3RoZW1lJyxcbiAgICB0aGVtZSA9PT0gJ2RhcmsnIHx8XG4gICAgKHRoZW1lID09PSAnc3lzdGVtJyAmJlxuICAgICAgd2luZG93Lm1hdGNoTWVkaWEoJyhwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyayknKS5tYXRjaGVzKVxuICAgICAgPyAnZGFyaydcbiAgICAgIDogJ2xpZ2h0JyxcbiAgKVxuXG4gIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCd0aGVtZS1jaGFuZ2VkJywgKGV2ZW50KSA9PiB7XG4gICAgbGV0IHRoZW1lID0gZXZlbnQuZGV0YWlsXG5cbiAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSgndGhlbWUnLCB0aGVtZSlcblxuICAgIGlmICh0aGVtZSA9PT0gJ3N5c3RlbScpIHtcbiAgICAgIHRoZW1lID0gd2luZG93Lm1hdGNoTWVkaWEoJyhwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyayknKS5tYXRjaGVzXG4gICAgICAgID8gJ2RhcmsnXG4gICAgICAgIDogJ2xpZ2h0J1xuICAgIH1cblxuICAgIHdpbmRvdy5BbHBpbmUuc3RvcmUoJ3RoZW1lJywgdGhlbWUpXG4gIH0pXG5cbiAgd2luZG93XG4gICAgLm1hdGNoTWVkaWEoJyhwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyayknKVxuICAgIC5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCAoZXZlbnQpID0+IHtcbiAgICAgIGlmIChsb2NhbFN0b3JhZ2UuZ2V0SXRlbSgndGhlbWUnKSA9PT0gJ3N5c3RlbScpIHtcbiAgICAgICAgd2luZG93LkFscGluZS5zdG9yZSgndGhlbWUnLCBldmVudC5tYXRjaGVzID8gJ2RhcmsnIDogJ2xpZ2h0JylcbiAgICAgIH1cbiAgICB9KVxuXG4gIHdpbmRvdy5BbHBpbmUuZWZmZWN0KCgpID0+IHtcbiAgICBjb25zdCB0aGVtZSA9IHdpbmRvdy5BbHBpbmUuc3RvcmUoJ3RoZW1lJylcblxuICAgIHRoZW1lID09PSAnZGFyaydcbiAgICAgID8gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsYXNzTGlzdC5hZGQoJ2RhcmsnKVxuICAgICAgOiBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgnZGFyaycpXG4gIH0pXG59KVxuIl0sCiAgIm1hcHBpbmdzIjogIjs7QUFBQSxNQUFNLGlCQUFpQixNQUFNO0FBQzNCLFdBQU87QUFBQSxNQUNMLE1BQU07QUFBQSxNQUNOLHFCQUFxQjtBQUFBLE1BQ3JCLGlCQUFpQjtBQUFBLE1BQ2pCLGtCQUFrQixDQUFDO0FBQUEsTUFDbkIsWUFBWTtBQUFBLE1BQ1osV0FBVyxDQUFDO0FBQUEsTUFDWixpQ0FBaUMsS0FBSztBQUNwQyxZQUFJLEtBQUssTUFBTSxJQUFJLFlBQVksRUFBRSxLQUFLLGVBQWUsTUFBTSxRQUFXO0FBQ3BFLGlCQUFPLEtBQUssTUFBTSxJQUFJLFlBQVksRUFBRSxLQUFLLGVBQWUsRUFBRSxpQkFBaUIsRUFBRSxHQUFHO0FBQUEsUUFDbEY7QUFBQSxNQUNGO0FBQUEsTUFDQSxtQkFBbUIsU0FBUztBQUMxQixZQUFJLEtBQUssaUNBQWlDLGVBQWUsTUFBTSxPQUFPO0FBQ3BFO0FBQUEsUUFDRjtBQUVBLFlBQUksUUFBUSxLQUFLLGlDQUFpQyx5QkFBeUIsTUFBTTtBQUNqRixhQUFLLFdBQVcsS0FBSztBQUFBLE1BQ3ZCO0FBQUEsTUFDQSxzQkFBc0IsU0FBUztBQUM3QixZQUFJLEtBQUssaUNBQWlDLGtCQUFrQixNQUFNLE9BQU87QUFDdkU7QUFBQSxRQUNGO0FBRUEsYUFBSyxXQUFXLElBQUk7QUFBQSxNQUN0QjtBQUFBLE1BQ0EsV0FBVyxRQUFRLE9BQU8scUJBQXFCLEdBQUcsaUJBQWlCLE9BQU87QUFDeEUsWUFBRyxLQUFLLFNBQVMsT0FBTztBQUN0QjtBQUFBLFFBQ0Y7QUFFQSxZQUFJLEtBQUssaUNBQWlDLG9CQUFvQixNQUFNLE1BQU07QUFDeEUsZ0JBQU0sZ0JBQWdCLEtBQUssTUFBTSxJQUFJLFlBQVksRUFBRSxLQUFLLGVBQWUsRUFBRTtBQUN6RSxtQkFBUyxTQUFTLGVBQWUsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUFBLFFBQzFEO0FBRUEsWUFBSSxLQUFLLGlDQUFpQyxnQkFBZ0IsTUFBTSxNQUFNO0FBQ3BFLG1CQUFTLFNBQVMsb0JBQW9CLEVBQUUsSUFBSSxLQUFLLGdCQUFnQixDQUFDO0FBQUEsUUFDcEU7QUFFQSxZQUFJLHFCQUFxQixHQUFHO0FBQzFCLG1CQUFTLElBQUksR0FBRyxJQUFJLG9CQUFvQixLQUFLO0FBQzNDLGdCQUFJLGdCQUFnQjtBQUNsQixvQkFBTUEsTUFBSyxLQUFLLGlCQUFpQixLQUFLLGlCQUFpQixTQUFTLENBQUM7QUFDakUsdUJBQVMsU0FBUyxvQkFBb0IsRUFBRSxJQUFJQSxJQUFHLENBQUM7QUFBQSxZQUNsRDtBQUNBLGlCQUFLLGlCQUFpQixJQUFJO0FBQUEsVUFDNUI7QUFBQSxRQUNGO0FBRUEsY0FBTSxLQUFLLEtBQUssaUJBQWlCLElBQUk7QUFFckMsWUFBSSxNQUFNLENBQUMsT0FBTztBQUNoQixjQUFJLElBQUk7QUFDTixpQkFBSyx3QkFBd0IsSUFBSSxJQUFJO0FBQUEsVUFDdkMsT0FBTztBQUNMLGlCQUFLLGtCQUFrQixLQUFLO0FBQUEsVUFDOUI7QUFBQSxRQUNGLE9BQU87QUFDTCxlQUFLLGtCQUFrQixLQUFLO0FBQUEsUUFDOUI7QUFBQSxNQUNGO0FBQUEsTUFDQSx3QkFBd0IsSUFBSSxPQUFPLE9BQU87QUFDeEMsYUFBSyxrQkFBa0IsSUFBSTtBQUUzQixZQUFJLEtBQUssb0JBQW9CLElBQUk7QUFDL0I7QUFBQSxRQUNGO0FBRUEsWUFBSSxLQUFLLG9CQUFvQixTQUFTLFNBQVMsT0FBTztBQUNwRCxlQUFLLGlCQUFpQixLQUFLLEtBQUssZUFBZTtBQUFBLFFBQ2pEO0FBRUEsWUFBSSxtQkFBbUI7QUFFdkIsWUFBSSxLQUFLLG9CQUFvQixPQUFPO0FBQ2xDLGVBQUssa0JBQWtCO0FBQ3ZCLGVBQUssc0JBQXNCO0FBQzNCLGVBQUssYUFBYSxLQUFLLGlDQUFpQyxlQUFlO0FBQUEsUUFDekUsT0FBTztBQUNMLGVBQUssc0JBQXNCO0FBRTNCLDZCQUFtQjtBQUVuQixxQkFBVyxNQUFNO0FBQ2YsaUJBQUssa0JBQWtCO0FBQ3ZCLGlCQUFLLHNCQUFzQjtBQUMzQixpQkFBSyxhQUFhLEtBQUssaUNBQWlDLGVBQWU7QUFBQSxVQUN6RSxHQUFHLEdBQUc7QUFBQSxRQUNSO0FBRUEsYUFBSyxVQUFVLE1BQU07QUFDbkIsY0FBSSxZQUFZLEtBQUssTUFBTSxFQUFFLEdBQUcsY0FBYyxhQUFhO0FBQzNELGNBQUksV0FBVztBQUNiLHVCQUFXLE1BQU07QUFDZix3QkFBVSxNQUFNO0FBQUEsWUFDbEIsR0FBRyxnQkFBZ0I7QUFBQSxVQUNyQjtBQUFBLFFBQ0YsQ0FBQztBQUFBLE1BQ0g7QUFBQSxNQUNBLGFBQWE7QUFDWCxZQUFJLFdBQVc7QUFFZixlQUFPLENBQUMsR0FBRyxLQUFLLElBQUksaUJBQWlCLFFBQVEsQ0FBQyxFQUMzQyxPQUFPLFFBQU0sQ0FBQyxHQUFHLGFBQWEsVUFBVSxDQUFDO0FBQUEsTUFDOUM7QUFBQSxNQUNBLGlCQUFpQjtBQUNmLGVBQU8sS0FBSyxXQUFXLEVBQUUsQ0FBQztBQUFBLE1BQzVCO0FBQUEsTUFDQSxnQkFBZ0I7QUFDZCxlQUFPLEtBQUssV0FBVyxFQUFFLE1BQU0sRUFBRSxFQUFFLENBQUM7QUFBQSxNQUN0QztBQUFBLE1BQ0EsZ0JBQWdCO0FBQ2QsZUFBTyxLQUFLLFdBQVcsRUFBRSxLQUFLLG1CQUFtQixDQUFDLEtBQUssS0FBSyxlQUFlO0FBQUEsTUFDN0U7QUFBQSxNQUNBLGdCQUFnQjtBQUNkLGVBQU8sS0FBSyxXQUFXLEVBQUUsS0FBSyxtQkFBbUIsQ0FBQyxLQUFLLEtBQUssY0FBYztBQUFBLE1BQzVFO0FBQUEsTUFDQSxxQkFBcUI7QUFDbkIsZ0JBQVEsS0FBSyxXQUFXLEVBQUUsUUFBUSxTQUFTLGFBQWEsSUFBSSxNQUFNLEtBQUssV0FBVyxFQUFFLFNBQVM7QUFBQSxNQUMvRjtBQUFBLE1BQ0EscUJBQXFCO0FBQ25CLGVBQU8sS0FBSyxJQUFJLEdBQUcsS0FBSyxXQUFXLEVBQUUsUUFBUSxTQUFTLGFBQWEsQ0FBQyxJQUFJO0FBQUEsTUFDMUU7QUFBQSxNQUNBLGtCQUFrQixNQUFNO0FBQ3RCLGFBQUssT0FBTztBQUVaLFlBQUksTUFBTTtBQUNSLG1CQUFTLEtBQUssVUFBVSxJQUFJLG1CQUFtQjtBQUFBLFFBQ2pELE9BQU87QUFDTCxtQkFBUyxLQUFLLFVBQVUsT0FBTyxtQkFBbUI7QUFFbEQscUJBQVcsTUFBTTtBQUNmLGlCQUFLLGtCQUFrQjtBQUN2QixpQkFBSyxNQUFNLFdBQVc7QUFBQSxVQUN4QixHQUFHLEdBQUc7QUFBQSxRQUNSO0FBQUEsTUFDRjtBQUFBLE1BQ0EsT0FBTztBQUNMLGFBQUssYUFBYSxLQUFLLGlDQUFpQyxlQUFlO0FBRXZFLGFBQUssVUFBVTtBQUFBLFVBQ2IsU0FBUyxHQUFHLGNBQWMsQ0FBQyxTQUFTO0FBQ2xDLGlCQUFLLFdBQVcsTUFBTSxTQUFTLE9BQU8sTUFBTSxzQkFBc0IsR0FBRyxNQUFNLGtCQUFrQixLQUFLO0FBQUEsVUFDcEcsQ0FBQztBQUFBLFFBQ0g7QUFFQSxhQUFLLFVBQVU7QUFBQSxVQUNiLFNBQVMsR0FBRywrQkFBK0IsQ0FBQyxFQUFFLEdBQUcsTUFBTTtBQUNyRCxpQkFBSyx3QkFBd0IsRUFBRTtBQUFBLFVBQ2pDLENBQUM7QUFBQSxRQUNIO0FBQUEsTUFDRjtBQUFBLE1BQ0EsVUFBVTtBQUNSLGFBQUssVUFBVSxRQUFRLENBQUMsYUFBYTtBQUNuQyxtQkFBUztBQUFBLFFBQ1gsQ0FBQztBQUFBLE1BQ0g7QUFBQSxJQUNGO0FBQUEsRUFDRjtBQUVBLE1BQU8sZ0JBQVE7OztBQ3pHZixTQUFPLGlCQUFpQjtBQUV4QixXQUFTLGlCQUFpQixlQUFlLE1BQU07QUFDN0MsVUFBTSxRQUFRLGFBQWEsUUFBUSxPQUFPLEtBQUs7QUFFL0MsV0FBTyxPQUFPO0FBQUEsTUFDWjtBQUFBLE1BQ0EsVUFBVSxVQUNULFVBQVUsWUFDVCxPQUFPLFdBQVcsOEJBQThCLEVBQUUsVUFDaEQsU0FDQTtBQUFBLElBQ047QUFFQSxXQUFPLGlCQUFpQixpQkFBaUIsQ0FBQyxVQUFVO0FBQ2xELFVBQUlDLFNBQVEsTUFBTTtBQUVsQixtQkFBYSxRQUFRLFNBQVNBLE1BQUs7QUFFbkMsVUFBSUEsV0FBVSxVQUFVO0FBQ3RCLFFBQUFBLFNBQVEsT0FBTyxXQUFXLDhCQUE4QixFQUFFLFVBQ3RELFNBQ0E7QUFBQSxNQUNOO0FBRUEsYUFBTyxPQUFPLE1BQU0sU0FBU0EsTUFBSztBQUFBLElBQ3BDLENBQUM7QUFFRCxXQUNHLFdBQVcsOEJBQThCLEVBQ3pDLGlCQUFpQixVQUFVLENBQUMsVUFBVTtBQUNyQyxVQUFJLGFBQWEsUUFBUSxPQUFPLE1BQU0sVUFBVTtBQUM5QyxlQUFPLE9BQU8sTUFBTSxTQUFTLE1BQU0sVUFBVSxTQUFTLE9BQU87QUFBQSxNQUMvRDtBQUFBLElBQ0YsQ0FBQztBQUVILFdBQU8sT0FBTyxPQUFPLE1BQU07QUFDekIsWUFBTUEsU0FBUSxPQUFPLE9BQU8sTUFBTSxPQUFPO0FBRXpDLE1BQUFBLFdBQVUsU0FDTixTQUFTLGdCQUFnQixVQUFVLElBQUksTUFBTSxJQUM3QyxTQUFTLGdCQUFnQixVQUFVLE9BQU8sTUFBTTtBQUFBLElBQ3RELENBQUM7QUFBQSxFQUNILENBQUM7IiwKICAibmFtZXMiOiBbImlkIiwgInRoZW1lIl0KfQo= diff --git a/packages/admin/resources/css/components/filament.css b/packages/admin/resources/css/components/filament.css new file mode 100644 index 000000000..fb2a03af7 --- /dev/null +++ b/packages/admin/resources/css/components/filament.css @@ -0,0 +1,16 @@ +.fi-modal-close-overlay { + @apply backdrop-blur-sm; +} + +@keyframes shaking { + 0% { transform: translateX(0) } + 25% { transform: translateX(5px) } + 50% { transform: translateX(-5px) } + 75% { transform: translateX(5px) } + 100% { transform: translateX(0) } +} + +.horizontal-shake { + animation: shaking 0.35s normal; + animation-iteration-count: 1 +} diff --git a/packages/admin/resources/css/shopper.css b/packages/admin/resources/css/shopper.css index 178f807cd..5f487114c 100755 --- a/packages/admin/resources/css/shopper.css +++ b/packages/admin/resources/css/shopper.css @@ -4,6 +4,7 @@ @import 'base.css'; @import 'components/sidebar.css'; +@import 'components/filament.css'; @import 'components/intl-phone.css'; @layer base { diff --git a/packages/admin/resources/js/plugins/internationalNumber.js b/packages/admin/resources/js/components/internationalNumber.js similarity index 100% rename from packages/admin/resources/js/plugins/internationalNumber.js rename to packages/admin/resources/js/components/internationalNumber.js diff --git a/packages/admin/resources/js/components/panel.js b/packages/admin/resources/js/components/panel.js new file mode 100644 index 000000000..3d24f39ae --- /dev/null +++ b/packages/admin/resources/js/components/panel.js @@ -0,0 +1,164 @@ +const SlideOverPanel = () => { + return { + open: false, + showActiveComponent: true, + activeComponent: false, + componentHistory: [], + panelWidth: null, + listeners: [], + getActiveComponentPanelAttribute(key) { + if (this.$wire.get('components')[this.activeComponent] !== undefined) { + return this.$wire.get('components')[this.activeComponent]['panelAttributes'][key]; + } + }, + closePanelOnEscape(trigger) { + if (this.getActiveComponentPanelAttribute('closeOnEscape') === false) { + return; + } + + let force = this.getActiveComponentPanelAttribute('closeOnEscapeIsForceful') === true; + this.closePanel(force); + }, + closePanelOnClickAway(trigger) { + if (this.getActiveComponentPanelAttribute('closeOnClickAway') === false) { + return; + } + + this.closePanel(true); + }, + closePanel(force = false, skipPreviousPanels = 0, destroySkipped = false) { + if(this.show === false) { + return; + } + + if (this.getActiveComponentPanelAttribute('dispatchCloseEvent') === true) { + const componentName = this.$wire.get('components')[this.activeComponent].name; + Livewire.dispatch('panelClosed', { name: componentName }); + } + + if (this.getActiveComponentPanelAttribute('destroyOnClose') === true) { + Livewire.dispatch('destroyComponent', { id: this.activeComponent }); + } + + if (skipPreviousPanels > 0) { + for (let i = 0; i < skipPreviousPanels; i++) { + if (destroySkipped) { + const id = this.componentHistory[this.componentHistory.length - 1]; + Livewire.dispatch('destroyComponent', { id: id }); + } + this.componentHistory.pop(); + } + } + + const id = this.componentHistory.pop(); + + if (id && !force) { + if (id) { + this.setActivePanelComponent(id, true); + } else { + this.setShowPropertyTo(false); + } + } else { + this.setShowPropertyTo(false); + } + }, + setActivePanelComponent(id, skip = false) { + this.setShowPropertyTo(true); + + if (this.activeComponent === id) { + return; + } + + if (this.activeComponent !== false && skip === false) { + this.componentHistory.push(this.activeComponent); + } + + let focusableTimeout = 50; + + if (this.activeComponent === false) { + this.activeComponent = id + this.showActiveComponent = true; + this.panelWidth = this.getActiveComponentPanelAttribute('maxWidthClass'); + } else { + this.showActiveComponent = false; + + focusableTimeout = 400; + + setTimeout(() => { + this.activeComponent = id; + this.showActiveComponent = true; + this.panelWidth = this.getActiveComponentPanelAttribute('maxWidthClass'); + }, 300); + } + + this.$nextTick(() => { + let focusable = this.$refs[id]?.querySelector('[autofocus]'); + if (focusable) { + setTimeout(() => { + focusable.focus(); + }, focusableTimeout); + } + }); + }, + focusables() { + let selector = 'a, button, input:not([type=\'hidden\'], textarea, select, details, [tabindex]:not([tabindex=\'-1\'])' + + return [...this.$el.querySelectorAll(selector)] + .filter(el => !el.hasAttribute('disabled')) + }, + firstFocusable() { + return this.focusables()[0] + }, + lastFocusable() { + return this.focusables().slice(-1)[0] + }, + nextFocusable() { + return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() + }, + prevFocusable() { + return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() + }, + nextFocusableIndex() { + return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) + }, + prevFocusableIndex() { + return Math.max(0, this.focusables().indexOf(document.activeElement)) - 1 + }, + setShowPropertyTo(open) { + this.open = open; + + if (open) { + document.body.classList.add('overflow-y-hidden'); + } else { + document.body.classList.remove('overflow-y-hidden'); + + setTimeout(() => { + this.activeComponent = false; + this.$wire.resetState(); + }, 300); + } + }, + init() { + this.panelWidth = this.getActiveComponentPanelAttribute('maxWidthClass'); + + this.listeners.push( + Livewire.on('closePanel', (data) => { + this.closePanel(data?.force ?? false, data?.skipPreviousPanels ?? 0, data?.destroySkipped ?? false); + }) + ); + + this.listeners.push( + Livewire.on('activePanelComponentChanged', ({ id }) => { + this.setActivePanelComponent(id); + }) + ); + }, + destroy() { + this.listeners.forEach((listener) => { + listener(); + }); + } + } +} + +export default SlideOverPanel diff --git a/packages/admin/resources/js/helpers/shortcut.js b/packages/admin/resources/js/helpers/shortcut.js deleted file mode 100755 index 9a5c966b0..000000000 --- a/packages/admin/resources/js/helpers/shortcut.js +++ /dev/null @@ -1,230 +0,0 @@ -/** - * http://www.openjs.com/scripts/events/keyboard_shortcuts/ - * Version : 2.01.B - * By Binny V A - * License : BSD - */ -shortcut = { - all_shortcuts: {}, //All the shortcuts are stored in this array - add: function (shortcut_combination, callback, opt) { - //Provide a set of default options - let default_options = { - type: 'keydown', - propagate: false, - disable_in_input: false, - target: document, - keycode: false, - } - - if (!opt) opt = default_options - else { - for (let dfo in default_options) { - if (typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo] - } - } - - let ele = opt.target - if (typeof opt.target == 'string') ele = document.getElementById(opt.target) - let ths = this - shortcut_combination = shortcut_combination.toLowerCase() - - //The function to be called at keypress - const func = function (e) { - e = e || window.event - - if (opt['disable_in_input']) { - //Don't enable shortcut keys in Input, Textarea fields - let element - if (e.target) element = e.target - else if (e.srcElement) element = e.srcElement - if (element.nodeType === 3) element = element.parentNode - - if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') - return - } - - //Find Which key is pressed - if (e.keyCode) code = e.keyCode - else if (e.which) code = e.which - let character = String.fromCharCode(code).toLowerCase() - - if (code === 188) character = ',' //If the user presses , when the type is onkeydown - if (code === 190) character = '.' //If the user presses , when the type is onkeydown - - const keys = shortcut_combination.split('+') - //Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked - let kp = 0 - - //Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken - const shift_nums = { - '`': '~', - 1: '!', - 2: '@', - 3: '#', - 4: '$', - 5: '%', - 6: '^', - 7: '&', - 8: '*', - 9: '(', - 0: ')', - '-': '_', - '=': '+', - ';': ':', - "'": '"', - ',': '<', - '.': '>', - '/': '?', - '\\': '|', - } - //Special Keys - and their codes - const special_keys = { - esc: 27, - escape: 27, - tab: 9, - space: 32, - return: 13, - enter: 13, - backspace: 8, - - scrolllock: 145, - scroll_lock: 145, - scroll: 145, - capslock: 20, - caps_lock: 20, - caps: 20, - numlock: 144, - num_lock: 144, - num: 144, - - pause: 19, - break: 19, - - insert: 45, - home: 36, - delete: 46, - end: 35, - - pageup: 33, - page_up: 33, - pu: 33, - - pagedown: 34, - page_down: 34, - pd: 34, - - left: 37, - up: 38, - right: 39, - down: 40, - - f1: 112, - f2: 113, - f3: 114, - f4: 115, - f5: 116, - f6: 117, - f7: 118, - f8: 119, - f9: 120, - f10: 121, - f11: 122, - f12: 123, - } - - const modifiers = { - shift: { wanted: false, pressed: false }, - ctrl: { wanted: false, pressed: false }, - alt: { wanted: false, pressed: false }, - meta: { wanted: false, pressed: false }, //Meta is Mac specific - } - - if (e.ctrlKey) modifiers.ctrl.pressed = true - if (e.shiftKey) modifiers.shift.pressed = true - if (e.altKey) modifiers.alt.pressed = true - if (e.metaKey) modifiers.meta.pressed = true - - let k - for (let i = 0; (k = keys[i]), i < keys.length; i++) { - //Modifiers - if (k === 'ctrl' || k === 'control') { - kp++ - modifiers.ctrl.wanted = true - } else if (k === 'shift') { - kp++ - modifiers.shift.wanted = true - } else if (k === 'alt') { - kp++ - modifiers.alt.wanted = true - } else if (k === 'meta') { - kp++ - modifiers.meta.wanted = true - } else if (k.length > 1) { - //If it is a special key - if (special_keys[k] === code) kp++ - } else if (opt['keycode']) { - if (opt['keycode'] === code) kp++ - } else { - //The special keys did not match - if (character === k) kp++ - else { - if (shift_nums[character] && e.shiftKey) { - //Stupid Shift key bug created by using lowercase - character = shift_nums[character] - if (character === k) kp++ - } - } - } - } - - if ( - kp === keys.length && - modifiers.ctrl.pressed === modifiers.ctrl.wanted && - modifiers.shift.pressed === modifiers.shift.wanted && - modifiers.alt.pressed === modifiers.alt.wanted && - modifiers.meta.pressed === modifiers.meta.wanted - ) { - callback(e) - - if (!opt['propagate']) { - //Stop the event - //e.cancelBubble is supported by IE - this will kill the bubbling process. - e.cancelBubble = true - e.returnValue = false - - //e.stopPropagation works in Firefox. - if (e.stopPropagation) { - e.stopPropagation() - e.preventDefault() - } - return false - } - } - } - this.all_shortcuts[shortcut_combination] = { - callback: func, - target: ele, - event: opt['type'], - } - //Attach the function with the event - if (ele.addEventListener) ele.addEventListener(opt['type'], func, false) - else if (ele.attachEvent) ele.attachEvent('on' + opt['type'], func) - else ele['on' + opt['type']] = func - }, - - //Remove the shortcut - just specify the shortcut and I will remove the binding - remove: function (shortcut_combination) { - shortcut_combination = shortcut_combination.toLowerCase() - const binding = this.all_shortcuts[shortcut_combination] - delete this.all_shortcuts[shortcut_combination] - if (!binding) return - const type = binding['event'] - const ele = binding['target'] - const callback = binding['callback'] - - if (ele.detachEvent) ele.detachEvent('on' + type, callback) - else if (ele.removeEventListener) - ele.removeEventListener(type, callback, false) - else ele['on' + type] = false - }, -} diff --git a/packages/admin/resources/js/helpers/trix.js b/packages/admin/resources/js/helpers/trix.js deleted file mode 100755 index 798f36352..000000000 --- a/packages/admin/resources/js/helpers/trix.js +++ /dev/null @@ -1,46 +0,0 @@ -import Trix from 'trix' - -Trix.config.blockAttributes.heading = { - tagName: 'h2', - terminal: true, - breakOnReturn: true, - group: false, -} - -Trix.config.blockAttributes.subHeading = { - tagName: 'h3', - terminal: true, - breakOnReturn: true, - group: false, -} - -Trix.config.textAttributes.underline = { - style: { textDecoration: 'underline' }, - inheritable: true, - parser: (element) => { - const style = window.getComputedStyle(element) - - return style.textDecoration.includes('underline') - }, -} - -Trix.Block.prototype.breaksOnReturn = function () { - const lastAttribute = this.getLastAttribute() - const blockConfig = Trix.getBlockConfig( - lastAttribute ? lastAttribute : 'default', - ) - - return blockConfig?.breakOnReturn ?? false -} - -Trix.LineBreakInsertion.prototype.shouldInsertBlockBreak = function () { - if ( - this.block.hasAttributes() && - this.block.isListItem() && - !this.block.isEmpty() - ) { - return this.startLocation.offset > 0 - } else { - return !this.shouldBreakFormattedBlock() ? this.breaksOnReturn : false - } -} diff --git a/packages/admin/resources/js/helpers/window.js b/packages/admin/resources/js/helpers/window.js deleted file mode 100755 index c83eea17d..000000000 --- a/packages/admin/resources/js/helpers/window.js +++ /dev/null @@ -1,16 +0,0 @@ -import Choices from 'choices.js' -import TomSelect from 'tom-select' - -window.choices = (element) => { - return new Choices(element, { removeItemButton: true }) -} - -window.tomSelect = (element, options = {}) => { - return new TomSelect(element, options) -} - -window.scrollToPosition = (selector) => - document.querySelector(selector).scrollIntoView({ - behavior: 'smooth', - block: 'end', - }) diff --git a/packages/admin/resources/js/index.js b/packages/admin/resources/js/index.js index b51a1b56d..66cde8f11 100755 --- a/packages/admin/resources/js/index.js +++ b/packages/admin/resources/js/index.js @@ -54,3 +54,49 @@ window.Alpine.effect(() => { Livewire.start() */ +import SlideOverPanel from './components/panel' + +window.SlideOverPanel = SlideOverPanel + +document.addEventListener('alpine:init', () => { + const theme = localStorage.getItem('theme') ?? 'light' + + window.Alpine.store( + 'theme', + theme === 'dark' || + (theme === 'system' && + window.matchMedia('(prefers-color-scheme: dark)').matches) + ? 'dark' + : 'light', + ) + + window.addEventListener('theme-changed', (event) => { + let theme = event.detail + + localStorage.setItem('theme', theme) + + if (theme === 'system') { + theme = window.matchMedia('(prefers-color-scheme: dark)').matches + ? 'dark' + : 'light' + } + + window.Alpine.store('theme', theme) + }) + + window + .matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', (event) => { + if (localStorage.getItem('theme') === 'system') { + window.Alpine.store('theme', event.matches ? 'dark' : 'light') + } + }) + + window.Alpine.effect(() => { + const theme = window.Alpine.store('theme') + + theme === 'dark' + ? document.documentElement.classList.add('dark') + : document.documentElement.classList.remove('dark') + }) +}) diff --git a/packages/admin/resources/js/plugins/keyPress.js b/packages/admin/resources/js/plugins/keyPress.js deleted file mode 100755 index b59908c01..000000000 --- a/packages/admin/resources/js/plugins/keyPress.js +++ /dev/null @@ -1,14 +0,0 @@ -import '../helpers/shortcut' - -export default function (Alpine) { - Alpine.directive('keypress', (el) => { - // Display the list of shortcuts - shortcut.add('Shift+F1', function () { - window.dispatchEvent(new CustomEvent('shortcut-list', { bubbles: true })) - }) - // Move to the documentation - shortcut.add('Alt+D', function () { - window.open('https://laravelshopper.dev', '_blank') - }) - }) -} diff --git a/packages/admin/resources/lang/en/layout.php b/packages/admin/resources/lang/en/layout.php index 8e4b60730..b6d9b1223 100755 --- a/packages/admin/resources/lang/en/layout.php +++ b/packages/admin/resources/lang/en/layout.php @@ -190,6 +190,7 @@ 'apply' => 'Apply', 'next' => 'Next', 'back' => 'Back', + 'theme_switcher' => 'Enable :label theme', ], 'error' => 'Your submission contains errors. Please try again', diff --git a/packages/admin/resources/lang/en/notifications.php b/packages/admin/resources/lang/en/notifications.php index 921c305f4..a12cc306b 100755 --- a/packages/admin/resources/lang/en/notifications.php +++ b/packages/admin/resources/lang/en/notifications.php @@ -5,6 +5,7 @@ return [ 'actions' => [ + 'save' => ':item successfully save', 'create' => ':item successfully added', 'update' => ':item successfully updated', 'remove' => ':item successfully removed', diff --git a/packages/admin/resources/lang/fr/layout.php b/packages/admin/resources/lang/fr/layout.php index 79a26b514..0c27ed007 100755 --- a/packages/admin/resources/lang/fr/layout.php +++ b/packages/admin/resources/lang/fr/layout.php @@ -190,6 +190,7 @@ 'apply' => 'Appliquer', 'next' => 'Suivant', 'back' => 'Retour', + 'theme_switcher' => 'Activer :label theme', ], 'error' => 'Votre soumission contient des erreurs. Veuillez réessayer', diff --git a/packages/admin/resources/lang/fr/notifications.php b/packages/admin/resources/lang/fr/notifications.php index 50311723f..ddbbcdb2f 100755 --- a/packages/admin/resources/lang/fr/notifications.php +++ b/packages/admin/resources/lang/fr/notifications.php @@ -5,6 +5,7 @@ return [ 'actions' => [ + 'save' => ':item enregistré avec succès', 'create' => ':item ajouté avec succès', 'update' => ':item mis à jour avec succès', 'remove' => ':item supprimé(e) avec succès', diff --git a/packages/admin/resources/lang/fr/words.php b/packages/admin/resources/lang/fr/words.php index c781edf13..bbff97884 100755 --- a/packages/admin/resources/lang/fr/words.php +++ b/packages/admin/resources/lang/fr/words.php @@ -77,7 +77,7 @@ 'is_enabled' => 'Est activé', 'selected_tab' => 'Onglet sélectionné', - 'set_visibility' => 'Rendre visible la\le :name pour les clients.', + 'set_visibility' => 'Rendre visible :name pour les clients.', 'set_global_visibility' => 'Visibilité de la page pour les clients.', 'log_out' => 'Se déconnecter', 'browser_platform' => ':browser sur :platform', diff --git a/packages/admin/resources/views/components/empty-state.blade.php b/packages/admin/resources/views/components/empty-state.blade.php index 1e25ed383..19c840a14 100755 --- a/packages/admin/resources/views/components/empty-state.blade.php +++ b/packages/admin/resources/views/components/empty-state.blade.php @@ -4,9 +4,10 @@ 'button' => false, 'permission' => false, 'url' => false, + 'panel' => null, ]) -
twMerge(['class' => 'relative w-full lg:flex lg:items-center py-12 lg:py-16']) }}> +
twMerge(['class' => 'relative w-full lg:flex lg:items-center py-10 lg:py-12']) }}>
{{ $slot }}
@@ -22,10 +23,14 @@ @if($permission) @can($permission) - @if($button && $url) + @if($url) {{ $button }} + @elseif($panel) + + {{ $button }} + @endif @endcan @endif diff --git a/packages/admin/resources/views/components/filament/section.blade.php b/packages/admin/resources/views/components/filament/section.blade.php index 3bf24ac2d..9370904e0 100644 --- a/packages/admin/resources/views/components/filament/section.blade.php +++ b/packages/admin/resources/views/components/filament/section.blade.php @@ -58,10 +58,10 @@ }" @if ($collapsible) x-on:collapse-section.window="if ($event.detail.id == $el.id) isCollapsed = true" - x-on:expand="isCollapsed = false" - x-on:open-section.window="if ($event.detail.id == $el.id) isCollapsed = false" - x-on:toggle-section.window="if ($event.detail.id == $el.id) isCollapsed = ! isCollapsed" - x-bind:class="isCollapsed && 'fi-collapsed'" + x-on:expand="isCollapsed = false" + x-on:open-section.window="if ($event.detail.id == $el.id) isCollapsed = false" + x-on:toggle-section.window="if ($event.detail.id == $el.id) isCollapsed = ! isCollapsed" + x-bind:class="isCollapsed && 'fi-collapsed'" @endif {{ $attributes->class([ @@ -80,7 +80,7 @@ @endif @class([ 'fi-section-header flex flex-col gap-3', - 'cursor-pointer' => $collapsible, + 'cursor-pointer px-4 py-2 bg-gray-100/75 rounded-lg dark:bg-white/5' => $collapsible, match ($compact) { true => 'p-0', false => 'py-4', @@ -176,9 +176,10 @@ @endif @class([ 'fi-section-content-ctn', - 'border-t border-gray-200 dark:border-white/10' => $hasHeader && (! $aside), + 'border-t border-gray-200 dark:border-white/10' => $hasHeader && (! $aside) && (! $collapsible), 'md:col-span-2 lg:max-w-3xl' => $aside, 'md:order-first' => $contentBefore, + 'pt-4 lg:pt-6 px-2' => $collapsible, ]) >
-
+
diff --git a/packages/admin/resources/views/components/layouts/base.blade.php b/packages/admin/resources/views/components/layouts/base.blade.php index 7e950b696..0e0aec453 100755 --- a/packages/admin/resources/views/components/layouts/base.blade.php +++ b/packages/admin/resources/views/components/layouts/base.blade.php @@ -1,6 +1,6 @@ @props(['title' => config('app.name')]) - + @if ($favicon = config('shopper.admin.favicon')) - + @else - + @endif {{ $title }} // {{ __('shopper::layout.meta_title') }} @@ -27,7 +27,8 @@ class="shopper scroll-smooth js-focus-visible min-h-screen antialiased" - + @stack('styles') @@ -49,18 +50,18 @@ class="shopper scroll-smooth js-focus-visible min-h-screen antialiased" twMerge(['class' => 'bg-white font-sans dark:bg-gray-950']) }}> - {{ $slot }} +{{ $slot }} - @livewire(\Filament\Notifications\Livewire\Notifications::class) - @livewire(\Shopper\Livewire\Components\SidePanel::class) +@livewire(\Filament\Notifications\Livewire\Notifications::class) +@livewire(\Shopper\Livewire\Components\SlideOverPanel::class) -
- @livewire(\LivewireUI\Modal\Modal::class) -
+
+ @livewire(\LivewireUI\Modal\Modal::class) +
- @filamentScripts +@filamentScripts - @include('shopper::includes._additional-scripts') +@include('shopper::includes._additional-scripts') diff --git a/packages/admin/resources/views/components/theme-switcher/button.blade.php b/packages/admin/resources/views/components/theme-switcher/button.blade.php new file mode 100644 index 000000000..7198648e3 --- /dev/null +++ b/packages/admin/resources/views/components/theme-switcher/button.blade.php @@ -0,0 +1,30 @@ +@props([ + 'icon', + 'theme', +]) + +@php + $label = __('shopper::layout.forms.actions.theme_switcher', ['label' => $theme]); +@endphp + + diff --git a/packages/admin/resources/views/components/theme-switcher/index.blade.php b/packages/admin/resources/views/components/theme-switcher/index.blade.php new file mode 100644 index 000000000..f2788413c --- /dev/null +++ b/packages/admin/resources/views/components/theme-switcher/index.blade.php @@ -0,0 +1,26 @@ +
+ + + + + +
diff --git a/packages/admin/resources/views/livewire/components/account/dropdown.blade.php b/packages/admin/resources/views/livewire/components/account/dropdown.blade.php index 976de939f..fc87b9963 100755 --- a/packages/admin/resources/views/livewire/components/account/dropdown.blade.php +++ b/packages/admin/resources/views/livewire/components/account/dropdown.blade.php @@ -20,7 +20,7 @@ class="relative inline-flex items-center w-full text-sm leading-5 rounded-full f x-transition:leave-end="transform opacity-0 scale-95" @click.outside="dropdownOpen = false" x-cloak - class="absolute z-50 top-10 right-0 w-56 origin-top-right rounded-lg backdrop-blur-sm bg-white/60 shadow-lg ring-1 ring-gray-200 dark:bg-gray-900/80 dark:ring-gray-800" + class="absolute z-50 top-10 right-2.5 w-56 origin-top-right rounded-lg bg-white shadow-lg ring-1 ring-gray-200 dark:bg-gray-900 dark:ring-white/5" x-ref="items" role="menu" aria-orientation="vertical" @@ -46,72 +46,8 @@ class="absolute z-50 top-10 right-0 w-56 origin-top-right rounded-lg backdrop-bl @endcan
-
-
- - {{ __('shopper::words.dark_mode') }} -
+
+
-
- -
- - - -
-
- -
- https:// -
- -
-
-
-
-
- - - - -
-
- -

- {{ __('shopper::words.set_visibility', ['name' => strtolower(__('shopper::words.brand'))]) }} -

-
-
-
-
- - - -
-
- - -
-
- -
-
diff --git a/packages/admin/resources/views/livewire/components/brands/edit.blade.php b/packages/admin/resources/views/livewire/components/brands/edit.blade.php deleted file mode 100755 index 2a08aed19..000000000 --- a/packages/admin/resources/views/livewire/components/brands/edit.blade.php +++ /dev/null @@ -1,25 +0,0 @@ - -
- - - - - - - - {{ $name }} - - - - @include('shopper::livewire.components.brands._form') - -
-
- - - {{ __('shopper::layout.forms.actions.update') }} - -
-
-
-
diff --git a/packages/admin/resources/views/livewire/components/side-panel.blade.php b/packages/admin/resources/views/livewire/components/side-panel.blade.php deleted file mode 100644 index 22faf618c..000000000 --- a/packages/admin/resources/views/livewire/components/side-panel.blade.php +++ /dev/null @@ -1,71 +0,0 @@ -
- -
- -
-
-
-
-
-
-
-

- {{ $title }} -

-
- -
-
-
-
- @if ($component) - @livewire($component) - @else -
- -
- @endif -
-
-
-
-
-
-
diff --git a/packages/admin/resources/views/livewire/components/slide-over-panel.blade.php b/packages/admin/resources/views/livewire/components/slide-over-panel.blade.php new file mode 100644 index 000000000..2e70e9a8c --- /dev/null +++ b/packages/admin/resources/views/livewire/components/slide-over-panel.blade.php @@ -0,0 +1,55 @@ +
+ +
+ +
+
+
+
+
+ @forelse($components as $id => $component) +
+ @livewire($component['name'], $component['arguments'], key($id)) +
+ @empty + @endforelse +
+
+
+
+
+
diff --git a/packages/admin/resources/views/livewire/modals/create-role.blade.php b/packages/admin/resources/views/livewire/modals/create-role.blade.php index 10d015ad4..8ffcc441c 100755 --- a/packages/admin/resources/views/livewire/modals/create-role.blade.php +++ b/packages/admin/resources/views/livewire/modals/create-role.blade.php @@ -32,7 +32,7 @@ - + {{ __('shopper::layout.forms.actions.cancel') }} diff --git a/packages/admin/resources/views/livewire/modals/delete-role.blade.php b/packages/admin/resources/views/livewire/modals/delete-role.blade.php index 97492acd3..78595002f 100755 --- a/packages/admin/resources/views/livewire/modals/delete-role.blade.php +++ b/packages/admin/resources/views/livewire/modals/delete-role.blade.php @@ -18,16 +18,12 @@ - - - - {{ __('shopper::layout.forms.actions.confirm') }} - - - - - {{ __('shopper::layout.forms.actions.cancel') }} - - + + + {{ __('shopper::layout.forms.actions.confirm') }} + + + {{ __('shopper::layout.forms.actions.cancel') }} + diff --git a/packages/admin/resources/views/livewire/components/brands/create.blade.php b/packages/admin/resources/views/livewire/pages/brand/create.blade.php similarity index 55% rename from packages/admin/resources/views/livewire/components/brands/create.blade.php rename to packages/admin/resources/views/livewire/pages/brand/create.blade.php index d035fb4fb..53ee904fe 100755 --- a/packages/admin/resources/views/livewire/components/brands/create.blade.php +++ b/packages/admin/resources/views/livewire/pages/brand/create.blade.php @@ -10,16 +10,5 @@ {{ __('shopper::words.actions_label.add_new', ['name' => strtolower(__('shopper::words.brand'))]) }} - - @include('shopper::livewire.components.brands._form') - -
-
- - - {{ __('shopper::layout.forms.actions.save') }} - -
-
diff --git a/packages/admin/resources/views/livewire/components/brands/browse.blade.php b/packages/admin/resources/views/livewire/pages/brand/index.blade.php similarity index 97% rename from packages/admin/resources/views/livewire/components/brands/browse.blade.php rename to packages/admin/resources/views/livewire/pages/brand/index.blade.php index d658f70d1..fb56e733a 100755 --- a/packages/admin/resources/views/livewire/components/brands/browse.blade.php +++ b/packages/admin/resources/views/livewire/pages/brand/index.blade.php @@ -9,7 +9,10 @@ @can('add_brands')
- + {{ __('shopper::words.actions_label.add_new', ['name' => strtolower(__('shopper::layout.forms.label.brand'))]) }} @@ -24,8 +27,8 @@ :title="__('shopper::pages/brands.title')" :content="__('shopper::pages/brands.content')" :button="__('shopper::words.actions_label.add_new', ['name' => __('shopper::words.brand')])" + panel="{ component: 'shopper-slide-overs.brand-form' }" permission="add_brands" - :url="route('shopper.brands.create')" >
@@ -131,7 +134,7 @@ @else
- + {{ $this->table }}
@endif diff --git a/packages/admin/resources/views/livewire/pages/settings/team/index.blade.php b/packages/admin/resources/views/livewire/pages/settings/team/index.blade.php index 1a9c2ba37..02e8068cc 100755 --- a/packages/admin/resources/views/livewire/pages/settings/team/index.blade.php +++ b/packages/admin/resources/views/livewire/pages/settings/team/index.blade.php @@ -71,7 +71,6 @@ class="group flex flex-col justify-between p-4 rounded-lg overflow-hidden ring-1
diff --git a/packages/admin/resources/views/livewire/slide-overs/brand-form.blade.php b/packages/admin/resources/views/livewire/slide-overs/brand-form.blade.php new file mode 100755 index 000000000..4b3dbc97d --- /dev/null +++ b/packages/admin/resources/views/livewire/slide-overs/brand-form.blade.php @@ -0,0 +1,37 @@ +
+ +
+ + {{ __('shopper::layout.forms.actions.cancel') }} + + + + {{ __('shopper::layout.forms.actions.save') }} + +
+
diff --git a/packages/admin/resources/views/livewire/slide-overs/create-team-member.blade.php b/packages/admin/resources/views/livewire/slide-overs/create-team-member.blade.php index 5c1156eb6..c48dbb42f 100755 --- a/packages/admin/resources/views/livewire/slide-overs/create-team-member.blade.php +++ b/packages/admin/resources/views/livewire/slide-overs/create-team-member.blade.php @@ -1,30 +1,48 @@ -
-
- {{ $this->form }} - -
-
-
-
-
- - - {{ __('shopper::layout.forms.actions.save') }} - -
+
+ + {{ __('shopper::layout.forms.actions.cancel') }} + + + + {{ __('shopper::layout.forms.actions.save') }} +
diff --git a/packages/admin/resources/views/livewire/tables/cells/brands/site.blade.php b/packages/admin/resources/views/livewire/tables/cells/brands/site.blade.php index d46faa8ad..ff862d3ef 100755 --- a/packages/admin/resources/views/livewire/tables/cells/brands/site.blade.php +++ b/packages/admin/resources/views/livewire/tables/cells/brands/site.blade.php @@ -1,6 +1,6 @@ -@if($value) - - {{ $value }} +@if($state) + + {{ $state }} @else diff --git a/packages/admin/resources/views/pages/brands/create.blade.php b/packages/admin/resources/views/pages/brands/create.blade.php deleted file mode 100755 index d5ed77a81..000000000 --- a/packages/admin/resources/views/pages/brands/create.blade.php +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/admin/resources/views/pages/brands/edit.blade.php b/packages/admin/resources/views/pages/brands/edit.blade.php deleted file mode 100755 index 9a4610ea2..000000000 --- a/packages/admin/resources/views/pages/brands/edit.blade.php +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/admin/resources/views/pages/brands/index.blade.php b/packages/admin/resources/views/pages/brands/index.blade.php deleted file mode 100755 index 179892427..000000000 --- a/packages/admin/resources/views/pages/brands/index.blade.php +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/admin/routes/admin/brand.php b/packages/admin/routes/admin/brand.php deleted file mode 100644 index 086f69afc..000000000 --- a/packages/admin/routes/admin/brand.php +++ /dev/null @@ -1,7 +0,0 @@ -name('index'); diff --git a/packages/admin/routes/cpanel.php b/packages/admin/routes/cpanel.php index 40c4b0d15..8b78ccd91 100755 --- a/packages/admin/routes/cpanel.php +++ b/packages/admin/routes/cpanel.php @@ -13,9 +13,7 @@ }); if (Feature::enabled('brand')) { - Route::as('brands.')->prefix('brands')->group(function (): void { - require __DIR__ . '/admin/brand.php'; - }); + Route::get('/brands', config('shopper.components.brand.pages.index'))->name('brands.index'); } if (Feature::enabled('category')) { diff --git a/packages/admin/src/Contracts/PanelContract.php b/packages/admin/src/Contracts/PanelContract.php new file mode 100644 index 000000000..bf2d5d7ba --- /dev/null +++ b/packages/admin/src/Contracts/PanelContract.php @@ -0,0 +1,9 @@ +authorize('browse_brands'); - - return view('shopper::pages.brands.index'); - } - - public function create(): View - { - $this->authorize('add_brands'); - - return view('shopper::pages.brands.create'); - } - - public function edit(int $id): View - { - $this->authorize('edit_brands'); - - return view('shopper::pages.brands.edit', [ - 'brand' => (new BrandRepository())->getById($id), - ]); - } -} diff --git a/packages/admin/src/Livewire/Components/Brands/Browse.php b/packages/admin/src/Livewire/Components/Brands/Browse.php deleted file mode 100755 index 12813387a..000000000 --- a/packages/admin/src/Livewire/Components/Brands/Browse.php +++ /dev/null @@ -1,19 +0,0 @@ - (new BrandRepository())->count(), - ]); - } -} diff --git a/packages/admin/src/Livewire/Components/Brands/Create.php b/packages/admin/src/Livewire/Components/Brands/Create.php deleted file mode 100755 index 6ef30b900..000000000 --- a/packages/admin/src/Livewire/Components/Brands/Create.php +++ /dev/null @@ -1,40 +0,0 @@ - 'onTrixValueUpdate', - 'shopper:fileUpdated' => 'onFileUpdate', - ]; - - public function store(): void - { - $brand = $this->save((new BrandRepository())->model()); - - if ($this->fileUrl) { - $brand->addMedia($this->fileUrl)->toMediaCollection(config('shopper.core.storage.collection_name')); - } - - session()->flash('success', __('shopper::notifications.actions.create', ['item' => __('shopper::words.brand')])); - - $this->redirectRoute('shopper.brands.index'); - } - - public function render(): View - { - return view('shopper::livewire.components.brands.create'); - } -} diff --git a/packages/admin/src/Livewire/Components/Brands/Edit.php b/packages/admin/src/Livewire/Components/Brands/Edit.php deleted file mode 100755 index eb69516b7..000000000 --- a/packages/admin/src/Livewire/Components/Brands/Edit.php +++ /dev/null @@ -1,59 +0,0 @@ - 'onTrixValueUpdate', - 'shopper:fileUpdated' => 'onFileUpdate', - ]; - - public function mount($brand): void - { - $this->brand = $brand; - $this->brandId = $brand->id; - $this->name = $brand->name; - $this->website = $brand->website; - $this->description = $brand->description; - $this->is_enabled = $brand->is_enabled; - $this->updateSeo = true; - $this->seoTitle = $brand->seo_title ?? $brand->name; - $this->seoDescription = $brand->seo_description; - } - - public function isUpdate(): bool - { - return true; - } - - public function store(): void - { - $this->save($this->brand); - - if ($this->fileUrl) { - $this->brand->addMedia($this->fileUrl)->toMediaCollection(config('shopper.core.storage.collection_name')); - } - - session()->flash('success', __('shopper::notifications.actions.update', ['item' => __('shopper::words.brand')])); - - $this->redirectRoute('shopper.brands.index'); - } - - public function render(): View - { - return view('shopper::livewire.components.brands.edit'); - } -} diff --git a/packages/admin/src/Livewire/Components/Brands/UseForm.php b/packages/admin/src/Livewire/Components/Brands/UseForm.php deleted file mode 100755 index bba60c989..000000000 --- a/packages/admin/src/Livewire/Components/Brands/UseForm.php +++ /dev/null @@ -1,67 +0,0 @@ - 'name', - 'description' => 'description', - ]; - - public function onTrixValueUpdate(string $value): void - { - $this->description = $value; - } - - public function onFileUpdate($file): void - { - $this->fileUrl = $file; - } - - public function rules(): array - { - return [ - 'name' => array_merge([ - 'required', - 'max:150', - ], $this->brandId ? [ - Rule::unique(shopper_table('brands'), 'name')->ignore($this->brandId), - ] : ['unique:' . shopper_table('brands')]), - ]; - } - - public function save(Model | string $model): mixed - { - $this->validate($this->rules()); - - $values = [ - 'name' => $this->name, - 'slug' => $this->name, - 'website' => $this->website, - 'description' => $this->description, - 'is_enabled' => $this->is_enabled, - 'seo_title' => $this->seoTitle, - 'seo_description' => str_limit($this->seoDescription, 157), - ]; - - return $this->brandId ? $model->update($values) : $model::query()->create($values); - } -} diff --git a/packages/admin/src/Livewire/Components/SidePanel.php b/packages/admin/src/Livewire/Components/SidePanel.php deleted file mode 100644 index 1a089b417..000000000 --- a/packages/admin/src/Livewire/Components/SidePanel.php +++ /dev/null @@ -1,47 +0,0 @@ -uniqueId = uniqid(Str::random(8)); - } - - #[On('openPanel')] - public function openPanel(string $title, string $component): void - { - $this->open = true; - $this->title = $title; - $this->component = $component; - } - - #[On('closePanel')] - public function closePanel(): void - { - $this->reset(); - } - - public function render(): View - { - return view('shopper::livewire.components.side-panel'); - } -} diff --git a/packages/admin/src/Livewire/Components/SlideOverComponent.php b/packages/admin/src/Livewire/Components/SlideOverComponent.php new file mode 100644 index 000000000..0bcc84a8b --- /dev/null +++ b/packages/admin/src/Livewire/Components/SlideOverComponent.php @@ -0,0 +1,136 @@ + 'max-w-sm', + 'md' => 'max-w-md', + 'lg' => 'max-w-lg', + 'xl' => 'max-w-xl', + '2xl' => 'max-w-2xl', + '3xl' => 'max-w-3xl', + '4xl' => 'max-w-4xl', + '5xl' => 'max-w-5xl', + '6xl' => 'max-w-6xl', + '7xl' => 'max-w-7xl', + ]; + + public function destroySkippedPanels(): self + { + $this->destroySkipped = true; + + return $this; + } + + public function skipPreviousPanels($count = 1, $destroy = false): self + { + $this->skipPreviousPanel($count, $destroy); + + return $this; + } + + public function skipPreviousPanel($count = 1, $destroy = false): self + { + $this->skipPanels = $count; + $this->destroySkipped = $destroy; + + return $this; + } + + public function forceClose(): self + { + $this->forceClose = true; + + return $this; + } + + public function closePanel(): void + { + $this->dispatch( + 'closePanel', + force: $this->forceClose, + skipPreviousPanels: $this->skipPanels, + destroySkipped: $this->destroySkipped + ); + } + + public function closePanelWithEvents(array $events): void + { + $this->emitPanelEvents($events); + $this->closePanel(); + } + + public static function panelMaxWidth(): string + { + return 'xl'; + } + + public static function panelMaxWidthClass(): string + { + if (! array_key_exists(static::panelMaxWidth(), static::$maxWidths)) { + throw new InvalidArgumentException( + sprintf( + 'Panel max width [%s] is invalid. The width must be one of the following [%s].', + static::panelMaxWidth(), + implode(', ', array_keys(static::$maxWidths)) + ), + ); + } + + return static::$maxWidths[static::panelMaxWidth()]; + } + + public static function closePanelOnClickAway(): bool + { + return true; + } + + public static function closePanelOnEscape(): bool + { + return true; + } + + public static function closePanelOnEscapeIsForceful(): bool + { + return true; + } + + public static function dispatchCloseEvent(): bool + { + return false; + } + + public static function destroyOnClose(): bool + { + return false; + } + + private function emitPanelEvents(array $events): void + { + foreach ($events as $component => $event) { + if (is_array($event)) { + [$event, $params] = $event; + } + + if (is_numeric($component)) { + $this->dispatch($event, ...$params ?? []); + } else { + $this->dispatch($event, ...$params ?? [])->to($component); + } + } + } +} diff --git a/packages/admin/src/Livewire/Components/SlideOverPanel.php b/packages/admin/src/Livewire/Components/SlideOverPanel.php new file mode 100644 index 000000000..41e8a2d76 --- /dev/null +++ b/packages/admin/src/Livewire/Components/SlideOverPanel.php @@ -0,0 +1,126 @@ +components = []; + $this->activeComponent = null; + } + + public function openPanel($component, $arguments = [], $panelAttributes = []): void + { + $requiredInterface = PanelContract::class; + $componentClass = app(ComponentRegistry::class)->getClass($component); + $reflect = new ReflectionClass($componentClass); + + if ($reflect->implementsInterface($requiredInterface) === false) { + throw new Exception("[{$componentClass}] does not implement [{$requiredInterface}] interface."); + } + + $id = md5($component . serialize($arguments)); + + $arguments = collect($arguments) + ->merge($this->resolveComponentProps($arguments, new $componentClass())) + ->all(); + + $this->components[$id] = [ + 'name' => $component, + 'arguments' => $arguments, + 'panelAttributes' => array_merge([ + 'closeOnClickAway' => $componentClass::closePanelOnClickAway(), + 'closeOnEscape' => $componentClass::closePanelOnEscape(), + 'closeOnEscapeIsForceful' => $componentClass::closePanelOnEscapeIsForceful(), + 'dispatchCloseEvent' => $componentClass::dispatchCloseEvent(), + 'destroyOnClose' => $componentClass::destroyOnClose(), + 'maxWidth' => $componentClass::panelMaxWidth(), + 'maxWidthClass' => $componentClass::panelMaxWidthClass(), + ], $panelAttributes), + ]; + + $this->activeComponent = $id; + + $this->dispatch('activePanelComponentChanged', id: $id); + } + + public function resolveComponentProps(array $attributes, Component $component): Collection + { + return $this->getPublicPropertyTypes($component) + ->intersectByKeys($attributes) + ->map(function ($className, $propName) use ($attributes) { + $resolved = $this->resolveParameter($attributes, $propName, $className); + + return $resolved; + }); + } + + protected function resolveParameter($attributes, $parameterName, $parameterClassName) + { + $parameterValue = $attributes[$parameterName]; + + if ($parameterValue instanceof UrlRoutable) { + return $parameterValue; + } + + if (enum_exists($parameterClassName)) { + $enum = $parameterClassName::tryFrom($parameterValue); + + if ($enum !== null) { + return $enum; + } + } + + $instance = app()->make($parameterClassName); + + if (! $model = $instance->resolveRouteBinding($parameterValue)) { + throw (new ModelNotFoundException())->setModel(get_class($instance), [$parameterValue]); + } + + return $model; + } + + public function getPublicPropertyTypes($component): Collection + { + return collect($component->all()) + ->map(fn ($value, $name) => Reflector::getParameterClassName(new ReflectionProperty($component, $name))) + ->filter(); + } + + public function destroyComponent($id): void + { + unset($this->components[$id]); + } + + public function getListeners(): array + { + return [ + 'openPanel', + 'destroyComponent', + ]; + } + + public function render(): View + { + return view('shopper::livewire.components.slide-over-panel'); + } +} diff --git a/packages/admin/src/Livewire/Pages/Brand/Index.php b/packages/admin/src/Livewire/Pages/Brand/Index.php new file mode 100755 index 000000000..769fcc629 --- /dev/null +++ b/packages/admin/src/Livewire/Pages/Brand/Index.php @@ -0,0 +1,138 @@ +authorize('browse_brands'); + } + + public function table(Table $table): Table + { + return $table + ->query((new BrandRepository())->makeModel()->newQuery()) + ->columns([ + Tables\Columns\SpatieMediaLibraryImageColumn::make('Logo') + ->collection(config('shopper.core.storage.collection_name')) + ->circular() + ->defaultImageUrl(url(config('shopper.media.fallback_url'))) + ->grow(false), + Tables\Columns\TextColumn::make('name') + ->label(__('shopper::layout.forms.label.name')) + ->searchable() + ->sortable(), + Tables\Columns\TextColumn::make('website') + ->label(__('shopper::layout.forms.label.website')) + ->formatStateUsing(fn (string $state): View => view( + 'shopper::livewire.tables.cells.brands.site', + ['state' => $state], + )), + Tables\Columns\IconColumn::make('is_enabled') + ->label(__('shopper::layout.forms.label.visibility')) + ->boolean(), + Tables\Columns\TextColumn::make('updated_at') + ->label(__('shopper::layout.forms.label.updated_at')) + ->date() + ->sortable(), + ]) + ->actions([ + Tables\Actions\Action::make('edit') + ->label(__('shopper::layout.forms.actions.edit')) + ->color('info') + ->action( + fn ($record) => $this->dispatch( + 'openPanel', + component: 'shopper-slide-overs.brand-form', + arguments: ['brandId' => $record->id] + ) + ) + ->visible(auth()->user()->can('edit_brands')), + ]) + ->groupedBulkActions([ + Tables\Actions\DeleteBulkAction::make() + ->label(__('shopper::layout.forms.actions.delete')) + ->requiresConfirmation() + ->action(function (Collection $records): void { + $records->each(fn (Brand $record) => $record->delete()); + + Notification::make() + ->title(__('shopper::components.tables.status.delete')) + ->body( + __('shopper::components.tables.messages.delete', [ + 'name' => mb_strtolower(__('shopper::layout.forms.label.brand')), + ]) + ) + ->success() + ->send(); + }) + ->deselectRecordsAfterCompletion(), + Tables\Actions\BulkAction::make('enabled') + ->label(__('shopper::layout.forms.actions.enable')) + ->icon('untitledui-check-verified') + ->action(function (Collection $records): void { + $records->each->updateStatus(); + + Notification::make() + ->title(__('shopper::components.tables.status.updated')) + ->body( + __('shopper::components.tables.messages.enabled', [ + 'name' => mb_strtolower(__('shopper::layout.forms.label.brand')), + ]) + ) + ->success() + ->send(); + }) + ->deselectRecordsAfterCompletion(), + Tables\Actions\BulkAction::make('disabled') + ->label(__('shopper::layout.forms.actions.disable')) + ->icon('untitledui-slash-circle-01') + ->action(function (Collection $records): void { + $records->each->updateStatus(status: false); + + Notification::make() + ->title(__('shopper::components.tables.status.updated')) + ->body( + __('shopper::components.tables.messages.disabled', [ + 'name' => mb_strtolower(__('shopper::layout.forms.label.brand')), + ]) + ) + ->success() + ->send(); + }) + ->deselectRecordsAfterCompletion(), + ]) + ->filters([ + Tables\Filters\TernaryFilter::make('is_enabled'), + ]) + ->persistFiltersInSession(); + } + + #[On('brand-save')] + public function render(): View + { + return view('shopper::livewire.pages.brand.index', [ + 'total' => (new BrandRepository())->count(), + ]); + } +} diff --git a/packages/admin/src/Livewire/SlideOvers/BrandForm.php b/packages/admin/src/Livewire/SlideOvers/BrandForm.php new file mode 100755 index 000000000..51917b5cf --- /dev/null +++ b/packages/admin/src/Livewire/SlideOvers/BrandForm.php @@ -0,0 +1,131 @@ +brand = $brandId + ? (new BrandRepository())->getById($brandId) + : (new BrandRepository())->makeModel(); + + $this->form->fill($this->brand->toArray()); + } + + public function form(Form $form): Form + { + return $form + ->schema([ + Section::make('General') + ->collapsible() + ->compact() + ->schema([ + Components\TextInput::make('name') + ->label(__('shopper::layout.forms.label.name')) + ->placeholder('Apple, Nike, Samsung...') + ->required() + ->live(onBlur: true) + ->afterStateUpdated(function (string $operation, $state, Set $set): void { + $set('slug', Str::slug($state)); + }), + Components\Hidden::make('slug'), + Components\TextInput::make('website') + ->label(__('shopper::layout.forms.label.website')) + ->placeholder('https://example.com') + ->url(), + Components\Toggle::make('is_enabled') + ->label(__('shopper::layout.forms.label.visibility')) + ->helperText(__('shopper::words.set_visibility', ['name' => mb_strtolower(__('shopper::words.brand'))])), + Components\RichEditor::make('description') + ->label(__('shopper::layout.forms.label.description')) + ->toolbarButtons([ + 'bold', + 'italic', + 'link', + 'redo', + 'strike', + 'underline', + 'undo', + ]), + ]), + Section::make('Media') + ->collapsible() + ->compact() + ->schema([ + Components\SpatieMediaLibraryFileUpload::make('file') + ->label(__('shopper::layout.forms.label.image_preview')) + ->collection(config('shopper.core.storage.collection_name')) + ->image() + ->maxSize(1024), + ]), + Section::make('Seo') + ->collapsible() + ->compact() + ->schema([ + Components\TextInput::make('seo_title') + ->label(__('shopper::layout.forms.label.title')), + Components\Textarea::make('seo_description') + ->label(__('shopper::layout.forms.label.description')) + ->hint(__('shopper::components.seo.characters')) + ->maxLength(160), + ]), + Section::make('Metadata') + ->collapsible() + ->compact() + ->schema([ + Components\KeyValue::make('metadata') + ->reorderable(), + ]), + ]) + ->statePath('data') + ->model($this->brand); + } + + public function save(): void + { + if ($this->brand->id) { + $this->brand->update($this->form->getState()); + } else { + $brand = (new BrandRepository())->create($this->form->getState()); + $this->form->model($brand)->saveRelationships(); + } + + Notification::make() + ->title(__('shopper::notifications.actions.save', ['item' => __('shopper::words.brand')])) + ->success() + ->send(); + + $this->dispatch('brand-save'); + + $this->closePanel(); + + $this->form->fill(); + } + + public function render(): View + { + return view('shopper::livewire.slide-overs.brand-form'); + } +} diff --git a/packages/admin/src/Livewire/SlideOvers/CreateTeamMember.php b/packages/admin/src/Livewire/SlideOvers/CreateTeamMember.php index 85dbb97b7..a6642b66a 100644 --- a/packages/admin/src/Livewire/SlideOvers/CreateTeamMember.php +++ b/packages/admin/src/Livewire/SlideOvers/CreateTeamMember.php @@ -13,14 +13,14 @@ use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; -use Livewire\Component; use Shopper\Components\Section; use Shopper\Core\Models\Role; use Shopper\Core\Models\User; use Shopper\Core\Repositories\UserRepository; +use Shopper\Livewire\Components\SlideOverComponent; use Shopper\Notifications\AdminSendCredentials; -class CreateTeamMember extends Component implements HasForms +class CreateTeamMember extends SlideOverComponent implements HasForms { use InteractsWithForms; diff --git a/packages/admin/src/Providers/FeatureServiceProvider.php b/packages/admin/src/Providers/FeatureServiceProvider.php new file mode 100644 index 000000000..1f11e8aca --- /dev/null +++ b/packages/admin/src/Providers/FeatureServiceProvider.php @@ -0,0 +1,39 @@ +registerComponentsConfigFiles(); + } + + public function boot(): void + { + foreach ($this->components() as $alias => $component) { + Livewire::component("shopper-{$alias}", $component); + } + } + + protected function components(): array + { + return array_merge( + $this->loadLivewireComponents('brand'), + ); + } +} diff --git a/packages/admin/src/ShopperServiceProvider.php b/packages/admin/src/ShopperServiceProvider.php index 89c44a7ed..c86c54558 100755 --- a/packages/admin/src/ShopperServiceProvider.php +++ b/packages/admin/src/ShopperServiceProvider.php @@ -24,6 +24,7 @@ use Shopper\Http\Responses\TwoFactorLoginResponse; use Shopper\Livewire\Components; use Shopper\Livewire\Pages; +use Shopper\Providers\FeatureServiceProvider; use Shopper\Providers\SidebarServiceProvider; use Shopper\Providers\TwoFactorAuthenticationProvider; use Shopper\Traits\LoadComponents; @@ -88,6 +89,7 @@ public function packageRegistered(): void $this->app->bind(LoginResponseContract::class, LoginResponse::class); $this->app->register(SidebarServiceProvider::class); + $this->app->register(FeatureServiceProvider::class); $this->app->scoped('shopper', fn (): ShopperPanel => new ShopperPanel()); diff --git a/packages/admin/tailwind.config.js b/packages/admin/tailwind.config.js index 7f7e947fe..932f902f8 100755 --- a/packages/admin/tailwind.config.js +++ b/packages/admin/tailwind.config.js @@ -24,7 +24,6 @@ module.exports = { extend: { colors: { primary: colors.blue, - custom: colors.blue, secondary: colors.gray, success: colors.emerald, warning: colors.amber, diff --git a/packages/core/database/migrations/2024_03_28_084412_add_metadata-fields_table.php b/packages/core/database/migrations/2024_03_28_084412_add_metadata-fields_table.php new file mode 100644 index 000000000..39cf87dd1 --- /dev/null +++ b/packages/core/database/migrations/2024_03_28_084412_add_metadata-fields_table.php @@ -0,0 +1,30 @@ +tables as $table) { + Schema::table($this->getTableName($table), function (Blueprint $blueprint): void { + $blueprint->json('metadata')->nullable(); + }); + } + } +}; diff --git a/packages/core/src/Models/Brand.php b/packages/core/src/Models/Brand.php index 714c138c7..ae3369bfd 100755 --- a/packages/core/src/Models/Brand.php +++ b/packages/core/src/Models/Brand.php @@ -12,6 +12,18 @@ use Shopper\Core\Traits\HasSlug; use Spatie\MediaLibrary\HasMedia as SpatieHasMedia; +/** + * @property-read int $id + * @property string $name + * @property string | null $slug + * @property string | null $website + * @property string | null $description + * @property int $position + * @property bool $is_enabled + * @property string | null $seo_title + * @property string | null $seo_description + * @property array $metadata + */ class Brand extends Model implements SpatieHasMedia { use HasFactory; @@ -22,6 +34,7 @@ class Brand extends Model implements SpatieHasMedia protected $casts = [ 'is_enabled' => 'boolean', + 'metadata' => 'array', ]; public function getTable(): string @@ -38,4 +51,11 @@ public function products(): HasMany { return $this->hasMany(config('shopper.models.product')); } + + public function updateStatus(bool $status = true): void + { + $this->is_enabled = $status; + + $this->save(); + } }