feat: build UI
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
@use "sass:color";
|
@use "sass:color";
|
||||||
@use "bulma/sass/utilities/initial-variables.scss" as *;
|
@use "bulma/sass/utilities/initial-variables.scss" as *;
|
||||||
|
@use "bulma/sass/utilities/derived-variables.scss" as *;
|
||||||
@use "bulma/sass/utilities/mixins.scss" as *;
|
@use "bulma/sass/utilities/mixins.scss" as *;
|
||||||
|
@use "bulma/sass/components/card.scss" as *;
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900');
|
@use "utils.scss";
|
||||||
|
@use "overrides-components.scss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--bulma-family-primary: 'Inter', 'SF Pro', 'Helvetica', 'Arial', sans-serif;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
@@ -124,81 +126,15 @@ $size: (
|
|||||||
// 4. CUSTOM CLASSES
|
// 4. CUSTOM CLASSES
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|
||||||
.fullheight { height: 100vh; }
|
|
||||||
|
|
||||||
.textsize {
|
|
||||||
@include mobile { font-size: 18px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-logo {
|
.header-logo {
|
||||||
background: url('/logo_dev.png') no-repeat center center;
|
background: url('/logo_dev.png') no-repeat center center;
|
||||||
background-size: 40px;
|
background-size: 40px;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-bottom { border-bottom: 1px solid color.change($black-pure, $alpha: 0.15) }
|
|
||||||
|
|
||||||
.carousel-height {
|
|
||||||
width: 100%;
|
|
||||||
height: 80vh;
|
|
||||||
@include mobile { height: 110vh; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mobile Spacing Utilities
|
|
||||||
.mobile-mt20 { @include mobile { margin-top: 20px; } }
|
|
||||||
.mobile-px10 { @include mobile { padding-left: 10px; padding-right: 10px; } }
|
|
||||||
.mobile-pt10 { @include mobile { padding-top: 10px; } }
|
|
||||||
.mobile-pt80 { padding-top: 120px; @include mobile { padding-top: 80px; } }
|
|
||||||
|
|
||||||
.fullhd-pt30 {
|
|
||||||
padding-top: 30px;
|
|
||||||
@include until($fullhd) { padding-top: 0px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-width {
|
|
||||||
width: 120px !important;
|
|
||||||
@include mobile { width: 112px !important; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.hideon-mobile { @include mobile { display: none; } }
|
|
||||||
|
|
||||||
// Typography Classes
|
|
||||||
.maintext {
|
|
||||||
margin-top: 20px;
|
|
||||||
font-size: 40px;
|
|
||||||
line-height: 3rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: $blue-dianne;
|
|
||||||
@include mobile { font-size: 34px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtext {
|
|
||||||
margin-top: 30px;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
line-height: 2rem;
|
|
||||||
color: $cutty-sark;
|
|
||||||
@include mobile { line-height: 1.8rem; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.dotslide {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activetab {
|
|
||||||
border-radius: 8px;
|
|
||||||
color: $white-pure;
|
|
||||||
background-color: $blue-dianne;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block Layout
|
// Block Layout
|
||||||
.blockdiv {
|
.blockdiv {
|
||||||
max-width: 1900px !important;
|
max-width: 1900px !important;
|
||||||
background-color: $white-pure;
|
|
||||||
padding: 60px 15px 40px 15px;
|
padding: 60px 15px 40px 15px;
|
||||||
|
|
||||||
@include until($desktop) { padding: 65px 20px 30px 20px; }
|
@include until($desktop) { padding: 65px 20px 30px 20px; }
|
||||||
@@ -213,31 +149,6 @@ $size: (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-text {
|
|
||||||
padding-left: 15%; padding-right: 5%;
|
|
||||||
@include until($desktop) { padding-left: 0; padding-right: 0; }
|
|
||||||
@include until($fullhd) { padding-left: 0; padding-right: 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.padding-image {
|
|
||||||
padding-left: 5%; padding-right: 15%;
|
|
||||||
@include until($fullhd) { padding-left: 0; padding-right: 0; }
|
|
||||||
@include until($desktop) { padding-left: 15%; padding-right: 15%; }
|
|
||||||
@include mobile { padding-left: 0; padding-right: 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.imgcontainer {
|
|
||||||
position: relative;
|
|
||||||
width: 100% !important;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.centered {
|
|
||||||
position: absolute;
|
|
||||||
top: 80%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tooltip Styles
|
// Tooltip Styles
|
||||||
.tooltip {
|
.tooltip {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -262,8 +173,6 @@ $size: (
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.to-left { right: 30px; }
|
|
||||||
|
|
||||||
@mixin tooltipshow() {
|
@mixin tooltipshow() {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -277,166 +186,3 @@ $size: (
|
|||||||
|
|
||||||
.tooltip:hover .tooltiptext { @include tooltipshow() }
|
.tooltip:hover .tooltiptext { @include tooltipshow() }
|
||||||
.tooltip:hover .tooltiptext .to-left { @include tooltipshow() }
|
.tooltip:hover .tooltiptext .to-left { @include tooltipshow() }
|
||||||
|
|
||||||
// Dot Indicators
|
|
||||||
@mixin dot($background) {
|
|
||||||
height: 22px;
|
|
||||||
width: 22px;
|
|
||||||
text-align: center;
|
|
||||||
color: $white-pure;
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: $background;
|
|
||||||
display: inline-block;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 15px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@each $name, $hex in $color {
|
|
||||||
.dot-#{$name} {
|
|
||||||
@include dot($hex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 5. HELPER CLASSES GENERATOR
|
|
||||||
// ==========================================
|
|
||||||
@each $name, $hex in $color {
|
|
||||||
.bg-#{$name} { background-color: $hex !important; }
|
|
||||||
.text-#{$name} { color: $hex !important; }
|
|
||||||
.border-#{$name} { border-color: $hex !important; }
|
|
||||||
.icon-#{$name} { color: $hex !important; }
|
|
||||||
|
|
||||||
.icon-bg-#{$name} {
|
|
||||||
background-color: $hex !important;
|
|
||||||
color: if(color.channel($hex, "lightness", $space: hsl) > 70, $blue-dianne, $white-pure) !important;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 6. BULMA OVERRIDES
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
// Backgrounds
|
|
||||||
.has-background-primary { background-color: $primary-color !important; }
|
|
||||||
.has-background-secondary { background-color: $secondary-color !important; }
|
|
||||||
.has-background-info { background-color: $info-color !important; }
|
|
||||||
.has-background-success { background-color: $success-color !important; }
|
|
||||||
.has-background-warning { background-color: $warning-color !important; }
|
|
||||||
.has-background-danger { background-color: $danger-color !important; }
|
|
||||||
.has-background-light { background-color: $light-color !important; }
|
|
||||||
.has-background-dark { background-color: $dark-color !important; }
|
|
||||||
.has-background-white { background-color: $white-pure !important; }
|
|
||||||
|
|
||||||
// Text Colors
|
|
||||||
.has-text-primary { color: #086e71 !important; }
|
|
||||||
.has-text-secondary { color: $secondary-color !important; }
|
|
||||||
.has-text-info { color: $info-color !important; }
|
|
||||||
.has-text-success { color: $success-color !important; }
|
|
||||||
.has-text-warning { color: $warning-color !important; }
|
|
||||||
.has-text-danger { color: $danger-color !important; }
|
|
||||||
.has-text-light { color: $accent-color !important; }
|
|
||||||
.has-text-dark { color: $dark-color !important; }
|
|
||||||
|
|
||||||
// Button/Element States (is-*)
|
|
||||||
.is-primary {
|
|
||||||
background-color: $primary-color !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
color: $white-pure !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-secondary {
|
|
||||||
background-color: $secondary-color !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
color: $white-pure !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-link {
|
|
||||||
background-color: $link-color !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
color: $white-pure !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-info {
|
|
||||||
background-color: $info-color !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
color: $white-pure !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-success {
|
|
||||||
background-color: $success-color !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
color: $white-pure !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-warning {
|
|
||||||
background-color: $warning-color !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
color: $white-pure !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-danger {
|
|
||||||
background-color: $danger-color !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
color: $white-pure !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-light {
|
|
||||||
background-color: $light-color !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
color: $blue-dianne !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-dark {
|
|
||||||
background-color: $dark-color !important;
|
|
||||||
border-color: transparent !important;
|
|
||||||
color: $white-pure !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Outlined Variants
|
|
||||||
.is-primary.is-outlined {
|
|
||||||
background-color: transparent !important;
|
|
||||||
border-color: $primary-color !important;
|
|
||||||
color: $primary-color !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-link.is-outlined {
|
|
||||||
background-color: transparent !important;
|
|
||||||
border-color: $link-color !important;
|
|
||||||
color: $link-color !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-info.is-outlined {
|
|
||||||
background-color: transparent !important;
|
|
||||||
border-color: $info-color !important;
|
|
||||||
color: $info-color !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-success.is-outlined {
|
|
||||||
background-color: transparent !important;
|
|
||||||
border-color: $success-color !important;
|
|
||||||
color: $success-color !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-warning.is-outlined {
|
|
||||||
background-color: transparent !important;
|
|
||||||
border-color: $warning-color !important;
|
|
||||||
color: $warning-color !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-primary.is-light {
|
|
||||||
background-color: rgba($primary-color, 0.1) !important;
|
|
||||||
color: $primary-color !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-info.is-light {
|
|
||||||
background-color: rgba($info-color, 0.2) !important;
|
|
||||||
color: color.adjust($info-color, $lightness: -10%) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-success.is-light {
|
|
||||||
background-color: rgba($success-color, 0.1) !important;
|
|
||||||
color: $success-color !important;
|
|
||||||
}
|
|
||||||
6
app/assets/styles/overrides-components.scss
Normal file
6
app/assets/styles/overrides-components.scss
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@use "bulma/sass/utilities/initial-variables.scss" as *;
|
||||||
|
|
||||||
|
.card {
|
||||||
|
--bulma-card-shadow: none;
|
||||||
|
border: 1px solid $grey-lighter;
|
||||||
|
}
|
||||||
237
app/assets/styles/utils.scss
Normal file
237
app/assets/styles/utils.scss
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
@use "sass:list";
|
||||||
|
|
||||||
|
.font-thin {
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
.font-extralight {
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
.font-light {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
.font-normal {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.font-medium {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.font-semibold {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.font-bold {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.font-extrabold {
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
.font-black {
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-xs {
|
||||||
|
border-radius: 0.125rem;
|
||||||
|
}
|
||||||
|
.rounded-sm {
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
.rounded-md {
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
.rounded-lg {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
.rounded-xl {
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
}
|
||||||
|
.rounded-2xl {
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
.rounded-3xl {
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
}
|
||||||
|
.rounded-4xl {
|
||||||
|
border-radius: 2rem;
|
||||||
|
}
|
||||||
|
.rounded-none {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.rounded-full {
|
||||||
|
border-radius: calc(infinity * 1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
$base: 0.25rem;
|
||||||
|
|
||||||
|
@function spacing($step) {
|
||||||
|
@return $step * $base;
|
||||||
|
}
|
||||||
|
|
||||||
|
$spacing-types: (
|
||||||
|
"p": "padding",
|
||||||
|
"m": "margin",
|
||||||
|
);
|
||||||
|
|
||||||
|
$spacing-variants: (
|
||||||
|
"": (""),
|
||||||
|
"x": ("-left", "-right"),
|
||||||
|
"y": ("-top", "-bottom"),
|
||||||
|
"t": ("-top"),
|
||||||
|
"b": ("-bottom"),
|
||||||
|
"l": ("-left"),
|
||||||
|
"r": ("-right"),
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $type-prefix, $type in $spacing-types {
|
||||||
|
@each $dir-suffix, $sides in $spacing-variants {
|
||||||
|
@for $i from 0 through 48 {
|
||||||
|
// Whole step: p-0, p-1, p-2 ...
|
||||||
|
.#{$type-prefix}#{$dir-suffix}-#{$i} {
|
||||||
|
@each $side in $sides {
|
||||||
|
#{$type}#{$side}: spacing($i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Half step: p-0\.5, p-1\.5, p-2\.5 ...
|
||||||
|
// Stop at 47.5 — 48.5 would exceed the scale
|
||||||
|
@if $i < 48 {
|
||||||
|
.#{$type-prefix}#{$dir-suffix}-#{$i}\.5 {
|
||||||
|
@each $side in $sides {
|
||||||
|
#{$type}#{$side}: spacing($i + 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── CSS custom properties ─────────────────────────────────────────────────
|
||||||
|
:root {
|
||||||
|
--spacing: 0.25rem;
|
||||||
|
--container-3xs: 16rem;
|
||||||
|
--container-2xs: 18rem;
|
||||||
|
--container-xs: 20rem;
|
||||||
|
--container-sm: 24rem;
|
||||||
|
--container-md: 28rem;
|
||||||
|
--container-lg: 32rem;
|
||||||
|
--container-xl: 36rem;
|
||||||
|
--container-2xl: 42rem;
|
||||||
|
--container-3xl: 48rem;
|
||||||
|
--container-4xl: 56rem;
|
||||||
|
--container-5xl: 64rem;
|
||||||
|
--container-6xl: 72rem;
|
||||||
|
--container-7xl: 80rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Shared mixin ──────────────────────────────────────────────────────────
|
||||||
|
@mixin set-props($props, $value) {
|
||||||
|
@each $prop in $props {
|
||||||
|
#{$prop}: $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Class types ───────────────────────────────────────────────────────────
|
||||||
|
$class-types: (
|
||||||
|
"w": (width),
|
||||||
|
"h": (height),
|
||||||
|
"size": (width, height),
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─── Numeric: w-0 → w-48, h-0 → h-48, size-0 → size-48 ───────────────────
|
||||||
|
@each $prefix, $props in $class-types {
|
||||||
|
@for $i from 0 through 48 {
|
||||||
|
.#{$prefix}-#{$i} {
|
||||||
|
@include set-props($props, calc(var(--spacing) * #{$i}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Fractions ─────────────────────────────────────────────────────────────
|
||||||
|
$fractions: (
|
||||||
|
"1\\/2": (1, 2),
|
||||||
|
"1\\/3": (1, 3),
|
||||||
|
"2\\/3": (2, 3),
|
||||||
|
"1\\/4": (1, 4),
|
||||||
|
"2\\/4": (2, 4),
|
||||||
|
"3\\/4": (3, 4),
|
||||||
|
"1\\/5": (1, 5),
|
||||||
|
"2\\/5": (2, 5),
|
||||||
|
"3\\/5": (3, 5),
|
||||||
|
"4\\/5": (4, 5),
|
||||||
|
"1\\/6": (1, 6),
|
||||||
|
"2\\/6": (2, 6),
|
||||||
|
"3\\/6": (3, 6),
|
||||||
|
"4\\/6": (4, 6),
|
||||||
|
"5\\/6": (5, 6),
|
||||||
|
"1\\/12": (1, 12),
|
||||||
|
"2\\/12": (2, 12),
|
||||||
|
"3\\/12": (3, 12),
|
||||||
|
"4\\/12": (4, 12),
|
||||||
|
"5\\/12": (5, 12),
|
||||||
|
"6\\/12": (6, 12),
|
||||||
|
"7\\/12": (7, 12),
|
||||||
|
"8\\/12": (8, 12),
|
||||||
|
"9\\/12": (9, 12),
|
||||||
|
"10\\/12": (10, 12),
|
||||||
|
"11\\/12": (11, 12),
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $prefix, $props in $class-types {
|
||||||
|
@each $name, $pair in $fractions {
|
||||||
|
$num: list.nth($pair, 1);
|
||||||
|
$den: list.nth($pair, 2);
|
||||||
|
.#{$prefix}-#{$name} {
|
||||||
|
@include set-props($props, calc(#{$num} / #{$den} * 100%));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Container sizes (w- only) ─────────────────────────────────────────────
|
||||||
|
$containers: (
|
||||||
|
"3xs", "2xs", "xs", "sm", "md", "lg", "xl",
|
||||||
|
"2xl", "3xl", "4xl", "5xl", "6xl", "7xl"
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $name in $containers {
|
||||||
|
.w-#{$name} { width: var(--container-#{$name}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Shared keywords (auto, px, full, min, max, fit) ───────────────────────
|
||||||
|
// These are identical in value across w-, h-, and size-
|
||||||
|
$shared-keywords: (
|
||||||
|
"auto": auto,
|
||||||
|
"px": 1px,
|
||||||
|
"full": 100%,
|
||||||
|
"min": min-content,
|
||||||
|
"max": max-content,
|
||||||
|
"fit": fit-content,
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $prefix, $props in $class-types {
|
||||||
|
@each $name, $value in $shared-keywords {
|
||||||
|
.#{$prefix}-#{$name} {
|
||||||
|
@include set-props($props, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Viewport keywords ─────────────────────────────────────────────────────
|
||||||
|
// dvw/dvh/lvw/lvh/svw/svh apply equally to w-, h-, and size-
|
||||||
|
$viewport-keywords: (
|
||||||
|
"dvw": 100dvw,
|
||||||
|
"dvh": 100dvh,
|
||||||
|
"lvw": 100lvw,
|
||||||
|
"lvh": 100lvh,
|
||||||
|
"svw": 100svw,
|
||||||
|
"svh": 100svh,
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $prefix, $props in $class-types {
|
||||||
|
@each $name, $value in $viewport-keywords {
|
||||||
|
.#{$prefix}-#{$name} {
|
||||||
|
@include set-props($props, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-screen { width: 100vw; }
|
||||||
|
.h-screen { height: 100vh; }
|
||||||
@@ -1,11 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="navbar is-fixed-top has-shadow px-3" role="navigation">
|
<nav class="navbar is-fixed-top has-shadow px-3" role="navigation">
|
||||||
<div class="navbar-brand mr-5">
|
<div class="navbar-brand mr-5">
|
||||||
<span class="navbar-item">
|
<span class="navbar-item is-gap-1">
|
||||||
<SvgIcon v-bind="{ name: 'dot.svg', size: 18, type: 'primary' }"</SvgIcon>
|
<div style="width: 16px; height: 16px" class="has-background-primary rounded-full"></div>
|
||||||
<span class="fsb-20 has-text-primary">{{$dayjs().format('DD/MM')}}</span>
|
<span class="fs-17 font-semibold has-text-primary">{{$dayjs().format('DD/MM')}}</span>
|
||||||
</span>
|
</span>
|
||||||
<a class="navbar-item header-logo" @click="changeTab(leftmenu[0])"></a>
|
<a
|
||||||
|
class="navbar-item p-0 has-text-primary"
|
||||||
|
@click="changeTab(leftmenu[0])"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
style="max-height: none; width: 44px"
|
||||||
|
width="80" height="80" viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="currentColor" d="M40 8C57.6731 8 72 22.3269 72 40C72 57.6731 57.6731 72 40 72C22.3269 72 8 57.6731 8 40C8 22.3269 22.3269 8 40 8ZM15.7285 31.5762V49.2266H21.9854C23.778 49.2266 25.3185 48.8727 26.6055 48.166C27.898 47.4593 28.8887 46.4453 29.5781 45.124C30.2733 43.8025 30.6211 42.2224 30.6211 40.3838C30.6211 38.5511 30.2733 36.9768 29.5781 35.6611C28.8887 34.3455 27.9033 33.3367 26.6221 32.6357C25.3409 31.9292 23.8123 31.5762 22.0371 31.5762H15.7285ZM33.3857 49.2266H45.3135V46.1494H37.1172V41.9355H44.667V38.8584H37.1172V34.6533H45.2793V31.5762H33.3857V49.2266ZM53.3818 49.2266H58.1914L64.2754 31.5762H60.1387L55.8643 44.9863H55.7002L51.4346 31.5762H47.2891L53.3818 49.2266ZM21.8389 34.7734C22.942 34.7734 23.8704 34.9687 24.623 35.3594C25.3756 35.75 25.9411 36.3593 26.3203 37.1865C26.7052 38.0138 26.8984 39.0797 26.8984 40.3838C26.8984 41.6995 26.7053 42.7743 26.3203 43.6074C25.9411 44.4346 25.3726 45.047 24.6143 45.4434C23.8617 45.8339 22.9339 46.0292 21.8311 46.0293H19.4609V34.7734H21.8389Z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
<a
|
<a
|
||||||
role="button"
|
role="button"
|
||||||
class="navbar-burger"
|
class="navbar-burger"
|
||||||
@@ -22,23 +31,23 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-menu" id="navMenu">
|
<div class="navbar-menu" id="navMenu">
|
||||||
<div class="navbar-start" style="min-width: 650px;">
|
<div class="navbar-start is-gap-1 is-align-items-center" style="min-width: 650px;">
|
||||||
<template v-for="(v, i) in leftmenu" :key="i" :id="v.code">
|
<template v-for="(v, i) in leftmenu" :key="i" :id="v.code">
|
||||||
<a class="navbar-item px-2" v-if="!v.submenu" @click="changeTab(v)">
|
<a class="navbar-item rounded-lg is-clipped p-0" v-if="!v.submenu" @click="changeTab(v)">
|
||||||
<span
|
<span :class="[
|
||||||
:class="`fsb-17 ${currentTab.code === v.code ? 'activetab' : ''}`"
|
'px-3 py-1.5 fs-14 font-medium',
|
||||||
style="padding: 3px 4px"
|
currentTab.code === v.code ? 'has-text-primary-50 has-background-primary-95' : 'has-text-grey-30'
|
||||||
>
|
]">
|
||||||
{{ v[lang] }}
|
{{ v[lang] }}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="navbar-item has-dropdown is-hoverable" v-else>
|
<div class="navbar-item has-dropdown is-hoverable" v-else>
|
||||||
<a class="navbar-item px-2" @click="changeTab(v)">
|
<a class="navbar-item px-2" @click="changeTab(v)">
|
||||||
<span
|
<span :class="[
|
||||||
:class="`icon-text ${currentTab.code === v.code ? 'activetab' : ''}`"
|
'px-3 py-1 font-medium',
|
||||||
style="padding: 3px 4px"
|
currentTab.code === v.code ? 'has-text-primary-bold has-background-primary-soft' : 'has-text-grey-30'
|
||||||
>
|
]">
|
||||||
<span class="fsb-16">{{ v[lang] }}</span>
|
<span>{{ v[lang] }}</span>
|
||||||
<SvgIcon
|
<SvgIcon
|
||||||
style="padding-top: 5px"
|
style="padding-top: 5px"
|
||||||
v-bind="{ name: 'down2.svg', type: currentTab.code === v.code ? 'white' : 'dark', size: 15 }"
|
v-bind="{ name: 'down2.svg', type: currentTab.code === v.code ? 'white' : 'dark', size: 15 }"
|
||||||
@@ -48,7 +57,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<div class="navbar-dropdown has-background-light">
|
<div class="navbar-dropdown has-background-light">
|
||||||
<a
|
<a
|
||||||
class="navbar-item has-background-light fs-15 has-text-black py-1 border-bottom"
|
class="navbar-item has-background-light py-1 border-bottom"
|
||||||
v-for="x in v.submenu"
|
v-for="x in v.submenu"
|
||||||
@click="changeTab(v, x)"
|
@click="changeTab(v, x)"
|
||||||
>
|
>
|
||||||
|
|||||||
27
app/components/dashboard/AvatarBox.vue
Normal file
27
app/components/dashboard/AvatarBox.vue
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="fs-13 font-semibold mx-0 px-0 is-flex is-justify-content-center is-align-items-center is-flex-shrink-0 rounded-full size-10"
|
||||||
|
style="border: 1px solid var(--bulma-grey-80)"
|
||||||
|
:style="image && 'border: none'">
|
||||||
|
<div>
|
||||||
|
<span>{{text}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['text', 'image', 'type', 'size']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.cbox {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--grey-100);
|
||||||
|
border: 1px solid hsl(0, 0%, 85%);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,90 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import DashboardHighlightCard from '@/components/dashboard/DashboardHighlightCard.vue';
|
||||||
|
import Delivery from '@/components/dashboard/Delivery.vue';
|
||||||
|
import OrderStatus from '@/components/dashboard/OrderStatus.vue';
|
||||||
|
import RevenueChart from '@/components/dashboard/RevenueChart.vue';
|
||||||
|
import TopCustomers from '@/components/dashboard/TopCustomers.vue';
|
||||||
|
import TopProducts from '@/components/dashboard/TopProducts.vue';
|
||||||
|
import Warnings from '@/components/dashboard/Warnings.vue';
|
||||||
|
|
||||||
|
const highlights = [
|
||||||
|
{
|
||||||
|
name: 'Doanh thu hôm nay',
|
||||||
|
value: '72.5M',
|
||||||
|
color: 'blue',
|
||||||
|
icon: 'material-symbols:attach-money-rounded',
|
||||||
|
subheader: {
|
||||||
|
value: '+12.5%',
|
||||||
|
fluctuation: 'up',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Số đơn hàng',
|
||||||
|
value: '73',
|
||||||
|
color: 'purple',
|
||||||
|
icon: 'material-symbols:shopping-cart-outline-rounded',
|
||||||
|
subheader: {
|
||||||
|
value: '+8 đơn',
|
||||||
|
fluctuation: 'up',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Đơn đang giao',
|
||||||
|
value: '8',
|
||||||
|
color: 'orange',
|
||||||
|
icon: 'material-symbols:delivery-truck-speed-outline-rounded',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Đơn hoàn thành',
|
||||||
|
value: '38',
|
||||||
|
color: 'green',
|
||||||
|
icon: 'material-symbols:check-circle-outline-rounded',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Công nợ phải thu',
|
||||||
|
value: '245M',
|
||||||
|
color: 'red',
|
||||||
|
icon: 'material-symbols:universal-currency-alt-outline-rounded',
|
||||||
|
subheader: {
|
||||||
|
value: '+15M',
|
||||||
|
fluctuation: 'down',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tồn kho',
|
||||||
|
value: '1,250',
|
||||||
|
color: 'cyan',
|
||||||
|
icon: 'material-symbols:box-outline-rounded',
|
||||||
|
subheader: {
|
||||||
|
value: '-45 SP',
|
||||||
|
fluctuation: 'down',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
Dashboard
|
<div>
|
||||||
|
<div class="fixed-grid has-2-cols has-3-cols-tablet has-6-cols-desktop">
|
||||||
|
<div class="grid">
|
||||||
|
<DashboardHighlightCard
|
||||||
|
v-for="highlight in highlights"
|
||||||
|
v-bind="highlight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="fixed-grid has-3-cols">
|
||||||
|
<div class="grid">
|
||||||
|
<div class="cell is-col-span-2">
|
||||||
|
<RevenueChart />
|
||||||
|
</div>
|
||||||
|
<div class="cell">
|
||||||
|
<TopCustomers />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<TopProducts />
|
||||||
|
<OrderStatus />
|
||||||
|
<Delivery />
|
||||||
|
<Warnings />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
41
app/components/dashboard/DashboardHighlightCard.vue
Normal file
41
app/components/dashboard/DashboardHighlightCard.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
name: String,
|
||||||
|
value: String,
|
||||||
|
color: String,
|
||||||
|
icon: String,
|
||||||
|
subheader: Object
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="cell">
|
||||||
|
<div class="card h-full">
|
||||||
|
<div class="card-content is-flex is-gap-0.5 is-justify-content-space-between">
|
||||||
|
<div>
|
||||||
|
<p class="fs-14 has-text-grey mb-1">{{ name }}</p>
|
||||||
|
<p class="fsb-26 mb-1 has-text-black">{{ value }}</p>
|
||||||
|
<div v-if="subheader"
|
||||||
|
:class="[
|
||||||
|
'is-flex is-gap-0.5 is-align-items-center',
|
||||||
|
subheader.fluctuation === 'up' ? 'has-text-green-40' : 'has-text-red-40'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:name="subheader.fluctuation === 'up' ? 'material-symbols:arrow-upward-rounded' :'material-symbols:arrow-downward-rounded'"
|
||||||
|
color="inherit"
|
||||||
|
/>
|
||||||
|
<p class="fs-14">{{ subheader.value }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'rounded-lg size-12 is-flex-shrink-0 is-flex is-justify-content-center is-align-items-center',
|
||||||
|
`has-background-${color}-soft has-text-${color}-40`
|
||||||
|
]"
|
||||||
|
">
|
||||||
|
<Icon :name="icon" :size="28" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
53
app/components/dashboard/Delivery.vue
Normal file
53
app/components/dashboard/Delivery.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<script setup>
|
||||||
|
import Driver from '@/components/dashboard/Driver.vue';
|
||||||
|
|
||||||
|
const drivers = [
|
||||||
|
{
|
||||||
|
name: 'Nguyễn Văn A',
|
||||||
|
status: 'Đang giao',
|
||||||
|
deliveries: 3,
|
||||||
|
deliveries_completed: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Trần Văn B',
|
||||||
|
status: 'Đang giao',
|
||||||
|
deliveries: 2,
|
||||||
|
deliveries_completed: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Lê Thị C',
|
||||||
|
status: 'Hoàn thành',
|
||||||
|
deliveries: 4,
|
||||||
|
deliveries_completed: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Phạm Văn D',
|
||||||
|
status: 'Đang giao',
|
||||||
|
deliveries: 1,
|
||||||
|
deliveries_completed: 0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="fs-17 font-semibold mb-4">Giao nhận & Tài xế</p>
|
||||||
|
<div class="fixed-grid has-3-cols">
|
||||||
|
<div class="grid">
|
||||||
|
<div class="cell is-col-span-2">
|
||||||
|
<div style="border-radius: 0.5rem; overflow: hidden">
|
||||||
|
<NuxtImg src="/map-demo.png" class="w-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cell is-flex is-flex-direction-column is-gap-2">
|
||||||
|
<Driver
|
||||||
|
v-for="driver in drivers"
|
||||||
|
:key="driver.name"
|
||||||
|
v-bind="driver"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
35
app/components/dashboard/Driver.vue
Normal file
35
app/components/dashboard/Driver.vue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<script setup>
|
||||||
|
import AvatarBox from '@/components/dashboard/AvatarBox.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
name: String,
|
||||||
|
status: String,
|
||||||
|
deliveries: Number,
|
||||||
|
deliveries_completed: Number,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="is-flex is-gap-2 fs-14 p-3 rounded-lg"
|
||||||
|
:style="{
|
||||||
|
border: '1px solid var(--bulma-grey-80)',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<AvatarBox :text="name.slice(0, 2)" />
|
||||||
|
<div class="is-flex-grow-1">
|
||||||
|
<div class="is-flex is-gap-1 is-align-items-center">
|
||||||
|
<p>{{ name }}</p>
|
||||||
|
<span :class="['tag', status === 'Đang giao' ? 'is-warning' : 'is-success']">{{ status }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="fs-13 has-text-grey">Đơn: {{ deliveries_completed }}/{{ deliveries }}</p>
|
||||||
|
<progress
|
||||||
|
v-if="deliveries !== deliveries_completed"
|
||||||
|
class="progress is-small is-primary mt-2"
|
||||||
|
style="--bulma-size-small: 0.4rem;"
|
||||||
|
:value="deliveries_completed"
|
||||||
|
:max="deliveries"
|
||||||
|
>
|
||||||
|
</progress>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
54
app/components/dashboard/OrderStatus.vue
Normal file
54
app/components/dashboard/OrderStatus.vue
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<script setup>
|
||||||
|
import OrderStatusCard from '@/components/dashboard/OrderStatusCard.vue';
|
||||||
|
|
||||||
|
const statuses = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
code: 'pending',
|
||||||
|
name: 'Chờ xử lý',
|
||||||
|
value: 12,
|
||||||
|
color: 'orange',
|
||||||
|
icon: 'material-symbols:clock-loader-40'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
code: 'delivering',
|
||||||
|
name: 'Đang giao',
|
||||||
|
value: 8,
|
||||||
|
color: 'blue',
|
||||||
|
icon: 'material-symbols:delivery-truck-speed-outline-rounded'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
code: 'delivered',
|
||||||
|
name: 'Đã giao',
|
||||||
|
value: 15,
|
||||||
|
color: 'purple',
|
||||||
|
icon: 'material-symbols:bucket-check-outline-rounded'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
code: 'completed',
|
||||||
|
name: 'Hoàn thành',
|
||||||
|
value: 38,
|
||||||
|
color: 'green',
|
||||||
|
icon: 'material-symbols:check-circle-outline-rounded'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="card h-full">
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="fs-17 font-semibold mb-4">Trạng thái đơn hàng</p>
|
||||||
|
<div class="fixed-grid has-2-cols-mobile has-4-cols">
|
||||||
|
<div class="grid">
|
||||||
|
<OrderStatusCard
|
||||||
|
v-for="status in statuses"
|
||||||
|
:key="status.name"
|
||||||
|
v-bind="status"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
33
app/components/dashboard/OrderStatusCard.vue
Normal file
33
app/components/dashboard/OrderStatusCard.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
id: Number,
|
||||||
|
code: String,
|
||||||
|
name: String,
|
||||||
|
value: Number,
|
||||||
|
icon: String,
|
||||||
|
color: String,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="cell">
|
||||||
|
<div class="card" :style="{ border: `1px solid var(--bulma-${color}-70)` }">
|
||||||
|
<div class="card-content is-flex is-flex-direction-column is-align-items-center is-gap-1">
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'p-3 is-flex rounded-full',
|
||||||
|
`has-background-${color}-90`,
|
||||||
|
`has-text-${color}-40`,
|
||||||
|
]" >
|
||||||
|
<Icon
|
||||||
|
:name="icon"
|
||||||
|
:size="24"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="is-flex is-flex-direction-column is-align-items-center">
|
||||||
|
<p class="fs-24 font-bold">{{ value }}</p>
|
||||||
|
<p class="has-text-grey">{{ name }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
61
app/components/dashboard/RevenueChart.vue
Normal file
61
app/components/dashboard/RevenueChart.vue
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup>
|
||||||
|
const { $shortenCurrency } = useNuxtApp();
|
||||||
|
const revenueChartOptions = {
|
||||||
|
chart: {
|
||||||
|
type: 'spline'
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: null,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
categories: [
|
||||||
|
'10/4', '11/4', '12/4', '13/4', '14/4', '15/4', '16/4', '17/4', '18/4'
|
||||||
|
],
|
||||||
|
accessibility: {
|
||||||
|
description: 'Dates'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
title: {
|
||||||
|
text: 'Doanh thu'
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
format: '{value}'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
crosshairs: true,
|
||||||
|
shared: true,
|
||||||
|
valueSuffix: ' VNĐ',
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
spline: {
|
||||||
|
marker: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: 'Doanh thu',
|
||||||
|
data: [45000000, 52000000, 48000000, 51000000, 58000000, 61000000, 67500000, 72000000, 69000000],
|
||||||
|
showInLegend: false,
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="is-flex is-justify-content-space-between mb-2">
|
||||||
|
<p style="font-weight: 600; font-size: 18px">Doanh thu theo ngày</p>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button is-primary fs-14">7 ngày</button>
|
||||||
|
<button class="button is-white fs-14">30 ngày</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<highcharts :options="revenueChartOptions" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
23
app/components/dashboard/TopCustomer.vue
Normal file
23
app/components/dashboard/TopCustomer.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup>
|
||||||
|
import AvatarBox from '@/components/dashboard/AvatarBox.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
name: String,
|
||||||
|
order_count: Number,
|
||||||
|
paid: Number,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { $shortenCurrency } = useNuxtApp();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="is-flex is-gap-1 is-justify-content-space-between">
|
||||||
|
<div class="is-flex is-gap-1">
|
||||||
|
<AvatarBox :text="name.slice(0, 2)" />
|
||||||
|
<div>
|
||||||
|
<p>{{ name }}</p>
|
||||||
|
<p class="fs-13 has-text-grey">{{ order_count }} đơn hàng</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="font-semibold">{{ $shortenCurrency(paid) }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
47
app/components/dashboard/TopCustomers.vue
Normal file
47
app/components/dashboard/TopCustomers.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<script setup>
|
||||||
|
import TopCustomer from '@/components/dashboard/TopCustomer.vue';
|
||||||
|
|
||||||
|
|
||||||
|
const customers = [
|
||||||
|
{
|
||||||
|
name: 'Công ty TNHH ABC',
|
||||||
|
order_count: 45,
|
||||||
|
paid: 125000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Siêu thị XYZ',
|
||||||
|
order_count: 38,
|
||||||
|
paid: 98000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Nhà hàng Đông Dương',
|
||||||
|
order_count: 32,
|
||||||
|
paid: 87000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Khách sạn Mường Thanh',
|
||||||
|
order_count: 28,
|
||||||
|
paid: 76000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Cửa hàng Bách Hoá',
|
||||||
|
order_count: 24,
|
||||||
|
paid: 64000000,
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="fs-17 font-semibold mb-4">Top khách hàng</p>
|
||||||
|
<div class="is-flex is-flex-direction-column is-gap-1.5">
|
||||||
|
<TopCustomer
|
||||||
|
v-for="cus in customers"
|
||||||
|
:key="cus.name"
|
||||||
|
v-bind="cus"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
29
app/components/dashboard/TopProduct.vue
Normal file
29
app/components/dashboard/TopProduct.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
name: String,
|
||||||
|
sold_count: Number,
|
||||||
|
revenue: Number,
|
||||||
|
topRevenue: Number,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { $shortenCurrency } = useNuxtApp();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="is-flex is-gap-1 is-justify-content-space-between mb-2">
|
||||||
|
<div>
|
||||||
|
<p>{{ name }}</p>
|
||||||
|
<p class="fs-13 has-text-grey-60">Đã bán {{ sold_count }} sản phẩm</p>
|
||||||
|
</div>
|
||||||
|
<p class="font-semibold">{{ $shortenCurrency(revenue) }}</p>
|
||||||
|
</div>
|
||||||
|
<progress class="progress is-small is-primary" :value="revenue" :max="topRevenue">
|
||||||
|
15%
|
||||||
|
</progress>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.progress {
|
||||||
|
--bulma-size-small: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
48
app/components/dashboard/TopProducts.vue
Normal file
48
app/components/dashboard/TopProducts.vue
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<script setup>
|
||||||
|
import TopProduct from '@/components/dashboard/TopProduct.vue';
|
||||||
|
|
||||||
|
const products = [
|
||||||
|
{
|
||||||
|
name: 'Gạo ST25 - Bao 5kg',
|
||||||
|
sold_count: 1250,
|
||||||
|
revenue: 156000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Nước mắm Phú Quốc - Chai 500ml',
|
||||||
|
sold_count: 980,
|
||||||
|
revenue: 132000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Đường tinh luyện - Bao 1kg',
|
||||||
|
sold_count: 856,
|
||||||
|
revenue: 98000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Dầu ăn Neptune - Chai 1L',
|
||||||
|
sold_count: 742,
|
||||||
|
revenue: 87000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bột mì đa dụng - Bao 1kg',
|
||||||
|
sold_count: 623,
|
||||||
|
revenue: 72000000
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="fs-17 font-semibold mb-4">Top sản phẩm</p>
|
||||||
|
<div class="is-flex is-flex-direction-column is-gap-2">
|
||||||
|
<TopProduct
|
||||||
|
v-for="product in products"
|
||||||
|
:key="product.name"
|
||||||
|
v-bind="{
|
||||||
|
...product,
|
||||||
|
topRevenue: products[0].revenue
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
36
app/components/dashboard/Warning.vue
Normal file
36
app/components/dashboard/Warning.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
name: String,
|
||||||
|
details: String,
|
||||||
|
level: Number,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
|
||||||
|
const color = computed(() => {
|
||||||
|
if (props.level === 1) return 'yellow';
|
||||||
|
if (props.level === 2) return 'orange';
|
||||||
|
if (props.level === 3) return 'red';
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="['card m-0', `has-background-${color}-95`]"
|
||||||
|
:style="{
|
||||||
|
borderColor: `var(--bulma-${color}-70)`,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="card-content p-4 is-flex is-align-items-center is-gap-2">
|
||||||
|
<Icon
|
||||||
|
:name="type === 'time' ? 'material-symbols:clock-loader-40' : 'material-symbols:box-outline-rounded'"
|
||||||
|
:size="20"
|
||||||
|
:class="`has-text-${color}-45`"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<p :class="`has-text-${color}-40`">{{ name }}</p>
|
||||||
|
<p class="fs-13 has-text-grey">{{ details }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
38
app/components/dashboard/Warnings.vue
Normal file
38
app/components/dashboard/Warnings.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script setup>
|
||||||
|
import Warning from '@/components/dashboard/Warning.vue';
|
||||||
|
|
||||||
|
const warnings = [
|
||||||
|
{
|
||||||
|
name: 'Công nợ sắp đến hạn',
|
||||||
|
details: 'Công ty TNHH ABC - 35.000.000đ - Hạn: 25/03/2026',
|
||||||
|
level: 1,
|
||||||
|
type: 'time',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Đơn giao trễ',
|
||||||
|
details: 'Đơn hàng #DH-2156 - Đã trễ 2 giờ - Khách: Siêu thị XYZ',
|
||||||
|
level: 3,
|
||||||
|
type: 'time',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tồn kho thấp',
|
||||||
|
details: 'Gạo ST25 - Chỉ còn 45 bao - Cần nhập thêm',
|
||||||
|
level: 2,
|
||||||
|
type: 'inventory'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="fs-17 font-semibold mb-4">Cảnh báo và thông báo</p>
|
||||||
|
<div class="is-flex is-flex-direction-column is-gap-1">
|
||||||
|
<Warning
|
||||||
|
v-for="warning in warnings"
|
||||||
|
:key="warning.details"
|
||||||
|
v-bind="warning"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,5 +1,43 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import InventoryHighlightCard from '@/components/inventory/InventoryHighlightCard.vue';
|
||||||
|
|
||||||
|
const inventoryHighlights = [
|
||||||
|
{
|
||||||
|
name: 'Tổng số SKU',
|
||||||
|
value: 12,
|
||||||
|
icon: 'material-symbols:deployed-code-outline',
|
||||||
|
color: 'blue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tổng tồn kho',
|
||||||
|
value: '1,120',
|
||||||
|
icon: 'material-symbols:box-outline-rounded',
|
||||||
|
color: 'green',
|
||||||
|
unit: 'đơn vị'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Giá trị tồn kho',
|
||||||
|
value: '294.5M',
|
||||||
|
icon: 'material-symbols:attach-money-rounded',
|
||||||
|
color: 'purple',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Sắp hết hàng',
|
||||||
|
value: 3,
|
||||||
|
icon: 'material-symbols:warning-outline-rounded',
|
||||||
|
color: 'red',
|
||||||
|
unit: 'sản phẩm'
|
||||||
|
},
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
Inventory
|
<div class="fixed-grid has-2-cols-mobile has-4-cols">
|
||||||
|
<div class="grid">
|
||||||
|
<InventoryHighlightCard
|
||||||
|
v-for="highlight in inventoryHighlights"
|
||||||
|
:key="highlight.name"
|
||||||
|
v-bind="highlight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
28
app/components/inventory/InventoryHighlightCard.vue
Normal file
28
app/components/inventory/InventoryHighlightCard.vue
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
name: String,
|
||||||
|
value: String,
|
||||||
|
unit: String,
|
||||||
|
icon: String,
|
||||||
|
color: String
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="cell">
|
||||||
|
<div class="card h-full">
|
||||||
|
<div class="card-content is-flex is-justify-content-space-between is-align-items-start is-gap-1">
|
||||||
|
<div>
|
||||||
|
<p class="fs-14 has-text-grey">{{ name }}</p>
|
||||||
|
<p class="fs-22 font-bold">{{ value }}</p>
|
||||||
|
</div>
|
||||||
|
<div :class="['p-3 rounded-lg is-flex is-justify-content-center is-align-items-center', `has-background-${color}-soft`]">
|
||||||
|
<Icon
|
||||||
|
:name="icon"
|
||||||
|
:class="`has-text-${color}-40`"
|
||||||
|
:size="24"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
34
app/components/orders/OrderHighlightCard.vue
Normal file
34
app/components/orders/OrderHighlightCard.vue
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
name: String,
|
||||||
|
value: Number,
|
||||||
|
icon: String,
|
||||||
|
color: String,
|
||||||
|
unit: String
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="cell">
|
||||||
|
<div class="card h-full">
|
||||||
|
<div class="card-content is-flex is-gap-1 is-justify-content-space-between is-align-items-center">
|
||||||
|
<div>
|
||||||
|
<p class="has-text-grey">{{ name }}</p>
|
||||||
|
<div class="is-flex is-gap-1 is-align-items-center">
|
||||||
|
<p class="fs-26 font-bold">{{ value }}</p>
|
||||||
|
<span class="fs-13 has-text-grey">{{ unit }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
:class="['p-3 is-flex', `has-background-${color}-soft`]" style="border-radius: 8px"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:name="icon"
|
||||||
|
:size="26"
|
||||||
|
:class="`has-text-${color}-40`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
53
app/components/orders/OrderPipeline.vue
Normal file
53
app/components/orders/OrderPipeline.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<script setup>
|
||||||
|
import PipelinePhase from '@/components/orders/PipelinePhase.vue';
|
||||||
|
|
||||||
|
const phases = [
|
||||||
|
{
|
||||||
|
name: 'Nháp',
|
||||||
|
value: 2,
|
||||||
|
icon: 'material-symbols:assignment-outline-rounded',
|
||||||
|
color: 'yellow',
|
||||||
|
index: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Đã xác nhận',
|
||||||
|
value: 3,
|
||||||
|
icon: 'material-symbols:check-circle-outline-rounded',
|
||||||
|
color: 'blue',
|
||||||
|
index: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Đang giao',
|
||||||
|
value: 2,
|
||||||
|
icon: 'material-symbols:delivery-truck-speed-outline-rounded',
|
||||||
|
color: 'orange',
|
||||||
|
index: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Hoàn thành',
|
||||||
|
value: 1,
|
||||||
|
icon: 'material-symbols:box-outline-rounded',
|
||||||
|
color: 'green',
|
||||||
|
index: 4,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="fs-17 font-semibold mb-4">Pipeline đơn hàng</p>
|
||||||
|
<div class="is-flex is-justify-content-space-evenly">
|
||||||
|
<PipelinePhase
|
||||||
|
v-for="phase in phases"
|
||||||
|
:key="phases.name"
|
||||||
|
v-bind="phase"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<hr class="has-background-grey-lighter" />
|
||||||
|
<div class="is-flex is-justify-content-space-between">
|
||||||
|
<p>Tổng đơn hàng</p>
|
||||||
|
<p class="fs-18 font-semibold">{{ phases.reduce((prev, curr) => prev + curr.value, 0) }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
85
app/components/orders/OrderRow.vue
Normal file
85
app/components/orders/OrderRow.vue
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
id: Number,
|
||||||
|
code: String,
|
||||||
|
employee: Number,
|
||||||
|
employee__name: String,
|
||||||
|
customer: Number,
|
||||||
|
customer__name: String,
|
||||||
|
customer__phone: String,
|
||||||
|
total: String,
|
||||||
|
status: Number,
|
||||||
|
status__name: String,
|
||||||
|
status__color: String,
|
||||||
|
payment_status: Number,
|
||||||
|
payment_status__name: String,
|
||||||
|
payment_status__color: String,
|
||||||
|
delivery_status: Number,
|
||||||
|
delivery_status__name: String,
|
||||||
|
delivery_status__color: String,
|
||||||
|
order__products: Array,
|
||||||
|
create_time: String
|
||||||
|
});
|
||||||
|
|
||||||
|
const { $dayjs, $shortenCurrency } = useNuxtApp();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
<p class="fs-15 font-semibold">{{ code }}</p>
|
||||||
|
<p class="fs-13 has-text-grey">{{ employee__name }}</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
<p>{{ customer__name }}</p>
|
||||||
|
<div class="is-flex is-gap-0.5 is-align-items-center mt-1 fs-13 has-text-grey">
|
||||||
|
<Icon name="material-symbols:call-outline-rounded"
|
||||||
|
:size="15"
|
||||||
|
/>
|
||||||
|
<span class="fs-12">{{ customer__phone }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="has-text-right">
|
||||||
|
<div>
|
||||||
|
<p class="font-semibold">{{ $shortenCurrency(total) }}</p>
|
||||||
|
<p class="fs-13 has-text-grey">{{ order__products.length }} SP</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="has-text-centered">
|
||||||
|
<span :class="[
|
||||||
|
'tag rounded-full',
|
||||||
|
`has-background-${status__color}-80 has-text-${status__color}-25`
|
||||||
|
]">
|
||||||
|
{{ status__name }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p :class="`has-text-${payment_status__color}-40`">{{ payment_status__name }}</p>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p :class="`has-text-${delivery_status__color}-40`">{{ delivery_status__name }}</p>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
<div class="is-flex is-gap-0.5">
|
||||||
|
<Icon :size="18" name="material-symbols:calendar-month-outline-rounded" />
|
||||||
|
<span class="fs-13">{{ $dayjs(create_time).format('L') }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="has-text-grey fs-12">{{ $dayjs(create_time).format('HH:mm') }}</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="button is-dark fs-13 rounded-full">Xác nhận</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
td {
|
||||||
|
vertical-align: middle;
|
||||||
|
--bulma-table-cell-padding: 0.75em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,54 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import OrderHighlightCard from '@/components/orders/OrderHighlightCard.vue';
|
||||||
|
import OrderPipeline from '@/components/orders/OrderPipeline.vue';
|
||||||
|
import OrdersTable from '@/components/orders/OrdersTable.vue';
|
||||||
|
|
||||||
|
const highlights = [
|
||||||
|
{
|
||||||
|
name: 'Nháp',
|
||||||
|
value: 2,
|
||||||
|
icon: 'material-symbols:assignment-outline-rounded',
|
||||||
|
color: 'yellow',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Đã xác nhận',
|
||||||
|
value: 3,
|
||||||
|
icon: 'material-symbols:check-circle-outline-rounded',
|
||||||
|
color: 'blue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Đang giao',
|
||||||
|
value: 2,
|
||||||
|
icon: 'material-symbols:delivery-truck-speed-outline-rounded',
|
||||||
|
color: 'orange',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Hoàn thành',
|
||||||
|
value: 1,
|
||||||
|
icon: 'material-symbols:box-outline-rounded',
|
||||||
|
color: 'green',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Doanh thu',
|
||||||
|
value: '6.8M',
|
||||||
|
icon: 'material-symbols:attach-money-rounded',
|
||||||
|
color: 'purple',
|
||||||
|
unit: 'VNĐ'
|
||||||
|
},
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
Orders
|
<div>
|
||||||
|
<div class="fixed-grid has-2-cols-mobile has-5-cols">
|
||||||
|
<div class="grid">
|
||||||
|
<OrderHighlightCard
|
||||||
|
v-for="highlight in highlights"
|
||||||
|
:key="highlight.name"
|
||||||
|
v-bind="highlight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<OrderPipeline />
|
||||||
|
<OrdersTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
231
app/components/orders/OrdersTable.vue
Normal file
231
app/components/orders/OrdersTable.vue
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
<script setup>
|
||||||
|
import OrderRow from '@/components/orders/OrderRow.vue';
|
||||||
|
|
||||||
|
const orders = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
code: 'SO001',
|
||||||
|
employee: 1,
|
||||||
|
employee__name: 'Trần Thị B',
|
||||||
|
customer: 1,
|
||||||
|
customer__name: 'Nguyễn Văn A',
|
||||||
|
customer__phone: '0901234567',
|
||||||
|
total: '5200000.00',
|
||||||
|
status: 1,
|
||||||
|
status__name: 'Nháp',
|
||||||
|
status__color: 'yellow',
|
||||||
|
payment_status: 1,
|
||||||
|
payment_status__name: 'Chưa thanh toán',
|
||||||
|
payment_status__color: 'red',
|
||||||
|
delivery_status: 1,
|
||||||
|
delivery_status__name: 'Chờ xử lý',
|
||||||
|
delivery_status__color: 'grey',
|
||||||
|
order__products: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Kem chống nắng SPF 50+',
|
||||||
|
unit_price: '280000.00',
|
||||||
|
quantity: 10,
|
||||||
|
discount: null,
|
||||||
|
total: '2800000.00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'Serum Vitamin C 30ml',
|
||||||
|
unit_price: '450000.00',
|
||||||
|
quantity: 5,
|
||||||
|
discount: 0.1,
|
||||||
|
total: '2025000.00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'Son dưỡng môi vitamin E',
|
||||||
|
unit_price: '75000.00',
|
||||||
|
quantity: 5,
|
||||||
|
discount: null,
|
||||||
|
total: '375000.00'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
create_time: '2026-02-11T08:51:04.587660+07:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
code: 'SO002',
|
||||||
|
employee: 1,
|
||||||
|
employee__name: 'Trần Thị B',
|
||||||
|
customer: 3,
|
||||||
|
customer__name: 'Lê Thị C',
|
||||||
|
customer__phone: '0912345678',
|
||||||
|
total: '8500000.00',
|
||||||
|
status: 2,
|
||||||
|
status__name: 'Đã xác nhận',
|
||||||
|
status__color: 'blue',
|
||||||
|
payment_status: 2,
|
||||||
|
payment_status__name: 'Một phần',
|
||||||
|
payment_status__color: 'orange',
|
||||||
|
delivery_status: 2,
|
||||||
|
delivery_status__name: 'Sẵn sàng',
|
||||||
|
delivery_status__color: 'blue',
|
||||||
|
order__products: [
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'Mặt nạ collagen 10 miếng',
|
||||||
|
unit_price: '320000.00',
|
||||||
|
quantity: 15,
|
||||||
|
discount: 0.05,
|
||||||
|
total: '4560000.00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: 'Toner cân bằng da 200ml',
|
||||||
|
unit_price: '180000.00',
|
||||||
|
quantity: 20,
|
||||||
|
discount: 0.1,
|
||||||
|
total: '3240000.00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: 'Gel rửa mặt làm sạch sâu',
|
||||||
|
unit_price: '140000.00',
|
||||||
|
quantity: 5,
|
||||||
|
discount: null,
|
||||||
|
total: '700000.00'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
create_time: '2026-04-06T09:10:09.587660+07:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
code: 'SO003',
|
||||||
|
employee: 5,
|
||||||
|
employee__name: 'Hoàng Văn E',
|
||||||
|
customer: 4,
|
||||||
|
customer__name: 'Phạm Văn D',
|
||||||
|
customer__phone: '0923456703',
|
||||||
|
total: '12300000.00',
|
||||||
|
status: 3,
|
||||||
|
status__name: 'Đang giao',
|
||||||
|
status__color: 'orange',
|
||||||
|
payment_status: 3,
|
||||||
|
payment_status__name: 'Đã thanh toán',
|
||||||
|
payment_status__color: 'green',
|
||||||
|
delivery_status: 3,
|
||||||
|
delivery_status__name: 'Hoàn thành',
|
||||||
|
delivery_status__color: 'green',
|
||||||
|
order__products: [
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: 'Kem dưỡng ẩm ban đêm',
|
||||||
|
unit_price: '380000.00',
|
||||||
|
quantity: 20,
|
||||||
|
discount: 0.05,
|
||||||
|
total: '7220000.00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
name: 'Phấn nền BB cream',
|
||||||
|
unit_price: '220000.00',
|
||||||
|
quantity: 15,
|
||||||
|
discount: null,
|
||||||
|
total: '3300000.00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
name: 'Nước tẩy trang 3 trong 1',
|
||||||
|
unit_price: '195000.00',
|
||||||
|
quantity: 10,
|
||||||
|
discount: 0.1,
|
||||||
|
total: '1755000.00'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
create_time: '2026-04-05T02:33:24.587660+07:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
code: 'SO004',
|
||||||
|
employee: 1,
|
||||||
|
employee__name: 'Trần Thị B',
|
||||||
|
customer: 6,
|
||||||
|
customer__name: 'Vũ Thị F',
|
||||||
|
customer__phone: '0934835222',
|
||||||
|
total: '6800000.00',
|
||||||
|
status: 4,
|
||||||
|
status__name: 'Hoàn thành',
|
||||||
|
status__color: 'green',
|
||||||
|
payment_status: 3,
|
||||||
|
payment_status__name: 'Đã thanh toán',
|
||||||
|
payment_status__color: 'green',
|
||||||
|
delivery_status: 3,
|
||||||
|
delivery_status__name: 'Hoàn thành',
|
||||||
|
delivery_status__color: 'green',
|
||||||
|
order__products: [
|
||||||
|
{
|
||||||
|
id: 9,
|
||||||
|
name: 'Dầu gội thảo mộc 500ml',
|
||||||
|
unit_price: '120000.00',
|
||||||
|
quantity: 30,
|
||||||
|
discount: 0.1,
|
||||||
|
total: '3240000.00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
name: 'Sữa rửa mặt trà xanh 150ml',
|
||||||
|
unit_price: '150000.00',
|
||||||
|
quantity: 20,
|
||||||
|
discount: 0.05,
|
||||||
|
total: '2850000.00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
name: 'Son dưỡng môi vitamin E',
|
||||||
|
unit_price: '75000.00',
|
||||||
|
quantity: 10,
|
||||||
|
discount: 0.05,
|
||||||
|
total: '712500.00'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
create_time: '2026-04-04T23:21:11.587660+07:00'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content p-0">
|
||||||
|
<p class="p-5 fs-17 font-semibold is-flex is-align-items-center is-gap-1">
|
||||||
|
<Icon name="material-symbols:list-alt-outline-rounded" :size="22" />
|
||||||
|
<span>Danh sách đơn hàng ({{ orders.length }})</span>
|
||||||
|
</p>
|
||||||
|
<table class="table is-fullwidth is-hoverable fs-14">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Đơn hàng</th>
|
||||||
|
<th>Khách hàng</th>
|
||||||
|
<th class="has-text-right">Tổng tiền</th>
|
||||||
|
<th class="has-text-centered">Trạng thái</th>
|
||||||
|
<th>Thanh toán</th>
|
||||||
|
<th>Giao hàng</th>
|
||||||
|
<th>Ngày tạo</th>
|
||||||
|
<th>Thao tác</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<OrderRow
|
||||||
|
v-for="order in orders"
|
||||||
|
:key="order.id"
|
||||||
|
v-bind="order"
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
th {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
44
app/components/orders/PipelinePhase.vue
Normal file
44
app/components/orders/PipelinePhase.vue
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
name: String,
|
||||||
|
value: Number,
|
||||||
|
icon: String,
|
||||||
|
color: String,
|
||||||
|
index: Number,
|
||||||
|
});
|
||||||
|
|
||||||
|
const progressUnfilled = computed(() => `var(--bulma-${props.color}-85)`);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="is-flex-grow-1">
|
||||||
|
<div class="is-flex is-justify-content-space-between">
|
||||||
|
<p :class="`has-text-${color}-40`">{{ name }}</p>
|
||||||
|
<p :class="['fs-18 font-bold', `has-text-${color}-30`]">{{ value }}</p>
|
||||||
|
</div>
|
||||||
|
<progress
|
||||||
|
:class="['progress is-small mt-2', `is-${color}`]"
|
||||||
|
value="20"
|
||||||
|
max="100"
|
||||||
|
>
|
||||||
|
</progress>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="index < 4"
|
||||||
|
class="is-flex is-justify-content-center is-align-items-center"
|
||||||
|
style="width: 60px; height: 48px"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="material-symbols:arrow-forward-rounded"
|
||||||
|
:size="24"
|
||||||
|
class="has-text-grey-70"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$progress-value-background-color: 'red';
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
--bulma-size-small: 0.5rem;
|
||||||
|
background-color: v-bind(progressUnfilled);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
style="min-height: 100vh"
|
style="min-height: 100vh; background-color: #f9fafb;"
|
||||||
class="has-background-white"
|
|
||||||
data-theme="light"
|
data-theme="light"
|
||||||
lang="vi"
|
lang="vi"
|
||||||
>
|
>
|
||||||
@@ -48,7 +47,7 @@ async function checkRedirect() {
|
|||||||
$store.commit("login", row);
|
$store.commit("login", row);
|
||||||
}
|
}
|
||||||
} else if (!$store.login) return /* $requestLogin(); */
|
} else if (!$store.login) return /* $requestLogin(); */
|
||||||
await checkLogin();
|
// await checkLogin();
|
||||||
}
|
}
|
||||||
async function checkLogin() {
|
async function checkLogin() {
|
||||||
if ($store.login ? $store.login.token : false) {
|
if ($store.login ? $store.login.token : false) {
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
<TopMenu @changetab="changeTab" />
|
<TopMenu @changetab="changeTab" />
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<div class="container blockdiv" style="padding-bottom: 0">
|
<div class="container blockdiv has-text-text-20">
|
||||||
<div class="fsb-18 mb-2 has-text-black" v-if="tab">
|
<div class="fs-17 font-semibold mb-2 is-flex is-align-items-center is-gap-1" v-if="tab">
|
||||||
<template v-if="subtab">
|
<template v-if="subtab">
|
||||||
<span>{{ tab[$store.lang] }}</span>
|
<span>{{ tab[$store.lang] }}</span>
|
||||||
<SvgIcon class="mx-2" v-bind="{ name: 'right.svg', size: 17, type: 'has-text-black' }"></SvgIcon>
|
<SvgIcon class="mx-2" v-bind="{ name: 'right.svg', size: 17, type: 'has-text-black' }"></SvgIcon>
|
||||||
<span>{{ subtab[$store.lang] }}</span>
|
<span>{{ subtab[$store.lang] }}</span>
|
||||||
</template>
|
</template>
|
||||||
<span v-else>{{ tab[$store.lang] }}</span>
|
<span v-else>{{ tab[$store.lang] }}</span>
|
||||||
<a class="ml-3" @click="refresh()">
|
<button class="button is-primary is-light rounded-full p-1" @click="refresh()">
|
||||||
<SvgIcon v-bind="{name: 'refresh.svg', type: 'primary', size: 20}"></SvgIcon>
|
<Icon name="material-symbols:refresh-rounded" :size="24" />
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<KeepAlive>
|
<KeepAlive>
|
||||||
<component :is="componentMap[vbind.component]" v-bind="vbind" :key="componentKey" v-if="componentKey" />
|
<component :is="componentMap[vbind.component]" v-bind="vbind" :key="componentKey" v-if="componentKey" />
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import dayjs from 'dayjs';
|
|||||||
import weekday from 'dayjs/plugin/weekday';
|
import weekday from 'dayjs/plugin/weekday';
|
||||||
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
|
import localizedFormat from 'dayjs/plugin/localizedFormat'
|
||||||
import 'dayjs/locale/vi';
|
import 'dayjs/locale/vi';
|
||||||
dayjs.extend(weekday);
|
dayjs.extend(weekday);
|
||||||
dayjs.extend(weekOfYear);
|
dayjs.extend(weekOfYear);
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
dayjs.extend(localizedFormat);
|
||||||
dayjs.locale('vi');
|
dayjs.locale('vi');
|
||||||
|
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin(() => {
|
||||||
@@ -608,6 +609,12 @@ export default defineNuxtPlugin(() => {
|
|||||||
return `https://img.vietqr.io/image/${bankInfo.bank.code}-${bankInfo.account.number}-print.png?${params.toString()}`;
|
return `https://img.vietqr.io/image/${bankInfo.bank.code}-${bankInfo.account.number}-print.png?${params.toString()}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function shortenCurrency(amount) {
|
||||||
|
return new Intl.NumberFormat('en', { notation: 'compact' }).format(
|
||||||
|
Number(amount),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
provide: {
|
provide: {
|
||||||
dialog,
|
dialog,
|
||||||
@@ -636,6 +643,7 @@ export default defineNuxtPlugin(() => {
|
|||||||
formatDateVN,
|
formatDateVN,
|
||||||
getFirstAndLastName,
|
getFirstAndLastName,
|
||||||
paymentQR,
|
paymentQR,
|
||||||
|
shortenCurrency
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
10293
my-bulma-project.css
10293
my-bulma-project.css
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,53 +1,32 @@
|
|||||||
$blue-dianne: #204853;
|
$red: #ff2e4e;
|
||||||
$parchment: #fefefe;
|
$orange: #fd8206;
|
||||||
$sirocco: #758385;
|
$yellow: #eec704;
|
||||||
$delta: #b0afaa;
|
$green: #16d164;
|
||||||
$cutty-sark: #566c72;
|
$cyan: #03c2c2;
|
||||||
$silver-rust: #ccc5bc;
|
$blue: #1678ff;
|
||||||
$fiord: #3c5b63;
|
$purple: #833bff;
|
||||||
$pewter: #959b99;
|
$pink: #ff2bdd;
|
||||||
$pearl-bush: #ffffff;
|
$grey: #767676;
|
||||||
|
|
||||||
// Set your brand colors
|
|
||||||
$primary: $blue-dianne;
|
|
||||||
$secondary: $fiord;
|
|
||||||
$accent: $parchment;
|
|
||||||
$findata: $sirocco;
|
|
||||||
$danger: #f14668;
|
|
||||||
$dark: $cutty-sark;
|
|
||||||
$light: $pearl-bush;
|
|
||||||
$twitter: $pewter;
|
|
||||||
$warning: $cutty-sark;
|
|
||||||
$info: $sirocco;
|
|
||||||
$success: $fiord;
|
|
||||||
|
|
||||||
// Path to Bulma's sass folder
|
|
||||||
@use "bulma/sass" with (
|
@use "bulma/sass" with (
|
||||||
$family-primary: 'Arial, sans-serif',
|
$family-primary: "'Inter', 'SF Pro', 'Helvetica', 'Arial', sans-serif",
|
||||||
$primary: $primary,
|
$primary: $blue,
|
||||||
$info: $info,
|
$link: $blue,
|
||||||
$success: $success,
|
$info: $cyan,
|
||||||
$warning: $warning,
|
$success: $green,
|
||||||
$danger: $danger,
|
$warning: $yellow,
|
||||||
$body-color: $sitecolor,
|
$danger: $red,
|
||||||
$text: $sitecolor,
|
$custom-colors: (
|
||||||
$link: $blue-dianne,
|
"red": $red,
|
||||||
$link-hover: $fiord
|
"orange": $orange,
|
||||||
|
"yellow": $yellow,
|
||||||
|
"green": $green,
|
||||||
|
"cyan": $cyan,
|
||||||
|
"blue": $blue,
|
||||||
|
"purple": $purple,
|
||||||
|
"pink": $pink,
|
||||||
|
"grey": $grey
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
a {
|
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900');
|
||||||
color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: $primary !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
strong, label {
|
|
||||||
color: $sitecolor !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hyperlink:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
color: $primary !important;
|
|
||||||
}
|
|
||||||
@@ -10,8 +10,7 @@ export default defineNuxtConfig({
|
|||||||
script: [{ src: "/js/html2pdf.bundle.min.js" }, { src: "/js/html2canvas.min.js" }],
|
script: [{ src: "/js/html2pdf.bundle.min.js" }, { src: "/js/html2canvas.min.js" }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
modules: ["@pinia/nuxt", "pinia-plugin-persistedstate/nuxt", "@nuxt/image","nuxt-qrcode"],
|
modules: ["@pinia/nuxt", "pinia-plugin-persistedstate/nuxt", "@nuxt/image","nuxt-qrcode", "@nuxt/icon"],
|
||||||
|
|
||||||
compatibilityDate: "2025-10-01",
|
compatibilityDate: "2025-10-01",
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: true },
|
||||||
});
|
});
|
||||||
|
|||||||
295
package-lock.json
generated
295
package-lock.json
generated
@@ -18,7 +18,7 @@
|
|||||||
"@vueup/vue-quill": "^1.2.0",
|
"@vueup/vue-quill": "^1.2.0",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"bowser": "^2.11.0",
|
"bowser": "^2.11.0",
|
||||||
"bulma": "^1.0.2",
|
"bulma": "^1.0.4",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"docx-preview": "^0.3.6",
|
"docx-preview": "^0.3.6",
|
||||||
@@ -44,10 +44,24 @@
|
|||||||
"vue3-quill": "^0.3.1"
|
"vue3-quill": "^0.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@nuxt/icon": "^2.2.1",
|
||||||
"nuxt": "^4.2.0",
|
"nuxt": "^4.2.0",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@antfu/install-pkg": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"package-manager-detector": "^1.3.0",
|
||||||
|
"tinyexec": "^1.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@aps_sdk/authentication": {
|
"node_modules/@aps_sdk/authentication": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aps_sdk/authentication/-/authentication-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aps_sdk/authentication/-/authentication-1.0.0.tgz",
|
||||||
@@ -1113,6 +1127,47 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@iconify/collections": {
|
||||||
|
"version": "1.0.670",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iconify/collections/-/collections-1.0.670.tgz",
|
||||||
|
"integrity": "sha512-32kW4oJ+QV1HCndcjwAprDyJJ0+T9ahP4y2lmLfekdGWNMwG1pVZah4biqT+HrZdEiBm8InJrbCvaitIusTE3g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@iconify/types": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@iconify/types": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@iconify/utils": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@antfu/install-pkg": "^1.1.0",
|
||||||
|
"@iconify/types": "^2.0.0",
|
||||||
|
"mlly": "^1.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@iconify/vue": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iconify/vue/-/vue-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@iconify/types": "^2.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/cyberalien"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": ">=3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@img/colour": {
|
"node_modules/@img/colour": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
|
||||||
@@ -2208,6 +2263,94 @@
|
|||||||
"node": "^18.17.0 || >=20.5.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nuxt/icon": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nuxt/icon/-/icon-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-GI840yYGuvHI0BGDQ63d6rAxGzG96jQcWrnaWIQKlyQo/7sx9PjXkSHckXUXyX1MCr9zY6U25Td6OatfY6Hklw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@iconify/collections": "^1.0.641",
|
||||||
|
"@iconify/types": "^2.0.0",
|
||||||
|
"@iconify/utils": "^3.1.0",
|
||||||
|
"@iconify/vue": "^5.0.0",
|
||||||
|
"@nuxt/devtools-kit": "^3.1.1",
|
||||||
|
"@nuxt/kit": "^4.2.2",
|
||||||
|
"consola": "^3.4.2",
|
||||||
|
"local-pkg": "^1.1.2",
|
||||||
|
"mlly": "^1.8.0",
|
||||||
|
"ohash": "^2.0.11",
|
||||||
|
"pathe": "^2.0.3",
|
||||||
|
"picomatch": "^4.0.3",
|
||||||
|
"std-env": "^3.10.0",
|
||||||
|
"tinyglobby": "^0.2.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nuxt/icon/node_modules/@nuxt/devtools-kit": {
|
||||||
|
"version": "3.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nuxt/devtools-kit/-/devtools-kit-3.2.4.tgz",
|
||||||
|
"integrity": "sha512-Yxy2Xgmq5hf3dQy983V0xh0OJV2mYwRZz9eVIGc3EaribdFGPDNGMMbYqX9qCty3Pbxn/bCF3J0UyPaNlHVayQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@nuxt/kit": "^4.4.2",
|
||||||
|
"execa": "^8.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vite": ">=6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nuxt/icon/node_modules/@nuxt/kit": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-5+IPRNX2CjkBhuWUwz0hBuLqiaJPRoKzQ+SvcdrQDbAyE+VDeFt74VpSFr5/R0ujrK4b+XnSHUJWdS72w6hsog==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"c12": "^3.3.3",
|
||||||
|
"consola": "^3.4.2",
|
||||||
|
"defu": "^6.1.4",
|
||||||
|
"destr": "^2.0.5",
|
||||||
|
"errx": "^0.1.0",
|
||||||
|
"exsolve": "^1.0.8",
|
||||||
|
"ignore": "^7.0.5",
|
||||||
|
"jiti": "^2.6.1",
|
||||||
|
"klona": "^2.0.6",
|
||||||
|
"mlly": "^1.8.1",
|
||||||
|
"ohash": "^2.0.11",
|
||||||
|
"pathe": "^2.0.3",
|
||||||
|
"pkg-types": "^2.3.0",
|
||||||
|
"rc9": "^3.0.0",
|
||||||
|
"scule": "^1.3.0",
|
||||||
|
"semver": "^7.7.4",
|
||||||
|
"tinyglobby": "^0.2.15",
|
||||||
|
"ufo": "^1.6.3",
|
||||||
|
"unctx": "^2.5.0",
|
||||||
|
"untyped": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nuxt/icon/node_modules/picomatch": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nuxt/icon/node_modules/rc9": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rc9/-/rc9-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"defu": "^6.1.6",
|
||||||
|
"destr": "^2.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nuxt/image": {
|
"node_modules/@nuxt/image": {
|
||||||
"version": "1.11.0",
|
"version": "1.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@nuxt/image/-/image-1.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@nuxt/image/-/image-1.11.0.tgz",
|
||||||
@@ -5715,10 +5858,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.15.0",
|
"version": "8.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -6877,8 +7019,7 @@
|
|||||||
"node_modules/bulma": {
|
"node_modules/bulma": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/bulma/-/bulma-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/bulma/-/bulma-1.0.4.tgz",
|
||||||
"integrity": "sha512-Ffb6YGXDiZYX3cqvSbHWqQ8+LkX6tVoTcZuVB3lm93sbAVXlO0D6QlOTMnV6g18gILpAXqkG2z9hf9z4hCjz2g==",
|
"integrity": "sha512-Ffb6YGXDiZYX3cqvSbHWqQ8+LkX6tVoTcZuVB3lm93sbAVXlO0D6QlOTMnV6g18gILpAXqkG2z9hf9z4hCjz2g=="
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/bundle-name": {
|
"node_modules/bundle-name": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
@@ -6897,23 +7038,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/c12": {
|
"node_modules/c12": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/c12/-/c12-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/c12/-/c12-3.3.4.tgz",
|
||||||
"integrity": "sha512-QkikB2X5voO1okL3QsES0N690Sn/K9WokXqUsDQsWy5SnYb+psYQFGA10iy1bZHj3fjISKsI67Q90gruvWWM3A==",
|
"integrity": "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.3",
|
"chokidar": "^5.0.0",
|
||||||
"confbox": "^0.2.2",
|
"confbox": "^0.2.4",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.6",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.3.1",
|
||||||
"exsolve": "^1.0.8",
|
"exsolve": "^1.0.8",
|
||||||
"giget": "^2.0.0",
|
"giget": "^3.2.0",
|
||||||
"jiti": "^2.6.1",
|
"jiti": "^2.6.1",
|
||||||
"ohash": "^2.0.11",
|
"ohash": "^2.0.11",
|
||||||
"pathe": "^2.0.3",
|
"pathe": "^2.0.3",
|
||||||
"perfect-debounce": "^2.0.0",
|
"perfect-debounce": "^2.1.0",
|
||||||
"pkg-types": "^2.3.0",
|
"pkg-types": "^2.3.0",
|
||||||
"rc9": "^2.1.2"
|
"rc9": "^3.0.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"magicast": "*"
|
"magicast": "*"
|
||||||
@@ -6924,6 +7064,49 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/c12/node_modules/chokidar": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
|
||||||
|
"dependencies": {
|
||||||
|
"readdirp": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20.19.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/c12/node_modules/giget": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/giget/-/giget-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-GvHTWcykIR/fP8cj8dMpuMMkvaeJfPvYnhq0oW+chSeIr+ldX21ifU2Ms6KBoyKZQZmVaUAAhQ2EZ68KJF8a7A==",
|
||||||
|
"bin": {
|
||||||
|
"giget": "dist/cli.mjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/c12/node_modules/rc9": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rc9/-/rc9-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"defu": "^6.1.6",
|
||||||
|
"destr": "^2.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/c12/node_modules/readdirp": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20.19.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cac": {
|
"node_modules/cac": {
|
||||||
"version": "6.7.14",
|
"version": "6.7.14",
|
||||||
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||||
@@ -7914,10 +8097,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/confbox": {
|
"node_modules/confbox": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz",
|
||||||
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
|
"integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/config-chain": {
|
"node_modules/config-chain": {
|
||||||
"version": "1.1.13",
|
"version": "1.1.13",
|
||||||
@@ -8679,10 +8861,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/defu": {
|
"node_modules/defu": {
|
||||||
"version": "6.1.4",
|
"version": "6.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
|
||||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
"integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/delayed-stream": {
|
"node_modules/delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -8905,10 +9086,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "17.2.3",
|
"version": "17.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz",
|
||||||
"integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
|
"integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==",
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -10076,6 +10256,7 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz",
|
||||||
"integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==",
|
"integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"citty": "^0.1.6",
|
"citty": "^0.1.6",
|
||||||
@@ -12554,15 +12735,14 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/mlly": {
|
"node_modules/mlly": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz",
|
||||||
"integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
|
"integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.15.0",
|
"acorn": "^8.16.0",
|
||||||
"pathe": "^2.0.3",
|
"pathe": "^2.0.3",
|
||||||
"pkg-types": "^1.3.1",
|
"pkg-types": "^1.3.1",
|
||||||
"ufo": "^1.6.1"
|
"ufo": "^1.6.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mlly/node_modules/confbox": {
|
"node_modules/mlly/node_modules/confbox": {
|
||||||
@@ -13318,6 +13498,7 @@
|
|||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz",
|
||||||
"integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==",
|
"integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"citty": "^0.1.6",
|
"citty": "^0.1.6",
|
||||||
@@ -14042,10 +14223,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/perfect-debounce": {
|
"node_modules/perfect-debounce": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
|
||||||
"integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==",
|
"integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g=="
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
@@ -15786,10 +15966,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.3",
|
"version": "7.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||||
"license": "ISC",
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
},
|
},
|
||||||
@@ -17197,6 +17376,7 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
|
||||||
"integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
|
"integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -17538,10 +17718,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ufo": {
|
"node_modules/ufo": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
|
||||||
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
|
"integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/ultrahtml": {
|
"node_modules/ultrahtml": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
@@ -17557,15 +17736,14 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unctx": {
|
"node_modules/unctx": {
|
||||||
"version": "2.4.1",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/unctx/-/unctx-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/unctx/-/unctx-2.5.0.tgz",
|
||||||
"integrity": "sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==",
|
"integrity": "sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.14.0",
|
"acorn": "^8.15.0",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
"magic-string": "^0.30.17",
|
"magic-string": "^0.30.21",
|
||||||
"unplugin": "^2.1.0"
|
"unplugin": "^2.3.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/unctx/node_modules/estree-walker": {
|
"node_modules/unctx/node_modules/estree-walker": {
|
||||||
@@ -17796,10 +17974,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/unplugin": {
|
"node_modules/unplugin": {
|
||||||
"version": "2.3.10",
|
"version": "2.3.11",
|
||||||
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz",
|
||||||
"integrity": "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==",
|
"integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/remapping": "^2.3.5",
|
"@jridgewell/remapping": "^2.3.5",
|
||||||
"acorn": "^8.15.0",
|
"acorn": "^8.15.0",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
"@vueup/vue-quill": "^1.2.0",
|
"@vueup/vue-quill": "^1.2.0",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"bowser": "^2.11.0",
|
"bowser": "^2.11.0",
|
||||||
"bulma": "^1.0.2",
|
"bulma": "^1.0.4",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"docx-preview": "^0.3.6",
|
"docx-preview": "^0.3.6",
|
||||||
@@ -51,6 +51,7 @@
|
|||||||
"vue": "latest"
|
"vue": "latest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@nuxt/icon": "^2.2.1",
|
||||||
"nuxt": "^4.2.0",
|
"nuxt": "^4.2.0",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/map-demo.png
Normal file
BIN
public/map-demo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 176 KiB |
Reference in New Issue
Block a user