diff --git a/website/resources/ts/@layouts/components/VerticalNav.vue b/website/resources/ts/@layouts/components/VerticalNav.vue deleted file mode 100644 index e0ebdaf..0000000 --- a/website/resources/ts/@layouts/components/VerticalNav.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - diff --git a/website/resources/ts/@layouts/components/VerticalNavGroup.vue b/website/resources/ts/@layouts/components/VerticalNavGroup.vue deleted file mode 100644 index 2fd4774..0000000 --- a/website/resources/ts/@layouts/components/VerticalNavGroup.vue +++ /dev/null @@ -1,108 +0,0 @@ - - - diff --git a/website/resources/ts/@layouts/components/VerticalNavLayout.vue b/website/resources/ts/@layouts/components/VerticalNavLayout.vue deleted file mode 100644 index 12621f4..0000000 --- a/website/resources/ts/@layouts/components/VerticalNavLayout.vue +++ /dev/null @@ -1,107 +0,0 @@ - - - diff --git a/website/resources/ts/@layouts/components/VerticalNavLink.vue b/website/resources/ts/@layouts/components/VerticalNavLink.vue deleted file mode 100644 index a1fc003..0000000 --- a/website/resources/ts/@layouts/components/VerticalNavLink.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - diff --git a/website/resources/ts/@layouts/components/VerticalNavSectionTitle.vue b/website/resources/ts/@layouts/components/VerticalNavSectionTitle.vue deleted file mode 100644 index 5a8feab..0000000 --- a/website/resources/ts/@layouts/components/VerticalNavSectionTitle.vue +++ /dev/null @@ -1,21 +0,0 @@ - - - diff --git a/website/resources/ts/@layouts/styles/_mixins.scss b/website/resources/ts/@layouts/styles/_mixins.scss deleted file mode 100644 index ed35ae4..0000000 --- a/website/resources/ts/@layouts/styles/_mixins.scss +++ /dev/null @@ -1,30 +0,0 @@ -@use "placeholders"; -@use "@configured-variables" as variables; - -@mixin rtl { - @if variables.$enable-rtl-styles { - [dir="rtl"] & { - @content; - } - } -} - -@mixin boxed-content($nest-selector: false) { - & { - @extend %boxed-content-spacing; - - @at-root { - @if $nest-selector == false { - .layout-content-width-boxed#{&} { - @extend %boxed-content; - } - } - // stylelint-disable-next-line @stylistic/indentation - @else { - .layout-content-width-boxed & { - @extend %boxed-content; - } - } - } - } -} diff --git a/website/resources/ts/@layouts/styles/_placeholders.scss b/website/resources/ts/@layouts/styles/_placeholders.scss deleted file mode 100644 index 0ab9f5f..0000000 --- a/website/resources/ts/@layouts/styles/_placeholders.scss +++ /dev/null @@ -1,9 +0,0 @@ -// Stub β€” placeholders not used in our Inertia setup -%boxed-content { - max-inline-size: 1440px; - margin-inline: auto; -} - -%boxed-content-spacing { - padding-inline: 1.5rem; -} diff --git a/website/resources/ts/@layouts/styles/_variables.scss b/website/resources/ts/@layouts/styles/_variables.scss deleted file mode 100644 index 90d610c..0000000 --- a/website/resources/ts/@layouts/styles/_variables.scss +++ /dev/null @@ -1,29 +0,0 @@ -// @use "@styles/style.scss"; - -// πŸ‘‰ Vertical nav -$layout-vertical-nav-z-index: 12 !default; -$layout-vertical-nav-width: 260px !default; -$layout-vertical-nav-collapsed-width: 80px !default; -$selector-vertical-nav-mini: ".layout-vertical-nav-collapsed .layout-vertical-nav:not(:hover)"; - -// πŸ‘‰ Horizontal nav -$layout-horizontal-nav-z-index: 11 !default; -$layout-horizontal-nav-navbar-height: 64px !default; - -// πŸ‘‰ Navbar -$layout-vertical-nav-navbar-height: 64px !default; -$layout-vertical-nav-navbar-is-contained: true !default; -$layout-vertical-nav-layout-navbar-z-index: 11 !default; -$layout-horizontal-nav-layout-navbar-z-index: 11 !default; - -// πŸ‘‰ Main content -$layout-boxed-content-width: 1440px !default; - -// πŸ‘‰Footer -$layout-vertical-nav-footer-height: 56px !default; - -// πŸ‘‰ Layout overlay -$layout-overlay-z-index: 11 !default; - -// πŸ‘‰ RTL -$enable-rtl-styles: true !default; diff --git a/website/resources/ts/@layouts/styles/_vertical-nav.scss b/website/resources/ts/@layouts/styles/_vertical-nav.scss deleted file mode 100644 index e8f7bc7..0000000 --- a/website/resources/ts/@layouts/styles/_vertical-nav.scss +++ /dev/null @@ -1,311 +0,0 @@ -@use "@configured-variables" as variables; -@use "variables" as layout-variables; - -// ━━━ Layout Wrapper ━━━ -.layout-wrapper.layout-nav-type-vertical { - display: flex; - min-block-size: 100vh; - flex: 1 1 auto; -} - -// ━━━ Vertical Nav (Sidebar) ━━━ -.layout-vertical-nav { - position: fixed; - inset-block: 0; - inset-inline-start: 0; - z-index: layout-variables.$layout-vertical-nav-z-index; - display: flex; - flex-direction: column; - inline-size: layout-variables.$layout-vertical-nav-width; - background-color: rgb(var(--v-theme-surface)); - border-inline-end: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); - transition: inline-size 0.25s ease-in-out, transform 0.25s ease-in-out, box-shadow 0.25s ease-in-out; - - // ━━━ Nav Header ━━━ - .nav-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 16px 8px; - min-block-size: layout-variables.$layout-vertical-nav-navbar-height; - } - - .nav-header-logo { - display: flex; - align-items: center; - gap: 10px; - text-decoration: none; - overflow: hidden; - } - - .nav-logo-img { - block-size: 28px; - inline-size: auto; - flex-shrink: 0; - } - - .nav-logo-title { - font-size: 1.125rem; - font-weight: 700; - letter-spacing: 0.5px; - color: rgb(var(--v-theme-on-surface)); - white-space: nowrap; - transition: opacity 0.15s ease; - } - - .nav-collapse-btn { - flex-shrink: 0; - } - - // ━━━ Nav Items List ━━━ - .nav-items { - list-style: none; - padding: 0; - margin: 0; - overflow-y: auto; - overflow-x: hidden; - flex-grow: 1; - padding-block: 4px; - padding-inline: 12px; - } - - // ━━━ Nav Link ━━━ - .nav-link { - margin-block: 2px; - - .nav-link-btn { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - border-radius: 6px; - text-decoration: none; - color: rgba(var(--v-theme-on-surface), 0.78); - transition: background-color 0.15s ease, color 0.15s ease; - white-space: nowrap; - overflow: hidden; - } - - .nav-item-icon { - flex-shrink: 0; - } - - .nav-item-title { - font-size: 0.9375rem; - line-height: 1.4; - flex-grow: 1; - overflow: hidden; - text-overflow: ellipsis; - } - - .nav-item-badge { - font-size: 0.75rem; - padding: 2px 6px; - border-radius: 4px; - background-color: rgb(var(--v-theme-error)); - color: rgb(var(--v-theme-on-error)); - line-height: 1; - flex-shrink: 0; - } - - &:not(.disabled) .nav-link-btn:hover { - background-color: rgba(var(--v-theme-on-surface), 0.06); - } - - &.active .nav-link-btn { - background: linear-gradient(270deg, rgb(var(--v-theme-primary)), rgba(var(--v-theme-primary), 0.7)); - color: rgb(var(--v-theme-on-primary)); - box-shadow: 0 2px 6px 0 rgba(var(--v-theme-primary), 0.4); - - .nav-item-icon { - color: inherit; - } - } - - &.disabled .nav-link-btn { - opacity: 0.4; - pointer-events: none; - } - } - - // ━━━ Nav Group ━━━ - .nav-group { - margin-block: 2px; - - .nav-group-label { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - border-radius: 6px; - cursor: pointer; - color: rgba(var(--v-theme-on-surface), 0.78); - transition: background-color 0.15s ease; - white-space: nowrap; - overflow: hidden; - user-select: none; - } - - .nav-group-label:hover { - background-color: rgba(var(--v-theme-on-surface), 0.06); - } - - &.active > .nav-group-label { - color: rgb(var(--v-theme-primary)); - } - - .nav-group-arrow { - margin-inline-start: auto; - transition: transform 0.25s ease-in-out; - flex-shrink: 0; - } - - &.open > .nav-group-label .nav-group-arrow { - transform: rotate(90deg); - } - - .nav-group-children { - list-style: none; - padding: 0; - margin: 0; - padding-inline-start: 12px; - } - - &.disabled > .nav-group-label { - opacity: 0.4; - pointer-events: none; - } - } - - // ━━━ Section Title ━━━ - .nav-section-title { - margin-block: 12px 4px; - padding-inline: 12px; - - .nav-section-title-text { - font-size: 0.6875rem; - font-weight: 600; - letter-spacing: 1px; - text-transform: uppercase; - color: rgba(var(--v-theme-on-surface), 0.38); - white-space: nowrap; - overflow: hidden; - } - - .nav-section-title-icon { - color: rgba(var(--v-theme-on-surface), 0.38); - margin-inline: auto; - } - - &:first-child { - margin-block-start: 0; - } - } -} - -// ━━━ Collapsed / Mini Mode ━━━ -.layout-vertical-nav-collapsed .layout-vertical-nav:not(:hover) { - inline-size: layout-variables.$layout-vertical-nav-collapsed-width; -} - -.layout-vertical-nav-collapsed .layout-vertical-nav:hover { - box-shadow: 0 4px 25px 0 rgba(0, 0, 0, 0.25); -} - -// ━━━ Content Wrapper ━━━ -.layout-content-wrapper { - display: flex; - flex-direction: column; - flex-grow: 1; - min-block-size: 100vh; - padding-inline-start: layout-variables.$layout-vertical-nav-width; - transition: padding-inline-start 0.25s ease-in-out; -} - -.layout-vertical-nav-collapsed .layout-content-wrapper { - padding-inline-start: layout-variables.$layout-vertical-nav-collapsed-width; -} - -// ━━━ Navbar ━━━ -.layout-navbar { - position: sticky; - inset-block-start: 0; - z-index: layout-variables.$layout-vertical-nav-layout-navbar-z-index; - background-color: rgb(var(--v-theme-surface)); - border-block-end: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); - min-block-size: layout-variables.$layout-vertical-nav-navbar-height; - - .navbar-content { - block-size: 100%; - } -} - -// ━━━ Page Content ━━━ -.layout-page-content { - flex-grow: 1; - padding: 24px; -} - -// ━━━ Footer ━━━ -.layout-footer { - display: flex; - align-items: center; - justify-content: center; - min-block-size: layout-variables.$layout-vertical-nav-footer-height; - padding-inline: 24px; -} - -// ━━━ Overlay ━━━ -.layout-overlay { - position: fixed; - inset: 0; - z-index: layout-variables.$layout-overlay-z-index; - background-color: rgba(0, 0, 0, 0.5); - opacity: 0; - pointer-events: none; - transition: opacity 0.25s ease-in-out; - - &.visible { - opacity: 1; - pointer-events: auto; - } -} - -// ━━━ Responsive (Mobile Overlay) ━━━ -@media (max-width: 1279.98px) { - .layout-vertical-nav { - position: fixed; - transform: translateX(-100%); - - &.visible { - transform: translateX(0); - } - } - - .layout-content-wrapper { - padding-inline-start: 0 !important; - } - - .layout-vertical-nav-collapsed .layout-content-wrapper { - padding-inline-start: 0 !important; - } -} - -// ━━━ Transition for nav group expand/collapse ━━━ -.vertical-nav-group-enter-active, -.vertical-nav-group-leave-active { - overflow: hidden; - transition: max-height 0.3s ease-in-out, opacity 0.25s ease-in-out; -} - -.vertical-nav-group-enter-from, -.vertical-nav-group-leave-to { - max-height: 0; - opacity: 0; -} - -.vertical-nav-group-enter-to, -.vertical-nav-group-leave-from { - max-height: 500px; - opacity: 1; -} diff --git a/website/resources/ts/Components/AppSidebar.vue b/website/resources/ts/Components/AppSidebar.vue new file mode 100644 index 0000000..86d8d05 --- /dev/null +++ b/website/resources/ts/Components/AppSidebar.vue @@ -0,0 +1,150 @@ + + + diff --git a/website/resources/ts/Components/AppTopNavbar.vue b/website/resources/ts/Components/AppTopNavbar.vue new file mode 100644 index 0000000..355ae43 --- /dev/null +++ b/website/resources/ts/Components/AppTopNavbar.vue @@ -0,0 +1,54 @@ + + + diff --git a/website/resources/ts/Components/Breadcrumbs.vue b/website/resources/ts/Components/Breadcrumbs.vue new file mode 100644 index 0000000..a2aba30 --- /dev/null +++ b/website/resources/ts/Components/Breadcrumbs.vue @@ -0,0 +1,21 @@ + + + diff --git a/website/resources/ts/Components/CommandPalette.vue b/website/resources/ts/Components/CommandPalette.vue new file mode 100644 index 0000000..0c9e223 --- /dev/null +++ b/website/resources/ts/Components/CommandPalette.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/website/resources/ts/Components/EmptyState.vue b/website/resources/ts/Components/EmptyState.vue new file mode 100644 index 0000000..7b42618 --- /dev/null +++ b/website/resources/ts/Components/EmptyState.vue @@ -0,0 +1,38 @@ + + + diff --git a/website/resources/ts/Components/NotificationPanel.vue b/website/resources/ts/Components/NotificationPanel.vue new file mode 100644 index 0000000..e20d346 --- /dev/null +++ b/website/resources/ts/Components/NotificationPanel.vue @@ -0,0 +1,141 @@ + + + diff --git a/website/resources/ts/Components/SkeletonLoader.vue b/website/resources/ts/Components/SkeletonLoader.vue new file mode 100644 index 0000000..ec34b5b --- /dev/null +++ b/website/resources/ts/Components/SkeletonLoader.vue @@ -0,0 +1,39 @@ + + + diff --git a/website/resources/ts/Components/ToastStack.vue b/website/resources/ts/Components/ToastStack.vue new file mode 100644 index 0000000..256f527 --- /dev/null +++ b/website/resources/ts/Components/ToastStack.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/website/resources/ts/Layouts/AccountLayout.vue b/website/resources/ts/Layouts/AccountLayout.vue index 9e0c552..7fc1fea 100644 --- a/website/resources/ts/Layouts/AccountLayout.vue +++ b/website/resources/ts/Layouts/AccountLayout.vue @@ -1,11 +1,13 @@ diff --git a/website/resources/ts/Layouts/AdminLayout.vue b/website/resources/ts/Layouts/AdminLayout.vue index 48bb01c..5a2b5bf 100644 --- a/website/resources/ts/Layouts/AdminLayout.vue +++ b/website/resources/ts/Layouts/AdminLayout.vue @@ -1,11 +1,13 @@ diff --git a/website/resources/ts/Layouts/AuthLayout.vue b/website/resources/ts/Layouts/AuthLayout.vue index 43287e3..402159a 100644 --- a/website/resources/ts/Layouts/AuthLayout.vue +++ b/website/resources/ts/Layouts/AuthLayout.vue @@ -1,7 +1,6 @@ - diff --git a/website/resources/ts/Layouts/MarketingLayout.vue b/website/resources/ts/Layouts/MarketingLayout.vue index 3f13030..f68f4de 100644 --- a/website/resources/ts/Layouts/MarketingLayout.vue +++ b/website/resources/ts/Layouts/MarketingLayout.vue @@ -8,7 +8,6 @@ import logoWhite from '@images/ezscale_logo_white.png' const theme = useTheme() const isDark = computed(() => theme.global.current.value.dark) - const isScrolled = ref(false) function handleScroll(): void { @@ -25,20 +24,14 @@ onUnmounted(() => { }) const currentPath = computed(() => { - if (typeof window !== 'undefined') { - return window.location.pathname - } + if (typeof window !== 'undefined') return window.location.pathname return '/' }) -const newsletterForm = useForm({ - email: '', -}) - +const newsletterForm = useForm({ email: '' }) const newsletterSuccess = ref(false) function subscribeNewsletter(): void { - // For now, just show success state (no backend endpoint yet) if (newsletterForm.email) { newsletterSuccess.value = true setTimeout(() => { @@ -67,7 +60,6 @@ const footerLinks = { { title: 'About', href: '/about' }, { title: 'Pricing', href: '/pricing' }, { title: 'Contact', href: '/contact' }, - { title: 'Blog', href: '/blog' }, ], support: [ { title: 'Help Center', href: 'https://ezscale.support' }, @@ -84,32 +76,36 @@ const footerLinks = { const socialLinks = [ { title: 'twitter', icon: 'tabler-brand-twitter-filled', href: '#' }, - { title: 'facebook', icon: 'tabler-brand-facebook-filled', href: '#' }, - { title: 'github', icon: 'tabler-brand-github-filled', href: '#' }, { title: 'discord', icon: 'tabler-brand-discord-filled', href: '#' }, + { title: 'github', icon: 'tabler-brand-github-filled', href: '#' }, ] + +const mobileMenuOpen = ref(false)