first commit

This commit is contained in:
Inshal
2024-05-29 22:34:28 +05:00
commit e63fc41a20
1470 changed files with 174828 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
<script setup>
import miscMaskDark from '@images/misc/misc-mask-dark.png'
import miscMaskLight from '@images/misc/misc-mask-light.png'
import tree1 from '@images/misc/tree1.png'
import tree3 from '@images/misc/tree3.png'
import misc404 from '@images/pages/404.png'
const authThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark)
definePage({
alias: '/pages/misc/not-found/:error(.*)',
meta: {
layout: 'blank',
public: true,
},
})
</script>
<template>
<div class="misc-wrapper">
<ErrorHeader
status-code="404"
title="Page Not Found ⚠️"
description="We couldn't find the page you are looking for."
class="mb-10"
/>
<!-- 👉 Image -->
<div class="misc-avatar w-100 text-center">
<VImg
:src="misc404"
alt="Coming Soon"
:max-width="800"
class="mx-auto"
/>
<VBtn
to="/"
class="mt-10"
style="z-index: 1;"
>
Back to Home
</VBtn>
<div class="d-md-flex gap-x-2 misc-footer-tree d-none">
<img
:src="tree3"
alt="tree"
height="120"
width="68"
>
<img
:src="tree3"
alt="tree"
height="70"
width="40"
class="align-self-end"
>
</div>
<img
height="210"
:src="tree1"
class="misc-footer-tree-1 d-none d-md-block"
>
<img
cover
:src="authThemeMask"
height="172"
class="misc-footer-img d-none d-md-block flip-in-rtl"
>
</div>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/pages/misc.scss";
.misc-footer-tree, .misc-footer-tree-1 {
position: absolute;
}
.misc-footer-tree {
inset-block-end: 3.75rem;
inset-inline-start: 3.75rem;
}
.misc-footer-tree-1 {
inset-block-end: 5rem;
inset-inline-end: 4.75rem;
}
</style>

View File

@@ -0,0 +1,36 @@
<script setup>
definePage({
meta: {
action: 'read',
subject: 'AclDemo',
},
})
</script>
<template>
<VRow>
<VCol
cols="12"
md="6"
>
<VCard title="Common">
<VCardText>No ability is required to view this card</VCardText>
<VCardText>
This card is visible to both 'user' and 'admin'
</VCardText>
</VCard>
</VCol>
<VCol
v-if="$can('read', 'all')"
cols="12"
md="6"
>
<VCard title="Analytics">
<VCardText>User with 'Analytics' subject's 'Read' ability can view this card</VCardText>
<VCardText class="text-danger">
This card is visible to 'admin' only
</VCardText>
</VCard>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,308 @@
<script setup>
import { VideoPlayer } from '@videojs-player/vue'
import instructorPosterImage from '@images/pages/instructor-poster-image.png'
import 'video.js/dist/video-js.css'
const courseDetails = ref()
const { data, error } = await useApi('/apps/academy/course-details')
if (error.value)
console.log(error.value)
else if (data.value)
courseDetails.value = data.value
const panelStatus = ref(0)
</script>
<template>
<VRow>
<VCol
cols="12"
md="8"
>
<VCard>
<VCardItem
title="UI/UX Basic Fundamentals"
class="pb-6"
>
<template #subtitle>
<div class="text-body-1">
Prof. <span class="text-h6 d-inline-block">{{ courseDetails?.title }}</span>
</div>
</template>
<template #append>
<div class="d-flex gap-4 align-center">
<VChip
variant="tonal"
color="error"
size="small"
>
UI/UX
</VChip>
<VIcon
size="20"
class="cursor-pointer"
icon="ri-share-forward-line"
/>
<VIcon
size="20"
class="cursor-pointer"
icon="ri-bookmark-line"
/>
</div>
</template>
</VCardItem>
<VCardText>
<VCard
flat
border
>
<div class="px-2 pt-2">
<VideoPlayer
src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4"
:poster="instructorPosterImage"
controls
plays-inline
:height="$vuetify.display.mdAndUp ? 440 : 250"
class="w-100 rounded"
/>
</div>
<VCardText>
<h5 class="text-h5 mb-4">
About this course
</h5>
<p class="text-body-1">
{{ courseDetails?.about }}
</p>
<VDivider class="my-6" />
<h5 class="text-h5 mb-4">
By the numbers
</h5>
<div class="d-flex gap-x-12 gap-y-5 flex-wrap">
<div>
<VList class="card-list text-medium-emphasis">
<VListItem>
<template #prepend>
<VIcon
icon="ri-check-line"
size="20"
/>
</template>
<VListItemTitle>Skill Level: {{ courseDetails?.skillLevel }}</VListItemTitle>
</VListItem>
<VListItem>
<template #prepend>
<VIcon
icon="ri-user-line"
size="20"
/>
</template>
<VListItemTitle>Students: {{ courseDetails?.totalStudents }}</VListItemTitle>
</VListItem>
<VListItem>
<template #prepend>
<VIcon
icon="ri-global-line"
size="20"
/>
</template>
<VListItemTitle>Languages: {{ courseDetails?.language }}</VListItemTitle>
</VListItem>
<VListItem>
<template #prepend>
<VIcon
icon="ri-closed-captioning-line"
size="20"
/>
</template>
<VListItemTitle>Captions: {{ courseDetails?.isCaptions }}</VListItemTitle>
</VListItem>
</VList>
</div>
<div>
<VList class="card-list text-medium-emphasis">
<VListItem>
<template #prepend>
<VIcon
icon="ri-pencil-line"
size="20"
/>
</template>
<VListItemTitle>Lectures: {{ courseDetails?.totalLectures }}</VListItemTitle>
</VListItem>
<VListItem>
<template #prepend>
<VIcon
icon="ri-time-line"
size="20"
/>
</template>
<VListItemTitle>Video: {{ courseDetails?.length }}</VListItemTitle>
</VListItem>
</VList>
</div>
</div>
<VDivider class="my-6" />
<h5 class="text-h5 mb-4">
Description
</h5>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-html="courseDetails?.description" />
<VDivider class="my-6" />
<h5 class="text-h5 mb-4">
Instructor
</h5>
<div class="d-flex align-center">
<VAvatar
:image="courseDetails?.instructorAvatar"
size="38"
class="me-3"
/>
<div>
<h6 class="text-h6 mb-1">
{{ courseDetails?.instructor }}
</h6>
<div class="text-body-1">
{{ courseDetails?.instructorPosition }}
</div>
</div>
</div>
</VCardText>
</VCard>
</VCardText>
</VCard>
</VCol>
<VCol
cols="12"
md="4"
>
<div class="course-content">
<VExpansionPanels
v-model="panelStatus"
variant="accordion"
>
<VExpansionPanel
v-for="(section, index) in courseDetails?.content"
:key="index"
elevation="0"
collapse-icon="ri-arrow-down-s-line"
:expand-icon="$vuetify.locale.isRtl ? 'ri-arrow-left-s-line' : 'ri-arrow-right-s-line'"
:value="index"
>
<template #title>
<div>
<h5 class="text-h5">
{{ section.title }}
</h5>
<div class="text-body-1">
{{ section.status }} | {{ section.time }}
</div>
</div>
</template>
<template #text>
<VList class="card-list">
<VListItem
v-for="(topic, id) in section.topics"
:key="id"
class="py-4"
>
<template #prepend>
<VCheckbox
class="ps-3 me-3"
:model-value="topic.isCompleted"
/>
</template>
<VListItemTitle>
<h6 class="text-h6">
{{ topic.title }}
</h6>
</VListItemTitle>
<VListItemSubtitle>
<div class="text-body-2">
{{ topic.time }}
</div>
</VListItemSubtitle>
</VListItem>
</VList>
</template>
</VExpansionPanel>
</VExpansionPanels>
</div>
</VCol>
</VRow>
</template>
<style lang="scss" scoped>
.course-content {
position: sticky;
inset-block: 4rem 0;
}
.card-list {
--v-card-list-gap: 16px;
}
</style>
<style lang="scss">
.course-content{
.v-expansion-panels{
border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
border-radius: 6px;
.v-expansion-panel{
&--active{
.v-expansion-panel-title--active{
border-block-end: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
.v-expansion-panel-title__overlay{
opacity: var(--v-hover-opacity) !important;
}
}
}
.v-expansion-panel-title{
.v-expansion-panel-title__overlay{
background-color: rgba(var(--v-theme-on-surface));
opacity: var(--v-hover-opacity);
}
&:hover{
.v-expansion-panel-title__overlay{
opacity: var(--v-hover-opacity) !important;
}
}
&__icon{
.v-icon{
block-size: 1.5rem;
color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
font-size: 1.5rem;
inline-size: 1.5rem;
}
}
}
.v-expansion-panel-text{
&__wrapper{
padding-block: 1rem;
padding-inline: 1.25rem;
}
}
}
}
}
.card-list {
.v-list-item__prepend{
.v-list-item__spacer{
inline-size: 8px !important;
}
}
}
</style>

View File

@@ -0,0 +1,356 @@
<script setup>
import AcademyAssignmentProgress from '@/views/apps/academy/AcademyAssignmentProgress.vue'
import AcademyCardInterestedTopics from '@/views/apps/academy/AcademyCardInterestedTopics.vue'
import AcademyCardPopularInstructors from '@/views/apps/academy/AcademyCardPopularInstructors.vue'
import AcademyCardTopCourses from '@/views/apps/academy/AcademyCardTopCourses.vue'
import AcademyCourseTable from '@/views/apps/academy/AcademyCourseTable.vue'
import AcademyUpcomingWebinar from '@/views/apps/academy/AcademyUpcomingWebinar.vue'
import customCheck from '@images/svg/check.svg'
import customLaptop from '@images/svg/laptop.svg'
import customLightbulb from '@images/svg/lightbulb.svg'
const borderColor = 'rgba(var(--v-border-color), var(--v-border-opacity))'
const topicsChartConfig = {
chart: {
height: 270,
type: 'bar',
toolbar: { show: false },
},
plotOptions: {
bar: {
horizontal: true,
barHeight: '70%',
distributed: true,
borderRadius: 7,
},
},
colors: [
'#8C57FF',
'#16B1FF',
'#56CA00',
'#8A8D93',
'#FF4C51',
'#FFB400',
],
grid: {
borderColor,
strokeDashArray: 10,
xaxis: { lines: { show: true } },
yaxis: { lines: { show: false } },
padding: {
top: -35,
bottom: -12,
},
},
dataLabels: {
enabled: true,
style: {
colors: ['#fff'],
fontWeight: 200,
fontSize: '13px',
},
offsetX: 0,
dropShadow: { enabled: false },
formatter(val, opt) {
return topicsChartConfig.labels[opt.dataPointIndex]
},
},
labels: [
'UI Design',
'UX Design',
'Music',
'Animation',
'Vue',
'SEO',
],
xaxis: {
categories: [
'6',
'5',
'4',
'3',
'2',
'1',
],
axisBorder: { show: false },
axisTicks: { show: false },
labels: {
style: {
colors: 'rgba(var(--v-theme-on-background), var(--v-disabled-opacity))',
fontSize: '13px',
},
formatter(val) {
return `${ val }%`
},
},
},
yaxis: {
max: 35,
labels: {
style: {
colors: 'rgba(var(--v-theme-on-background), var(--v-disabled-opacity))',
fontSize: '13px',
},
},
},
tooltip: {
enabled: true,
style: { fontSize: '12px' },
onDatasetHover: { highlightDataSeries: false },
},
legend: { show: false },
}
const donutChartColors = {
donut: {
series1: '#22A95E',
series2: '#24B364',
series3: '#56CA00',
series4: '#53D28C',
series5: '#7EDDA9',
series6: '#A9E9C5',
},
}
const timeSpendingChartConfig = {
chart: {
height: 157,
width: 130,
parentHeightOffset: 0,
type: 'donut',
},
labels: [
'36h',
'56h',
'16h',
'32h',
'56h',
'16h',
],
colors: [
donutChartColors.donut.series1,
donutChartColors.donut.series2,
donutChartColors.donut.series3,
donutChartColors.donut.series4,
donutChartColors.donut.series5,
donutChartColors.donut.series6,
],
stroke: { width: 0 },
dataLabels: {
enabled: false,
formatter(val) {
return `${ Number.parseInt(val) }%`
},
},
legend: { show: false },
tooltip: { theme: false },
grid: { padding: { top: 0 } },
plotOptions: {
pie: {
donut: {
size: '75%',
labels: {
show: true,
value: {
fontSize: '1.125rem',
color: 'rgba(var(--v-theme-on-background), var(--v-high-emphasis-opacity))',
fontWeight: 500,
offsetY: -15,
formatter(val) {
return `${ Number.parseInt(val) }%`
},
},
name: { offsetY: 20 },
total: {
show: true,
fontSize: '.7rem',
label: 'Total',
color: 'rgba(var(--v-theme-on-background), var(--v-disabled-opacity))',
formatter() {
return '231h'
},
},
},
},
},
},
}
const timeSpendingChartSeries = [
23,
35,
10,
20,
35,
23,
]
</script>
<template>
<div>
<VRow class="py-5 match-height">
<!-- 👉 Welcome -->
<VCol
cols="12"
md="8"
sm="6"
:class="$vuetify.display.smAndUp ? 'border-e' : 'border-b'"
>
<div class="pe-3">
<h5 class="text-h5 text-high-emphasis mb-1">
Welcome back, Felecia 👋🏻
</h5>
<div
class="text-wrap text-medium-emphasis mb-4"
style="max-inline-size: 400px;"
>
Your progress this week is Awesome. let's keep it up
and get a lot of points reward!
</div>
<div class="d-flex justify-space-between flex-wrap gap-6 flex-column flex-md-row">
<div
v-for="{ title, value, icon, color } in [
{ title: 'Hours Spent', value: '34h', icon: customLaptop, color: 'primary' },
{ title: 'Test Results', value: '82%', icon: customLightbulb, color: 'info' },
{ title: 'Course Completed', value: '14', icon: customCheck, color: 'warning' },
]"
:key="title"
>
<div class="d-flex">
<VAvatar
variant="tonal"
:color="color"
rounded
size="54"
class="text-primary me-4"
>
<VIcon
:icon="icon"
size="38"
/>
</VAvatar>
<div>
<h6 class="text-h6 text-medium-emphasis">
{{ title }}
</h6>
<h4
class="text-h4 font-weight-medium"
:class="`text-${color}`"
>
{{ value }}
</h4>
</div>
</div>
</div>
</div>
</div>
</VCol>
<!-- 👉 Time Spending -->
<VCol
cols="12"
md="4"
sm="6"
>
<div class="d-flex justify-space-between align-center">
<div class="d-flex flex-column ps-3">
<h5 class="text-h5 mb-1 text-no-wrap">
Time Spending
</h5>
<div class="mb-6 text-body-1">
Weekly Report
</div>
<h4 class="text-h4 mb-2">
231<span class="text-medium-emphasis">h</span> 14<span class="text-medium-emphasis">m</span>
</h4>
<div>
<VChip
color="success"
density="comfortable"
>
+18.4%
</VChip>
</div>
</div>
<div>
<VueApexCharts
type="donut"
height="150"
width="150"
:options="timeSpendingChartConfig"
:series="timeSpendingChartSeries"
/>
</div>
</div>
</VCol>
</VRow>
<VRow class="match-height">
<VCol
cols="12"
md="8"
>
<!-- 👉 Topic You are Interested in -->
<AcademyCardInterestedTopics />
</VCol>
<!-- 👉 Popular Instructors -->
<VCol
cols="12"
md="4"
sm="6"
>
<AcademyCardPopularInstructors />
</VCol>
<!-- 👉 Academy Top Courses -->
<VCol
cols="12"
md="4"
sm="6"
>
<AcademyCardTopCourses />
</VCol>
<!-- 👉 Academy Upcoming Webinar -->
<VCol
cols="12"
md="4"
sm="6"
>
<AcademyUpcomingWebinar />
</VCol>
<!-- 👉 Academy Assignment Progress -->
<VCol
cols="12"
md="4"
sm="6"
>
<AcademyAssignmentProgress />
</VCol>
<!-- 👉 Academy Course Table -->
<VCol>
<AcademyCourseTable />
</VCol>
</VRow>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/libs/apex-chart.scss";
.topicCard{
.v-badge.v-badge--dot{
.v-badge__badge{
border-radius: 6px;
block-size: 12px;
inline-size: 12px;
}
}
}
</style>

View File

@@ -0,0 +1,229 @@
<script setup>
import { VideoPlayer } from '@videojs-player/vue'
import AcademyMyCourses from '@/views/apps/academy/AcademyMyCourses.vue'
import academyCourseIllustration1 from '@images/pages/academy-course-illustration1.png'
import academyCourseIllustration2 from '@images/pages/academy-course-illustration2.png'
import boyAcademyIllustration from '@images/pages/boy-academy-illustration.png'
import girlAcademyIllustration from '@images/pages/girl-academy-illustration.png'
import guitarCoursePoster from '@images/pages/guitar-course.png'
import singingCoursePoster from '@images/pages/singing-course.png'
const searchQuery = ref('')
</script>
<template>
<div>
<VCard class="mb-6">
<VCardText class="py-12 position-relative">
<div
class="d-flex flex-column gap-y-4 mx-auto"
:class="$vuetify.display.mdAndUp ? 'w-50' : 'w-100'"
>
<h4
class="text-h4 text-center text-wrap mx-auto"
:class="$vuetify.display.mdAndUp ? 'w-75' : 'w-100'"
>
Education, talents, and career
opportunities. <span class="text-primary"> All in one place.</span>
</h4>
<p class="text-center text-wrap text-body-1 mx-auto mb-0">
Grow your skill with the most reliable online courses and certifications in marketing, information technology, programming, and data science.
</p>
<div class="d-flex justify-center align-center gap-x-4">
<VTextField
v-model="searchQuery"
density="compact"
placeholder="Find your course"
style="max-inline-size: 400px;"
/>
<VBtn
color="primary"
icon="ri-search-line"
class="rounded"
/>
</div>
</div>
<img
:src="academyCourseIllustration1"
class="illustration1 d-none d-md-block"
height="180"
>
<img
:src="academyCourseIllustration2"
class="illustration2 d-none d-md-block"
height="100"
>
</VCardText>
</VCard>
<AcademyMyCourses :search-query="searchQuery" />
<div class="mb-6">
<VRow>
<VCol
cols="12"
md="6"
>
<VCard
flat
color="rgba(var(--v-theme-primary), 0.08)"
>
<VCardText>
<div class="d-flex flex-column-reverse flex-sm-row gap-4 justify-space-between">
<div class="text-center text-sm-start">
<h5 class="text-h5 text-primary mb-1">
Earn a Certificate
</h5>
<p class="text-body-1 text-high-emphasis mb-6">
Get the right professional certificate program for you.
</p>
<VBtn>View Programs</VBtn>
</div>
<div class="text-center">
<img :src="boyAcademyIllustration">
</div>
</div>
</VCardText>
</VCard>
</VCol>
<VCol
cols="12"
md="6"
>
<VCard
flat
color="rgba(var(--v-theme-error), 0.08)"
>
<VCardText>
<div class="d-flex flex-column-reverse flex-sm-row gap-4 justify-space-between">
<div class="text-center text-sm-start">
<h5 class="text-h5 text-error mb-1">
Best Rated Courses
</h5>
<p class="text-body-1 text-high-emphasis text-wrap mb-6">
Enroll now in the most popular and best rated courses.
</p>
<VBtn color="error">
View Courses
</VBtn>
</div>
<div class="text-center">
<img :src="girlAcademyIllustration">
</div>
</div>
</VCardText>
</VCard>
</VCol>
</VRow>
</div>
<VCard>
<VCardText>
<VRow>
<VCol
cols="12"
md="4"
>
<div class="d-flex flex-column align-center gap-y-4 h-100 justify-center">
<VAvatar
variant="tonal"
size="52"
rounded
color="primary"
>
<VIcon
icon="ri-gift-line"
size="36"
/>
</VAvatar>
<h4 class="text-h4">
Today's Free Courses
</h4>
<p class="text-body-1 text-center mb-0">
We offers 284 Free Online courses from top tutors and companies to help you start or advance your career skills. Learn online for free and fast today!
</p>
<VBtn>Get Premium Courses</VBtn>
</div>
</VCol>
<VCol
cols="12"
md="4"
sm="6"
>
<VCard
flat
border
>
<div class="pa-2">
<VideoPlayer
src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4"
:poster="singingCoursePoster"
controls
plays-inline
:height="$vuetify.display.mdAndUp ? 200 : 150"
class="w-100 rounded"
/>
</div>
<VCardText class="pt-3">
<h5 class="text-h5 mb-2">
Your First Singing Lesson
</h5>
<p class="text-body-1 mb-0">
In the same way as any other artistic domain, singing lends itself perfectly to self-teaching.
</p>
</VCardText>
</VCard>
</VCol>
<VCol
cols="12"
md="4"
sm="6"
>
<VCard
flat
border
>
<div class="pa-2">
<VideoPlayer
src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4"
:poster="guitarCoursePoster"
controls
plays-inline
:height="$vuetify.display.mdAndUp ? 200 : 150"
class="w-100 rounded"
/>
</div>
<VCardText class="pt-3">
<h5 class="text-h5 mb-2">
Guitar for Beginners
</h5>
<p class="text-body-1 mb-0">
The Fender Acoustic Guitar is best choice for beginners and professionals.
</p>
</VCardText>
</VCard>
</VCol>
</VRow>
</VCardText>
</VCard>
</div>
</template>
<style lang="scss">
@import 'video.js/dist/video-js.css';
.illustration1 {
position: absolute;
inset-block-end: 0;
inset-inline-end: 0;
}
.illustration2 {
position: absolute;
inset-block-start: 2rem;
inset-inline-start: 2.5rem;
}
</style>

View File

@@ -0,0 +1,175 @@
<script setup>
import FullCalendar from '@fullcalendar/vue3'
import {
blankEvent,
useCalendar,
} from '@/views/apps/calendar/useCalendar'
import { useCalendarStore } from '@/views/apps/calendar/useCalendarStore'
// Components
import CalendarEventHandler from '@/views/apps/calendar/CalendarEventHandler.vue'
// 👉 Store
const store = useCalendarStore()
// 👉 Event
const event = ref(structuredClone(blankEvent))
const isEventHandlerSidebarActive = ref(false)
watch(isEventHandlerSidebarActive, val => {
if (!val)
event.value = structuredClone(blankEvent)
})
const { isLeftSidebarOpen } = useResponsiveLeftSidebar()
// 👉 useCalendar
const { refCalendar, calendarOptions, addEvent, updateEvent, removeEvent, jumpToDate } = useCalendar(event, isEventHandlerSidebarActive, isLeftSidebarOpen)
// SECTION Sidebar
// 👉 Check all
const checkAll = computed({
/*GET: Return boolean `true` => if length of options matches length of selected filters => Length matches when all events are selected
SET: If value is `true` => then add all available options in selected filters => Select All
Else if => all filters are selected (by checking length of both array) => Empty Selected array => Deselect All
*/
get: () => store.selectedCalendars.length === store.availableCalendars.length,
set: val => {
if (val)
store.selectedCalendars = store.availableCalendars.map(i => i.label)
else if (store.selectedCalendars.length === store.availableCalendars.length)
store.selectedCalendars = []
},
})
// !SECTION
const calendarApi = ref(null)
</script>
<template>
<div>
<VCard>
<!-- `z-index: 0` Allows overlapping vertical nav on calendar -->
<VLayout style="z-index: 0;">
<!-- 👉 Navigation drawer -->
<VNavigationDrawer
v-model="isLeftSidebarOpen"
width="292"
absolute
touchless
location="start"
class="calendar-add-event-drawer"
:temporary="$vuetify.display.mdAndDown"
>
<div class="pa-5">
<VBtn
block
prepend-icon="ri-add-line"
@click="isEventHandlerSidebarActive = true"
>
Add event
</VBtn>
</div>
<VDivider />
<div class="d-flex align-center justify-center pa-2">
<AppDateTimePicker
v-model="calendarApi"
:config="{ inline: true }"
class="calendar-date-picker"
@update:model-value="jumpToDate($event)"
/>
</div>
<VDivider />
<div class="pa-5">
<h5 class="text-h5 mb-4">
Event Filters
</h5>
<div class="d-flex flex-column calendars-checkbox">
<VCheckbox
v-model="checkAll"
label="View all"
/>
<VCheckbox
v-for="calendar in store.availableCalendars"
:key="calendar.label"
v-model="store.selectedCalendars"
:value="calendar.label"
:color="calendar.color"
:label="calendar.label"
/>
</div>
</div>
</VNavigationDrawer>
<VMain>
<VCard flat>
<FullCalendar
ref="refCalendar"
:options="calendarOptions"
/>
</VCard>
</VMain>
</VLayout>
</VCard>
<CalendarEventHandler
v-model:isDrawerOpen="isEventHandlerSidebarActive"
:event="event"
@add-event="addEvent"
@update-event="updateEvent"
@remove-event="removeEvent"
/>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/libs/full-calendar";
.calendars-checkbox {
.v-label {
color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity));
opacity: var(--v-high-emphasis-opacity);
}
}
.calendar-add-event-drawer {
&.v-navigation-drawer:not(.v-navigation-drawer--temporary) {
border-end-start-radius: 0.375rem;
border-start-start-radius: 0.375rem;
}
}
.calendar-date-picker {
display: none;
+.flatpickr-input {
+.flatpickr-calendar.inline {
border: none;
box-shadow: none;
.flatpickr-months {
border-block-end: none;
}
}
}
& ~ .flatpickr-calendar .flatpickr-weekdays {
margin-block: 0 4px;
}
}
</style>
<style lang="scss" scoped>
.v-layout {
overflow: visible !important;
.v-card {
overflow: visible;
}
}
</style>

View File

@@ -0,0 +1,429 @@
<script setup>
import { PerfectScrollbar } from 'vue3-perfect-scrollbar'
import {
useDisplay,
useTheme,
} from 'vuetify'
import { themes } from '@/plugins/vuetify/theme'
import ChatActiveChatUserProfileSidebarContent from '@/views/apps/chat/ChatActiveChatUserProfileSidebarContent.vue'
import ChatLeftSidebarContent from '@/views/apps/chat/ChatLeftSidebarContent.vue'
import ChatLog from '@/views/apps/chat/ChatLog.vue'
import ChatUserProfileSidebarContent from '@/views/apps/chat/ChatUserProfileSidebarContent.vue'
import { useChat } from '@/views/apps/chat/useChat'
import { useChatStore } from '@/views/apps/chat/useChatStore'
definePage({ meta: { layoutWrapperClasses: 'layout-content-height-fixed' } })
// composables
const vuetifyDisplays = useDisplay()
const store = useChatStore()
const { isLeftSidebarOpen } = useResponsiveLeftSidebar(vuetifyDisplays.smAndDown)
const { resolveAvatarBadgeVariant } = useChat()
// Perfect scrollbar
const chatLogPS = ref()
const scrollToBottomInChatLog = () => {
const scrollEl = chatLogPS.value.$el || chatLogPS.value
scrollEl.scrollTop = scrollEl.scrollHeight
}
// Search query
const q = ref('')
watch(q, val => store.fetchChatsAndContacts(val), { immediate: true })
// Open Sidebar in smAndDown when "start conversation" is clicked
const startConversation = () => {
if (vuetifyDisplays.mdAndUp.value)
return
isLeftSidebarOpen.value = true
}
// Chat message
const msg = ref('')
const sendMessage = async () => {
if (!msg.value)
return
await store.sendMsg(msg.value)
// Reset message input
msg.value = ''
// Scroll to bottom
nextTick(() => {
scrollToBottomInChatLog()
})
}
const openChatOfContact = async userId => {
await store.getChat(userId)
// Reset message input
msg.value = ''
// Set unseenMsgs to 0
const contact = store.chatsContacts.find(c => c.id === userId)
if (contact)
contact.chat.unseenMsgs = 0
// if smAndDown => Close Chat & Contacts left sidebar
if (vuetifyDisplays.smAndDown.value)
isLeftSidebarOpen.value = false
// Scroll to bottom
nextTick(() => {
scrollToBottomInChatLog()
})
}
// User profile sidebar
const isUserProfileSidebarOpen = ref(false)
// Active chat user profile sidebar
const isActiveChatUserProfileSidebarOpen = ref(false)
// file input
const refInputEl = ref()
const moreList = [
{
title: 'View Contact',
value: 'View Contact',
},
{
title: 'Mute Notifications',
value: 'Mute Notifications',
},
{
title: 'Block Contact',
value: 'Block Contact',
},
{
title: 'Clear Chat',
value: 'Clear Chat',
},
{
title: 'Report',
value: 'Report',
},
]
const { name } = useTheme()
const chatContentContainerBg = computed(() => {
let color = 'transparent'
if (themes)
color = themes?.[name.value].colors?.['chat-bg']
return color
})
</script>
<template>
<VLayout class="chat-app-layout bg-surface">
<!-- 👉 user profile sidebar -->
<VNavigationDrawer
v-model="isUserProfileSidebarOpen"
temporary
touchless
absolute
class="user-profile-sidebar"
location="start"
width="370"
>
<ChatUserProfileSidebarContent @close="isUserProfileSidebarOpen = false" />
</VNavigationDrawer>
<!-- 👉 Active Chat sidebar -->
<VNavigationDrawer
v-model="isActiveChatUserProfileSidebarOpen"
width="374"
absolute
temporary
location="end"
touchless
class="active-chat-user-profile-sidebar"
>
<ChatActiveChatUserProfileSidebarContent @close="isActiveChatUserProfileSidebarOpen = false" />
</VNavigationDrawer>
<!-- 👉 Left sidebar -->
<VNavigationDrawer
v-model="isLeftSidebarOpen"
absolute
touchless
location="start"
width="370"
:temporary="$vuetify.display.smAndDown"
class="chat-list-sidebar"
:permanent="$vuetify.display.mdAndUp"
>
<ChatLeftSidebarContent
v-model:isDrawerOpen="isLeftSidebarOpen"
v-model:search="q"
@open-chat-of-contact="openChatOfContact"
@show-user-profile="isUserProfileSidebarOpen = true"
@close="isLeftSidebarOpen = false"
/>
</VNavigationDrawer>
<!-- 👉 Chat content -->
<VMain class="chat-content-container">
<!-- 👉 Right content: Active Chat -->
<div
v-if="store.activeChat"
class="d-flex flex-column h-100"
>
<!-- 👉 Active chat header -->
<div class="active-chat-header d-flex align-center text-medium-emphasis">
<!-- Sidebar toggler -->
<IconBtn
class="d-md-none me-4"
@click="isLeftSidebarOpen = true"
>
<VIcon icon="ri-menu-line" />
</IconBtn>
<!-- avatar -->
<div
class="d-flex align-center cursor-pointer"
@click="isActiveChatUserProfileSidebarOpen = true"
>
<VBadge
dot
location="bottom right"
offset-x="3"
offset-y="3"
:color="resolveAvatarBadgeVariant(store.activeChat.contact.status)"
bordered
class="me-4"
>
<VAvatar
size="40"
:variant="!store.activeChat.contact.avatar ? 'tonal' : undefined"
:color="!store.activeChat.contact.avatar ? resolveAvatarBadgeVariant(store.activeChat.contact.status) : undefined"
class="cursor-pointer"
>
<VImg
v-if="store.activeChat.contact.avatar"
:src="store.activeChat.contact.avatar"
:alt="store.activeChat.contact.fullName"
/>
<span v-else>{{ avatarText(store.activeChat.contact.fullName) }}</span>
</VAvatar>
</VBadge>
<div class="flex-grow-1 overflow-hidden">
<h6 class="text-h6 font-weight-regular">
{{ store.activeChat.contact.fullName }}
</h6>
<p class="text-body-2 text-truncate mb-0">
{{ store.activeChat.contact.role }}
</p>
</div>
</div>
<VSpacer />
<!-- Header right content -->
<div class="d-sm-flex align-center d-none">
<IconBtn>
<VIcon icon="ri-phone-line" />
</IconBtn>
<IconBtn>
<VIcon icon="ri-vidicon-line" />
</IconBtn>
<IconBtn>
<VIcon icon="ri-search-line" />
</IconBtn>
</div>
<MoreBtn :menu-list="moreList" />
</div>
<VDivider />
<!-- Chat log -->
<PerfectScrollbar
ref="chatLogPS"
tag="ul"
:options="{ wheelPropagation: false }"
class="flex-grow-1"
>
<ChatLog />
</PerfectScrollbar>
<!-- Message form -->
<VForm
class="chat-log-message-form mb-5 mx-5"
@submit.prevent="sendMessage"
>
<VTextField
:key="store.activeChat?.contact.id"
v-model="msg"
variant="solo"
density="default"
class="chat-message-input"
placeholder="Type your message..."
autofocus
>
<template #append-inner>
<IconBtn>
<VIcon icon="ri-mic-line" />
</IconBtn>
<IconBtn
class="me-4"
@click="refInputEl?.click()"
>
<VIcon icon="ri-attachment-2" />
</IconBtn>
<VBtn
append-icon="ri-send-plane-line"
@click="sendMessage"
>
Send
</VBtn>
</template>
</VTextField>
<input
ref="refInputEl"
type="file"
name="file"
accept=".jpeg,.png,.jpg,GIF"
hidden
>
</VForm>
</div>
<!-- 👉 Start conversation -->
<div
v-else
class="d-flex h-100 align-center justify-center flex-column"
>
<VAvatar
size="98"
color="primary"
variant="tonal"
class="mb-5"
>
<VIcon
size="50"
icon="ri-wechat-line"
/>
</VAvatar>
<p
class="mb-0 px-4 py-2 font-weight-medium elevation-2 rounded-xl bg-primary"
:class="[{ 'cursor-pointer': $vuetify.display.smAndDown }]"
@click="startConversation"
>
Start Conversation
</p>
</div>
</VMain>
</VLayout>
</template>
<style lang="scss">
@use "@styles/variables/vuetify.scss";
@use "@core-scss/base/mixins.scss";
@use "@layouts/styles/mixins" as layoutsMixins;
// Variables
$chat-app-header-height: 76px;
// Placeholders
%chat-header {
display: flex;
align-items: center;
min-block-size: $chat-app-header-height;
padding-inline: 1.25rem;
}
.chat-app-layout {
border-radius: vuetify.$card-border-radius;
@include mixins.elevation(vuetify.$card-elevation);
$sel-chat-app-layout: &;
@at-root {
.skin--bordered {
@include mixins.bordered-skin($sel-chat-app-layout);
}
}
.active-chat-user-profile-sidebar,
.user-profile-sidebar {
.v-navigation-drawer__content {
display: flex;
flex-direction: column;
}
}
.chat-list-header,
.active-chat-header {
@extend %chat-header;
}
.chat-list-search {
.v-field__outline__start {
flex-basis: 20px !important;
border-radius: 28px 0 0 28px !important;
}
.v-field__outline__end {
border-radius: 0 28px 28px 0 !important;
}
@include layoutsMixins.rtl {
.v-field__outline__start {
flex-basis: 20px !important;
border-radius: 0 28px 28px 0 !important;
}
.v-field__outline__end {
border-radius: 28px 0 0 28px !important;
}
}
}
.chat-list-sidebar {
.v-navigation-drawer__content {
display: flex;
flex-direction: column;
}
}
}
.chat-content-container {
/* stylelint-disable-next-line value-keyword-case */
background-color: v-bind(chatContentContainerBg);
// Adjust the padding so text field height stays 48px
.chat-message-input {
.v-field__append-inner {
align-items: center;
padding-block-start: 0;
}
.v-field--appended {
padding-inline-end: 6px;
}
}
}
.chat-user-profile-badge {
.v-badge__badge {
/* stylelint-disable liberty/use-logical-spec */
min-width: 12px !important;
height: 0.75rem;
/* stylelint-enable liberty/use-logical-spec */
}
}
</style>

View File

@@ -0,0 +1,107 @@
<script setup>
import CustomerBioPanel from '@/views/apps/ecommerce/customer/view/CustomerBioPanel.vue'
import CustomerTabAddressAndBilling from '@/views/apps/ecommerce/customer/view/CustomerTabAddressAndBilling.vue'
import CustomerTabNotification from '@/views/apps/ecommerce/customer/view/CustomerTabNotification.vue'
import CustomerTabOverview from '@/views/apps/ecommerce/customer/view/CustomerTabOverview.vue'
import CustomerTabSecurity from '@/views/apps/ecommerce/customer/view/CustomerTabSecurity.vue'
const route = useRoute('apps-ecommerce-customer-details-id')
const customerData = ref()
const userTab = ref(null)
const tabs = [
{
icon: 'ri-group-line',
title: 'Overview',
},
{
icon: 'ri-lock-line',
title: 'Security',
},
{
icon: 'ri-map-pin-line',
title: 'Address & Billing',
},
{
icon: 'ri-notification-3-line',
title: 'Notifications',
},
]
const { data, error } = await useApi(`/apps/ecommerce/customers/${ route.params.id }`)
if (error.value)
console.log(error.value)
else if (data.value)
customerData.value = data.value
</script>
<template>
<div>
<!-- 👉 Header -->
<div class="d-flex justify-space-between align-center flex-wrap gap-y-4 mb-6">
<div>
<h4 class="text-h4">
Customer ID #{{ route.params.id }}
</h4>
<p class="text-body-1 mb-0">
Aug 17, 2020, 5:48 (ET)
</p>
</div>
<VBtn
variant="outlined"
color="error"
>
Delete Customer
</VBtn>
</div>
<!-- 👉 Customer Profile -->
<VRow v-if="customerData">
<VCol
cols="12"
md="5"
lg="4"
>
<CustomerBioPanel :customer-data="customerData" />
</VCol>
<VCol
cols="12"
md="7"
lg="8"
>
<VTabs
v-model="userTab"
class="v-tabs-pill mb-6 6 disable-tab-transition"
>
<VTab
v-for="tab in tabs"
:key="tab.icon"
>
<VIcon
start
:icon="tab.icon"
/>
<span>{{ tab.title }}</span>
</VTab>
</VTabs>
<VWindow
v-model="userTab"
class="mb-6 disable-tab-transition"
:touch="false"
>
<VWindowItem>
<CustomerTabOverview />
</VWindowItem>
<VWindowItem>
<CustomerTabSecurity />
</VWindowItem>
<VWindowItem>
<CustomerTabAddressAndBilling />
</VWindowItem>
<VWindowItem>
<CustomerTabNotification />
</VWindowItem>
</VWindow>
</VCol>
</VRow>
</div>
</template>

View File

@@ -0,0 +1,187 @@
<script setup>
import ECommerceAddCustomerDrawer from '@/views/apps/ecommerce/ECommerceAddCustomerDrawer.vue'
const searchQuery = ref('')
const isAddCustomerDrawerOpen = ref(false)
// Data table options
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
// Data table Headers
const headers = [
{
title: 'Customer',
key: 'customer',
},
{
title: 'Customer Id',
key: 'customerId',
},
{
title: 'Country',
key: 'country',
},
{
title: 'Orders',
key: 'orders',
},
{
title: 'Total Spent',
key: 'totalSpent',
},
]
const updateOptions = options => {
page.value = options.page
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const { data: customerData } = await useApi(createUrl('/apps/ecommerce/customers', {
query: {
q: searchQuery,
itemsPerPage,
page,
sortBy,
orderBy,
},
}))
const customers = computed(() => customerData.value.customers)
const totalCustomers = computed(() => customerData.value.total)
</script>
<template>
<div>
<VCard>
<VCardText>
<div class="d-flex justify-space-between flex-wrap gap-y-4">
<VTextField
v-model="searchQuery"
style="max-inline-size: 200px; min-inline-size: 200px;"
density="compact"
placeholder="Search .."
/>
<div class="d-flex flex-row gap-4 align-center flex-wrap">
<VBtn
prepend-icon="ri-upload-2-line"
variant="outlined"
color="secondary"
>
Export
</VBtn>
<VBtn
prepend-icon="ri-add-line"
@click="isAddCustomerDrawerOpen = !isAddCustomerDrawerOpen"
>
Add Customer
</VBtn>
</div>
</div>
</VCardText>
<VDataTableServer
v-model:items-per-page="itemsPerPage"
v-model:page="page"
:items="customers"
item-value="customer"
:headers="headers"
:items-length="totalCustomers"
show-select
class="text-no-wrap"
@update:options="updateOptions"
>
<template #item.customer="{ item }">
<div class="d-flex align-center gap-x-3">
<VAvatar
size="34"
:image="item.avatar"
/>
<div class="d-flex flex-column">
<RouterLink
:to="{ name: 'apps-ecommerce-customer-details-id', params: { id: item.customerId } }"
class="text-h6 font-weight-medium"
>
{{ item.customer }}
</RouterLink>
<span class="text-sm">{{ item.email }}</span>
</div>
</div>
</template>
<template #item.customerId="{ item }">
<h6 class="text-h6 font-weight-regular">
#{{ item.customerId }}
</h6>
</template>
<template #item.orders="{ item }">
{{ item.order }}
</template>
<template #item.country="{ item }">
<div class="d-flex gap-x-2">
<img
:src="item.countryFlag"
height="22"
width="22"
>
<span class="text-body-1">{{ item.country }}</span>
</div>
</template>
<template #item.totalSpent="{ item }">
<h6 class="text-h6">
${{ item.totalSpent }}
</h6>
</template>
<!-- Pagination -->
<template #bottom>
<VDivider />
<div class="d-flex justify-end flex-wrap gap-x-6 px-2 py-1">
<div class="d-flex align-center gap-x-2 text-medium-emphasis text-base">
Rows Per Page:
<VSelect
v-model="itemsPerPage"
class="per-page-select"
variant="plain"
:items="[10, 20, 25, 50, 100]"
/>
</div>
<p class="d-flex align-center text-base text-high-emphasis me-2 mb-0">
{{ paginationMeta({ page, itemsPerPage }, totalCustomers) }}
</p>
<div class="d-flex gap-x-2 align-center me-2">
<VBtn
class="flip-in-rtl"
icon="ri-arrow-left-s-line"
variant="text"
density="comfortable"
color="high-emphasis"
:disabled="page <= 1"
@click="page <= 1 ? page = 1 : page--"
/>
<VBtn
class="flip-in-rtl"
icon="ri-arrow-right-s-line"
density="comfortable"
variant="text"
color="high-emphasis"
:disabled="page >= Math.ceil(totalCustomers / itemsPerPage)"
@click="page >= Math.ceil(totalCustomers / itemsPerPage) ? page = Math.ceil(totalCustomers / itemsPerPage) : page++ "
/>
</div>
</div>
</template>
</VDataTableServer>
</VCard>
<ECommerceAddCustomerDrawer v-model:is-drawer-open="isAddCustomerDrawerOpen" />
</div>
</template>

View File

@@ -0,0 +1,587 @@
<script setup>
const selectedStatus = ref('All')
const searchQuery = ref('')
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
const {
data: ReviewData,
execute: fetchReviews,
} = await useApi(createUrl('/apps/ecommerce/reviews', {
query: {
q: searchQuery,
status: selectedStatus,
page,
itemsPerPage,
sortBy,
orderBy,
},
}))
const reviews = computed(() => ReviewData.value.reviews)
const totalReviews = computed(() => ReviewData.value.total)
const updateOptions = options => {
page.value = options.page
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const deleteReview = async id => {
await $api(`/apps/ecommerce/reviews/${ id }`, { method: 'DELETE' })
fetchReviews()
}
const reviewCardData = [
{
rating: 5,
value: 124,
},
{
rating: 4,
value: 40,
},
{
rating: 3,
value: 12,
},
{
rating: 2,
value: 7,
},
{
rating: 1,
value: 2,
},
]
const headers = [
{
title: 'Product',
key: 'product',
},
{
title: 'Reviewer',
key: 'reviewer',
},
{
title: 'Review',
key: 'review',
sortable: false,
},
{
title: 'Date',
key: 'date',
},
{
title: 'Status',
key: 'status',
},
{
title: 'Actions',
key: 'actions',
sortable: false,
},
]
const labelColor = 'rgba(var(--v-theme-on-surface), var(--v-disabled-opacity))'
const reviewStatChartSeries = [{
data: [
20,
40,
60,
80,
100,
80,
60,
],
}]
const reviewStatChartConfig = {
chart: {
height: 160,
width: 190,
type: 'bar',
toolbar: { show: false },
},
legend: { show: false },
grid: {
show: false,
padding: {
top: -25,
bottom: -12,
},
},
colors: [
'rgba(var(--v-theme-success), var(--v-activated-opacity))',
'rgba(var(--v-theme-success), var(--v-activated-opacity))',
'rgba(var(--v-theme-success), var(--v-activated-opacity))',
'rgba(var(--v-theme-success), var(--v-activated-opacity))',
'rgba(var(--v-theme-success), 1)',
'rgba(var(--v-theme-success), var(--v-activated-opacity))',
'rgba(var(--v-theme-success), var(--v-activated-opacity))',
],
plotOptions: {
bar: {
barHeight: '75%',
columnWidth: '35%',
borderRadius: 5,
distributed: true,
},
},
dataLabels: { enabled: false },
xaxis: {
categories: [
'M',
'T',
'W',
'T',
'F',
'S',
'S',
],
axisBorder: { show: false },
axisTicks: { show: false },
labels: {
style: {
colors: labelColor,
fontSize: '13px',
},
},
},
yaxis: { labels: { show: false } },
responsive: [
{
breakpoint: 0,
options: {
chart: { width: '100%' },
plotOptions: { bar: { columnWidth: '40%' } },
},
},
{
breakpoint: 1440,
options: {
chart: {
height: 150,
width: 190,
toolbar: { show: !1 },
},
plotOptions: {
bar: {
borderRadius: 6,
columnWidth: '40%',
},
},
},
},
{
breakpoint: 1400,
options: {
plotOptions: {
bar: {
borderRadius: 6,
columnWidth: '40%',
},
},
},
},
{
breakpoint: 1200,
options: {
chart: {
height: 130,
width: 190,
toolbar: { show: !1 },
},
plotOptions: {
bar: {
borderRadius: 6,
columnWidth: '40%',
},
},
},
},
{
breakpoint: 992,
chart: {
height: 150,
width: 190,
toolbar: { show: !1 },
},
options: {
plotOptions: {
bar: {
borderRadius: 5,
columnWidth: '40%',
},
},
},
},
{
breakpoint: 883,
options: {
plotOptions: {
bar: {
borderRadius: 5,
columnWidth: '40%',
},
},
},
},
{
breakpoint: 768,
options: {
chart: {
height: 150,
width: 190,
toolbar: { show: !1 },
},
plotOptions: {
bar: {
borderRadius: 4,
columnWidth: '40%',
},
},
},
},
{
breakpoint: 576,
options: {
chart: {
width: '100%',
height: '200',
type: 'bar',
},
plotOptions: {
bar: {
borderRadius: 6,
columnWidth: '30% ',
},
},
},
},
{
breakpoint: 420,
options: {
plotOptions: {
chart: {
width: '100%',
height: '200',
type: 'bar',
},
bar: {
borderRadius: 3,
columnWidth: '30%',
},
},
},
},
],
}
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Total Review Card -->
<VCard>
<VCardText>
<VRow>
<VCol
cols="12"
sm="6"
>
<div :class="$vuetify.display.smAndUp ? 'border-e' : 'border-b'">
<div class="d-flex align-center gap-x-2">
<h4 class="text-h3 text-primary">
4.89
</h4>
<VIcon
icon="ri-star-smile-line"
color="primary"
size="32"
/>
</div>
<h6 class="my-2 text-h6">
Total 187 reviews
</h6>
<div class="mb-2">
All reviews are from genuine customers
</div>
<VChip
color="primary"
size="small"
:class="$vuetify.display.smAndUp ? '' : 'mb-4'"
>
+5 This week
</VChip>
</div>
</VCol>
<VCol
cols="12"
sm="6"
>
<div
v-for="(item, index) in reviewCardData"
:key="index"
class="d-flex align-center gap-4 mb-3"
>
<div class="text-sm text-no-wrap">
{{ item.rating }} Star
</div>
<div class="w-100">
<VProgressLinear
color="primary"
height="8"
:model-value="(item.value / 185) * 100"
rounded
/>
</div>
<div class="text-sm">
{{ item.value }}
</div>
</div>
</VCol>
</VRow>
</VCardText>
</VCard>
</VCol>
<VCol
cols="12"
md="6"
>
<VCard>
<VCardText>
<VRow>
<VCol
cols="12"
sm="5"
>
<div>
<h5 class="text-h5 mb-2">
Reviews statistics
</h5>
<div class="mb-9">
<span class="me-2">12 New Reviews</span>
<VChip
color="success"
size="small"
>
+8.4%
</VChip>
</div>
<div>
<div class="text-high-emphasis text-body-1 mb-2">
<span class="text-success">87%</span> Positive Reviews
</div>
<div class="text-body-2">
Weekly Report
</div>
</div>
</div>
</VCol>
<VCol
cols="12"
sm="7"
>
<div class="d-flex justify-start justify-sm-end">
<VueApexCharts
id="shipment-statistics"
type="bar"
height="150"
:options="reviewStatChartConfig"
:series="reviewStatChartSeries"
/>
</div>
</VCol>
</VRow>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<VCard>
<VCardText>
<div class="d-flex justify-space-between flex-wrap gap-y-4">
<VTextField
v-model="searchQuery"
style="max-inline-size: 250px; min-inline-size: 200px;"
placeholder="Search"
density="compact"
/>
<div class="d-flex flex-row gap-4 align-center flex-wrap">
<VSelect
v-model="selectedStatus"
style="min-inline-size: 6.25rem;"
density="compact"
:items="[
{ title: 'All', value: 'All' },
{ title: 'Published', value: 'Published' },
{ title: 'Pending', value: 'Pending' },
]"
/>
<VBtn prepend-icon="ri-upload-2-line">
Export
</VBtn>
</div>
</div>
</VCardText>
<VDataTableServer
v-model:items-per-page="itemsPerPage"
v-model:page="page"
:headers="headers"
:items="reviews"
show-select
:items-length="totalReviews"
item-value="id"
class="text-no-wrap rounded-0"
@update:options="updateOptions"
>
<template #item.product="{ item }">
<div class="d-flex gap-x-4 align-center">
<VAvatar
:image="item.productImage"
:size="38"
variant="tonal"
rounded
/>
<div class="d-flex flex-column">
<h6 class="text-h6">
{{ item.product }}
</h6>
<span class="text-sm text-wrap clamp-text">{{ item.companyName }}</span>
</div>
</div>
</template>
<template #item.reviewer="{ item }">
<div class="d-flex align-center gap-x-4">
<VAvatar
:image="item.avatar"
size="34"
/>
<div class="d-flex flex-column">
<RouterLink
:to="{ name: 'apps-ecommerce-customer-details-id', params: { id: 478426 } }"
class="font-weight-medium"
>
{{ item.reviewer }}
</RouterLink>
<span class="text-body-2">{{ item.email }}</span>
</div>
</div>
</template>
<template #item.review="{ item }">
<div class="py-4">
<VRating
:size="24"
readonly
:model-value="item.review"
/>
<h6 class="text-h6 mb-1">
{{ item.head }}
</h6>
<p class="text-sm text-medium-emphasis text-wrap mb-0">
{{ item.para }}
</p>
</div>
</template>
<template #item.date="{ item }">
<span class="text-body-1">{{ new Date(item.date).toDateString() }}</span>
</template>
<template #item.status="{ item }">
<VChip
:color="item.status === 'Published' ? 'success' : 'warning'"
size="small"
>
{{ item.status }}
</VChip>
</template>
<template #item.actions="{ item }">
<IconBtn size="small">
<VIcon icon="ri-more-2-line" />
<VMenu activator="parent">
<VList>
<VListItem
value="view"
:to="{ name: 'apps-ecommerce-order-details-id', params: { id: item.id } }"
>
View
</VListItem>
<VListItem
value="delete"
@click="deleteReview(item.id)"
>
Delete
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<!-- Pagination -->
<template #bottom>
<VDivider />
<div class="d-flex justify-end flex-wrap gap-x-6 px-2 py-1">
<div class="d-flex align-center gap-x-2 text-medium-emphasis text-base">
Rows Per Page:
<VSelect
v-model="itemsPerPage"
class="per-page-select"
variant="plain"
:items="[10, 20, 25, 50, 100]"
/>
</div>
<p class="d-flex align-center text-base text-high-emphasis me-2 mb-0">
{{ paginationMeta({ page, itemsPerPage }, totalReviews) }}
</p>
<div class="d-flex gap-x-2 align-center me-2">
<VBtn
class="flip-in-rtl"
icon="ri-arrow-left-s-line"
variant="text"
density="comfortable"
color="high-emphasis"
:disabled="page <= 1"
@click="page <= 1 ? page = 1 : page--"
/>
<VBtn
class="flip-in-rtl"
icon="ri-arrow-right-s-line"
density="comfortable"
variant="text"
color="high-emphasis"
:disabled="page >= Math.ceil(totalReviews / itemsPerPage)"
@click="page >= Math.ceil(totalReviews / itemsPerPage) ? page = Math.ceil(totalReviews / itemsPerPage) : page++ "
/>
</div>
</div>
</template>
</VDataTableServer>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/libs/apex-chart.scss";
</style>

View File

@@ -0,0 +1,413 @@
<script setup>
import avatar1 from '@images/avatars/avatar-1.png'
import product21 from '@images/ecommerce-images/product-21.png'
import product22 from '@images/ecommerce-images/product-22.png'
import product23 from '@images/ecommerce-images/product-23.png'
import product24 from '@images/ecommerce-images/product-24.png'
const route = useRoute('apps-ecommerce-order-details-id')
const isConfirmDialogVisible = ref(false)
const isUserInfoEditDialogVisible = ref(false)
const isEditAddressDialogVisible = ref(false)
const headers = [
{
title: 'Product',
key: 'productName',
},
{
title: 'Price',
key: 'price',
},
{
title: 'Quantity',
key: 'quantity',
},
{
title: 'Total',
key: 'total',
sortable: false,
},
]
const orderData = [
{
productName: 'OnePlus 7 Pro',
productImage: product21,
brand: 'OnePlus',
price: 799,
quantity: 1,
},
{
productName: 'Magic Mouse',
productImage: product22,
brand: 'Apple',
price: 89,
quantity: 1,
},
{
productName: 'Wooden Chair',
productImage: product23,
brand: 'insofer',
price: 289,
quantity: 2,
},
{
productName: 'Air Jorden',
productImage: product24,
brand: 'Nike',
price: 299,
quantity: 2,
},
]
</script>
<template>
<div>
<div class="d-flex justify-space-between align-center flex-wrap gap-y-4 mb-6">
<div>
<div class="d-flex gap-2 align-center mb-2 flex-wrap">
<h5 class="text-h5">
Order #{{ route.params.id }}
</h5>
<div class="d-flex gap-x-2">
<VChip
variant="tonal"
color="success"
size="small"
>
Paid
</VChip>
<VChip
variant="tonal"
color="info"
size="small"
>
Ready to Pickup
</VChip>
</div>
</div>
<div>
<span class="text-body-1">
Aug 17, 2020, 5:48 (ET)
</span>
</div>
</div>
<VBtn
variant="outlined"
color="error"
@click="isConfirmDialogVisible = !isConfirmDialogVisible"
>
Delete Order
</VBtn>
</div>
<VRow>
<VCol
cols="12"
md="8"
>
<!-- 👉 Order Details -->
<VCard class="mb-6">
<VCardItem>
<template #title>
<h5 class="text-h5">
Order Details
</h5>
</template>
<template #append>
<span class="text-primary cursor-pointer">Edit</span>
</template>
</VCardItem>
<VDataTable
:headers="headers"
:items="orderData"
item-value="productName"
show-select
class="text-no-wrap"
>
<template #item.productName="{ item }">
<div class="d-flex gap-x-3">
<VAvatar
size="34"
variant="tonal"
:image="item.productImage"
rounded
/>
<div class="d-flex flex-column align-center">
<h6 class="text-h6">
{{ item.productName }}
</h6>
<span class="text-sm text-start align-self-start">
{{ item.brand }}
</span>
</div>
</div>
</template>
<template #item.price="{ item }">
<span>${{ item.price }}</span>
</template>
<template #item.total="{ item }">
<span>
${{ item.price * item.quantity }}
</span>
</template>
<template #bottom />
</VDataTable>
<VDivider />
<VCardText>
<div class="d-flex align-end flex-column">
<table class="text-high-emphasis">
<tbody>
<tr>
<td width="200px">
Subtotal:
</td>
<td class="font-weight-medium">
$2,093
</td>
</tr>
<tr>
<td>Shipping fee: </td>
<td class="font-weight-medium">
$2
</td>
</tr>
<tr>
<td>Tax: </td>
<td class="font-weight-medium">
$28
</td>
</tr>
<tr>
<td class="font-weight-medium">
Total:
</td>
<td class="font-weight-medium">
$2,113
</td>
</tr>
</tbody>
</table>
</div>
</VCardText>
</VCard>
<!-- 👉 Shipping Activity -->
<VCard title="Shipping Activity">
<VCardText>
<VTimeline
truncate-line="both"
align="start"
side="end"
line-inset="10"
line-color="primary"
density="compact"
class="v-timeline-density-compact"
>
<VTimelineItem
dot-color="primary"
size="x-small"
>
<div class="d-flex justify-space-between align-center mb-3">
<span class="app-timeline-title">Order was placed (Order ID: #32543)</span>
<span class="app-timeline-meta">Tuesday 11:29 AM</span>
</div>
<p class="app-timeline-text mb-0">
Your order has been placed successfully
</p>
</VTimelineItem>
<VTimelineItem
dot-color="primary"
size="x-small"
>
<div class="d-flex justify-space-between align-center mb-3">
<span class="app-timeline-title">Pick-up</span>
<span class="app-timeline-meta">Wednesday 11:29 AM</span>
</div>
<p class="app-timeline-text mb-0">
Pick-up scheduled with courier
</p>
</VTimelineItem>
<VTimelineItem
dot-color="primary"
size="x-small"
>
<div class="d-flex justify-space-between align-center mb-3">
<span class="app-timeline-title">Dispatched</span>
<span class="app-timeline-meta">Thursday 8:15 AM</span>
</div>
<p class="app-timeline-text mb-0">
Item has been picked up by courier.
</p>
</VTimelineItem>
<VTimelineItem
dot-color="primary"
size="x-small"
>
<div class="d-flex justify-space-between align-center mb-3">
<span class="app-timeline-title">Package arrived</span>
<span class="app-timeline-meta">Saturday 15:20 AM</span>
</div>
<p class="app-timeline-text mb-0">
Package arrived at an Amazon facility, NY
</p>
</VTimelineItem>
<VTimelineItem
dot-color="primary"
size="x-small"
>
<div class="d-flex justify-space-between align-center mb-3">
<span class="app-timeline-title">Dispatched for delivery</span>
<span class="app-timeline-meta">Today 14:12 PM</span>
</div>
<p class="app-timeline-text mb-0">
Package has left an Amazon facility , NY
</p>
</VTimelineItem>
<VTimelineItem
dot-color="primary"
size="x-small"
>
<div class="d-flex justify-space-between align-center mb-3">
<span class="app-timeline-title">Delivery</span>
</div>
<p class="app-timeline-text mb-0">
Package will be delivered by tomorrow
</p>
</VTimelineItem>
</VTimeline>
</VCardText>
</VCard>
</VCol>
<VCol
cols="12"
md="4"
>
<!-- 👉 Customer Details -->
<VCard class="mb-6">
<VCardText class="d-flex flex-column gap-y-6">
<h5 class="text-h5">
Customer Details
</h5>
<div class="d-flex align-center">
<VAvatar
:image="avatar1"
class="me-3"
/>
<div>
<div class="text-body-1 text-high-emphasis font-weight-medium">
Shamus Tuttle
</div>
<span>Customer ID: #47389</span>
</div>
</div>
<div class="d-flex align-center">
<VAvatar
variant="tonal"
color="success"
class="me-3"
>
<VIcon icon="ri-shopping-cart-line" />
</VAvatar>
<h6 class="text-h6">
12 Orders
</h6>
</div>
<div class="d-flex flex-column gap-y-1">
<div class="d-flex justify-space-between gap-1 text-body-2">
<h6 class="text-h6">
Contact Info
</h6>
<span
class="text-base text-primary font-weight-medium cursor-pointer"
@click="isUserInfoEditDialogVisible = !isUserInfoEditDialogVisible"
>
Edit
</span>
</div>
<span>Email: Sheldon88@yahoo.com</span>
<span>Mobile: +1 (609) 972-22-22</span>
</div>
</VCardText>
</VCard>
<!-- 👉 Shipping Address -->
<VCard class="mb-6">
<VCardText>
<div class="d-flex align-center justify-space-between gap-1 mb-6">
<div class="text-body-1 text-high-emphasis font-weight-medium">
Shipping Address
</div>
<span
class="text-base text-primary font-weight-medium cursor-pointer"
@click="isEditAddressDialogVisible = !isEditAddressDialogVisible"
>Edit</span>
</div>
<div>
45 Rocker Terrace <br> Latheronwheel <br> KW5 8NW, London <br> UK
</div>
</VCardText>
</VCard>
<!-- 👉 Billing Address -->
<VCard>
<VCardText>
<div class="d-flex align-center justify-space-between gap-1 mb-3">
<div class="text-body-1 text-high-emphasis font-weight-medium">
Billing Address
</div>
<span
class="text-base text-primary font-weight-medium cursor-pointer"
@click="isEditAddressDialogVisible = !isEditAddressDialogVisible"
>Edit</span>
</div>
<div>
45 Rocker Terrace <br> Latheronwheel <br> KW5 8NW, London <br> UK
</div>
<div class="mt-6">
<h6 class="text-h6 mb-1">
Mastercard
</h6>
<div class="text-base">
Card Number: ******4291
</div>
</div>
</VCardText>
</VCard>
</VCol>
</VRow>
<ConfirmDialog
v-model:isDialogVisible="isConfirmDialogVisible"
confirmation-question="Are you sure to cancel your Order?"
cancel-msg="Order cancelled!!"
cancel-title="Cancelled"
confirm-msg="Your order cancelled successfully."
confirm-title="Cancelled!"
/>
<UserInfoEditDialog v-model:isDialogVisible="isUserInfoEditDialogVisible" />
<AddEditAddressDialog v-model:isDialogVisible="isEditAddressDialogVisible" />
</div>
</template>

View File

@@ -0,0 +1,395 @@
<script setup>
import mastercard from '@images/logos/mastercard.png'
import paypal from '@images/logos/paypal.png'
const widgetData = ref([
{
title: 'Pending Payment',
value: 56,
icon: 'ri-calendar-2-line',
},
{
title: 'Completed',
value: 12689,
icon: 'ri-check-double-line',
},
{
title: 'Refunded',
value: 124,
icon: 'ri-wallet-3-line',
},
{
title: 'Failed',
value: 32,
icon: 'ri-error-warning-line',
},
])
const searchQuery = ref('')
// Data table options
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
// Data table Headers
const headers = [
{
title: 'Order',
key: 'order',
},
{
title: 'Date',
key: 'date',
},
{
title: 'Customers',
key: 'customers',
},
{
title: 'Payment',
key: 'payment',
sortable: false,
},
{
title: 'Status',
key: 'status',
},
{
title: 'Method',
key: 'method',
sortable: false,
},
{
title: 'Actions',
key: 'actions',
sortable: false,
},
]
const updateOptions = options => {
page.value = options.page
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const resolvePaymentStatus = status => {
if (status === 1)
return {
text: 'Paid',
color: 'success',
}
if (status === 2)
return {
text: 'Pending',
color: 'warning',
}
if (status === 3)
return {
text: 'Cancelled',
color: 'secondary',
}
if (status === 4)
return {
text: 'Failed',
color: 'error',
}
}
const resolveStatus = status => {
if (status === 'Delivered')
return {
text: 'Delivered',
color: 'success',
}
if (status === 'Out for Delivery')
return {
text: 'Out for Delivery',
color: 'primary',
}
if (status === 'Ready to Pickup')
return {
text: 'Ready to Pickup',
color: 'info',
}
if (status === 'Dispatched')
return {
text: 'Dispatched',
color: 'warning',
}
}
const {
data: ordersData,
execute: fetchOrders,
} = await useApi(createUrl('/apps/ecommerce/orders', {
query: {
q: searchQuery,
page,
itemsPerPage,
sortBy,
orderBy,
},
}))
const orders = computed(() => ordersData.value.orders)
const totalOrder = computed(() => ordersData.value.total)
const deleteOrder = async id => {
await $api(`/apps/ecommerce/orders/${ id }`, { method: 'DELETE' })
fetchOrders()
}
</script>
<template>
<div>
<VCard class="mb-6">
<VCardText class="px-2">
<VRow>
<template
v-for="(data, index) in widgetData"
:key="index"
>
<VCol
cols="12"
sm="6"
md="3"
class="px-6"
>
<div
class="d-flex justify-space-between"
:class="$vuetify.display.xs ? 'product-widget' : $vuetify.display.sm ? index < 2 ? 'product-widget' : '' : ''"
>
<div class="d-flex flex-column gap-y-1">
<h4 class="text-h4">
{{ data.value }}
</h4>
<span class="text-base text-capitalize">
{{ data.title }}
</span>
</div>
<VAvatar
variant="tonal"
rounded
size="42"
>
<VIcon
:icon="data.icon"
size="26"
/>
</VAvatar>
</div>
</VCol>
<VDivider
v-if="$vuetify.display.mdAndUp ? index !== widgetData.length - 1 : $vuetify.display.smAndUp ? index % 2 === 0 : false"
vertical
inset
length="100"
/>
</template>
</VRow>
</VCardText>
</VCard>
<VCard>
<VCardText>
<div class="d-flex justify-sm-space-between align-center justify-start flex-wrap gap-4">
<VTextField
v-model="searchQuery"
placeholder="Search Order"
density="compact"
style=" max-inline-size: 200px; min-inline-size: 200px;"
/>
<VBtn
prepend-icon="ri-upload-2-line"
variant="outlined"
color="secondary"
>
Export
</VBtn>
</div>
</VCardText>
<VDataTableServer
v-model:items-per-page="itemsPerPage"
v-model:page="page"
:headers="headers"
:items="orders"
item-value="order"
:items-length="totalOrder"
show-select
class="text-no-wrap"
@update:options="updateOptions"
>
<!-- Order ID -->
<template #item.order="{ item }">
<RouterLink :to="{ name: 'apps-ecommerce-order-details-id', params: { id: item.order } }">
#{{ item.order }}
</RouterLink>
</template>
<!-- Date -->
<template #item.date="{ item }">
{{ new Date(item.date).toDateString() }}
</template>
<!-- Customers -->
<template #item.customers="{ item }">
<div class="d-flex align-center">
<VAvatar
size="34"
:variant="!item.avatar.length ? 'tonal' : undefined"
:rounded="1"
class="me-4"
>
<VImg
v-if="item.avatar"
:src="item.avatar"
/>
<span
v-else
class="font-weight-medium"
>{{ avatarText(item.customer) }}</span>
</VAvatar>
<div class="d-flex flex-column">
<RouterLink :to="{ name: 'pages-user-profile-tab', params: { tab: 'profile' } }">
<div class="text-high-emphasis font-weight-medium">
{{ item.customer }}
</div>
</RouterLink>
<span class="text-sm">{{ item.email }}</span>
</div>
</div>
</template>
<!-- Payments -->
<template #item.payment="{ item }">
<div
:class="`text-${resolvePaymentStatus(item.payment)?.color}`"
class="d-flex align-center font-weight-medium"
>
<VIcon
size="10"
icon="ri-circle-fill"
class="me-2"
/>
<span>{{ resolvePaymentStatus(item.payment)?.text }}</span>
</div>
</template>
<!-- Status -->
<template #item.status="{ item }">
<VChip
v-bind="resolveStatus(item.status)"
size="small"
/>
</template>
<!-- Method -->
<template #item.method="{ item }">
<div class="d-flex align-start gap-x-2">
<img :src="item.method === 'mastercard' ? mastercard : paypal">
<div>
<VIcon
icon="ri-more-line"
class="me-2"
/>
<span v-if="item.method === 'mastercard'">
{{ item.methodNumber }}
</span>
<span v-else>
@gmail.com
</span>
</div>
</div>
</template>
<!-- Actions -->
<template #item.actions="{ item }">
<IconBtn size="small">
<VIcon icon="ri-more-2-line" />
<VMenu activator="parent">
<VList>
<VListItem value="view">
<RouterLink
:to="{ name: 'apps-ecommerce-order-details-id', params: { id: item.order } }"
class="text-high-emphasis"
>
View
</RouterLink>
</VListItem>
<VListItem
value="delete"
@click="deleteOrder(item.id)"
>
Delete
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<!-- Pagination -->
<template #bottom>
<VDivider />
<div class="d-flex justify-end flex-wrap gap-x-6 px-2 py-1">
<div class="d-flex align-center gap-x-2 text-medium-emphasis text-base">
Rows Per Page:
<VSelect
v-model="itemsPerPage"
class="per-page-select"
variant="plain"
:items="[10, 20, 25, 50, 100]"
/>
</div>
<p class="d-flex align-center text-base text-high-emphasis me-2 mb-0">
{{ paginationMeta({ page, itemsPerPage }, totalOrder) }}
</p>
<div class="d-flex gap-x-2 align-center me-2">
<VBtn
class="flip-in-rtl"
icon="ri-arrow-left-s-line"
variant="text"
density="comfortable"
color="high-emphasis"
:disabled="page <= 1"
@click="page <= 1 ? page = 1 : page--"
/>
<VBtn
class="flip-in-rtl"
icon="ri-arrow-right-s-line"
density="comfortable"
variant="text"
color="high-emphasis"
:disabled="page >= Math.ceil(totalOrder / itemsPerPage)"
@click="page >= Math.ceil(totalOrder / itemsPerPage) ? page = Math.ceil(totalOrder / itemsPerPage) : page++ "
/>
</div>
</div>
</template>
</VDataTableServer>
</VCard>
</div>
</template>
<style lang="scss" scoped>
#customer-link{
&:hover{
color: '#000' !important
}
}
.product-widget{
border-block-end: 1px solid rgba(var(--v-theme-on-surface), var(--v-border-opacity));
padding-block-end: 1rem;
}
</style>

View File

@@ -0,0 +1,688 @@
<script setup>
import {
useDropZone,
useFileDialog,
useObjectUrl,
} from '@vueuse/core'
import { ref } from 'vue'
const optionCounter = ref(1)
const dropZoneRef = ref()
const fileData = ref([])
const { open, onChange } = useFileDialog({ accept: 'image/*' })
function onDrop(DroppedFiles) {
DroppedFiles?.forEach(file => {
if (file.type.slice(0, 6) !== 'image/') {
alert('Only image files are allowed')
return
}
fileData.value.push({
file,
url: useObjectUrl(file).value ?? '',
})
})
}
onChange(selectedFiles => {
if (!selectedFiles)
return
for (const file of selectedFiles) {
fileData.value.push({
file,
url: useObjectUrl(file).value ?? '',
})
}
})
useDropZone(dropZoneRef, onDrop)
const content = ref(`<p>
This is a radically reduced version of tiptap. It has support for a document, with paragraphs and text. That's it. It's probably too much for real minimalists though.
</p>
<p>
The paragraph extension is not really required, but you need at least one node. Sure, that node can be something different.
</p>`)
const activeTab = ref('Restock')
const shippingList = [
{
desc: 'You\'ll be responsible for product delivery.Any damage or delay during shipping may cost you a Damage fee',
title: 'Fulfilled by Seller',
value: 'Fulfilled by Seller',
},
{
desc: 'Your product, Our responsibility.For a measly fee, we will handle the delivery process for you.',
title: 'Fulfilled by Company name',
value: 'Fulfilled by Company name',
},
]
const shippingType = ref('Fulfilled by Company name')
const deliveryType = ref('Worldwide delivery')
const selectedAttrs = ref([
'Biodegradable',
'Expiry Date',
])
const inventoryTabsData = [
{
icon: 'ri-add-line',
title: 'Restock',
value: 'Restock',
},
{
icon: 'ri-flight-takeoff-line',
title: 'Shipping',
value: 'Shipping',
},
{
icon: 'ri-map-pin-line',
title: 'Global Delivery',
value: 'Global Delivery',
},
{
icon: 'ri-attachment-2',
title: 'Attributes',
value: 'Attributes',
},
{
icon: 'ri-lock-unlock-line',
title: 'Advanced',
value: 'Advanced',
},
]
const inStock = ref(true)
const isTaxable = ref(true)
</script>
<template>
<div>
<div class="d-flex flex-wrap justify-space-between gap-4 mb-6">
<div class="d-flex flex-column justify-center">
<h4 class="text-h4">
Add a new product
</h4>
<span class="text-medium-emphasis">Orders placed across your store</span>
</div>
<div class="d-flex gap-4 align-center flex-wrap">
<VBtn
variant="outlined"
color="secondary"
>
Discard
</VBtn>
<VBtn
variant="outlined"
color="primary"
>
Save Draft
</VBtn>
<VBtn>Publish Product</VBtn>
</div>
</div>
<VRow>
<VCol md="8">
<!-- 👉 Product Information -->
<VCard
class="mb-6"
title="Product Information"
>
<VCardText>
<VRow>
<VCol cols="12">
<VTextField
label="Product Name"
placeholder="iPhone 14"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
label="SKU"
placeholder="FXSK123U"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
label="Barcode"
placeholder="0123-4567"
/>
</VCol>
<VCol>
<VLabel class="mb-1">
Description (Optional)
</VLabel>
<TiptapEditor
v-model="content"
class="border rounded"
/>
</VCol>
</VRow>
</VCardText>
</VCard>
<!-- 👉 Product Image -->
<VCard class="mb-6">
<VCardItem>
<template #title>
Product Image
</template>
<template #append>
<div class="text-primary font-weight-medium cursor-pointer">
Add Media from URL
</div>
</template>
</VCardItem>
<VCardText>
<div class="flex">
<div class="w-full h-auto relative">
<div
ref="dropZoneRef"
class="cursor-pointer"
@click="() => open()"
>
<div
v-if="fileData.length === 0"
class="d-flex flex-column justify-center align-center gap-y-2 pa-12 border-dashed drop-zone"
>
<VAvatar
variant="tonal"
color="secondary"
rounded
>
<VIcon icon="ri-upload-2-line" />
</VAvatar>
<h4 class="text-h4">
Drag and Drop Your Image Here.
</h4>
<span class="text-disabled">or</span>
<VBtn variant="outlined">
Browse Images
</VBtn>
</div>
<div
v-else
class="d-flex justify-center align-center gap-3 pa-8 border-dashed drop-zone flex-wrap"
>
<VRow class="match-height w-100">
<template
v-for="(item, index) in fileData"
:key="index"
>
<VCol
cols="12"
sm="4"
>
<VCard
:ripple="false"
border
>
<VCardText
class="d-flex flex-column"
@click.stop
>
<VImg
:src="item.url"
width="200px"
height="150px"
class="w-100 mx-auto"
/>
<div class="mt-2">
<span class="clamp-text text-wrap">
{{ item.file.name }}
</span>
<span>
{{ item.file.size / 1000 }} KB
</span>
</div>
</VCardText>
<VCardActions>
<VBtn
variant="text"
block
@click.stop="fileData.splice(index, 1)"
>
Remove File
</VBtn>
</VCardActions>
</VCard>
</VCol>
</template>
</VRow>
</div>
</div>
</div>
</div>
</VCardText>
</VCard>
<!-- 👉 Variants -->
<VCard
title="Variants"
class="mb-6"
>
<VCardText>
<template
v-for="i in optionCounter"
:key="i"
>
<VRow>
<VCol
cols="12"
md="4"
>
<VSelect
:items="['Size', 'Color', 'Weight', 'Smell']"
placeholder="Select Variant"
label="Select Variant"
/>
</VCol>
<VCol
cols="12"
md="8"
>
<VTextField
label="Variant Value"
type="number"
placeholder="Enter Variant Value"
/>
</VCol>
</VRow>
</template>
<VBtn
class="mt-6"
@click="optionCounter++"
>
Add another option
</VBtn>
</VCardText>
</VCard>
<!-- 👉 Inventory -->
<VCard
title="Inventory"
class="inventory-card"
>
<VCardText>
<VRow>
<VCol
cols="12"
md="4"
>
<VTabs
v-model="activeTab"
direction="vertical"
color="primary"
class="v-tabs-pill"
>
<VTab
v-for="(tab, index) in inventoryTabsData"
:key="index"
:value="tab.value"
>
<VIcon
:icon="tab.icon"
class="me-2"
/>
<span>{{ tab.title }}</span>
</VTab>
</VTabs>
</VCol>
<VDivider
:vertical="$vuetify.display.mdAndUp ? true : false"
inset
/>
<VCol
cols="12"
md="8"
>
<VWindow
v-model="activeTab"
class="w-100"
:touch="false"
>
<VWindowItem value="Restock">
<div class="d-flex flex-column gap-y-4">
<div class="text-body-1 font-weight-medium">
Options
</div>
<div class="d-flex gap-x-4 align-center">
<VTextField
label="Add to stock"
placeholder="100"
density="compact"
/>
<VBtn prepend-icon="ri-check-line">
Confirm
</VBtn>
</div>
<div class="d-flex flex-column gap-2 text-high-emphasis">
<div>
Product in stock now: 54
</div>
<div>
Product in transit: 390
</div>
<div>
Last time restocked: 24th June, 2022
</div>
<div>
Total stock over lifetime: 2,430
</div>
</div>
</div>
</VWindowItem>
<VWindowItem value="Shipping">
<VRadioGroup v-model="shippingType">
<template #label>
<span class="font-weight-medium mb-1">Shipping Type</span>
</template>
<VRadio
v-for="item in shippingList"
:key="item.value"
:value="item.value"
class="mb-4 ps-1"
inline
>
<template #label>
<div>
<div class="text-high-emphasis font-weight-medium mb-1">
{{ item.title }}
</div>
<div class="text-sm">
{{ item.desc }}
</div>
</div>
</template>
</VRadio>
</VRadioGroup>
</VWindowItem>
<VWindowItem value="Global Delivery">
<div>
<VRadioGroup v-model="deliveryType">
<template #label>
<span class="font-weight-medium mb-1">Global Delivery</span>
</template>
<VRadio
value="Worldwide delivery"
class="mb-4 ps-1"
>
<template #label>
<div>
<div class="text-high-emphasis font-weight-medium mb-1">
Worldwide delivery
</div>
<div class="text-sm">
Only available with Shipping method:
<span class="text-primary">
Fulfilled by Company name
</span>
</div>
</div>
</template>
</VRadio>
<VRadio
value="Selected Countries"
class="mb-4 ps-1"
>
<template #label>
<div>
<div class="text-high-emphasis font-weight-medium mb-1">
Selected Countries
</div>
<VTextField
placeholder="USA"
style="min-inline-size: 200px;"
/>
</div>
</template>
</VRadio>
<VRadio>
<template #label>
<div>
<div class="text-high-emphasis font-weight-medium mb-1">
Local delivery
</div>
<div class="text-sm">
Deliver to your country of residence
<span class="text-primary">
Change profile address
</span>
</div>
</div>
</template>
</VRadio>
</VRadioGroup>
</div>
</VWindowItem>
<VWindowItem value="Attributes">
<div class="mb-2 text-base font-weight-medium">
Attributes
</div>
<div>
<VCheckbox
v-model="selectedAttrs"
label="Fragile Product"
value="Fragile Product"
class="ps-3"
/>
<VCheckbox
v-model="selectedAttrs"
value="Biodegradable"
label="Biodegradable"
class="ps-3"
/>
<VCheckbox
v-model="selectedAttrs"
value="Frozen Product"
class="ps-3"
>
<template #label>
<div class="d-flex flex-column mb-1">
<div>Frozen Product</div>
<VTextField
placeholder="40 C"
type="number"
style="min-inline-size: 250px;"
/>
</div>
</template>
</VCheckbox>
<VCheckbox
v-model="selectedAttrs"
value="Expiry Date"
class="ps-3"
>
<template #label>
<div class="d-flex flex-column mb-1">
<div>Expiry Date of Product</div>
<AppDateTimePicker
model-value="2025-06-14"
placeholder="Select a Date"
/>
</div>
</template>
</VCheckbox>
</div>
</VWindowItem>
<VWindowItem value="Advanced">
<div class="mb-3 text-base font-weight-medium">
Advanced
</div>
<VRow>
<VCol
cols="12"
sm="6"
md="7"
>
<VSelect
style="min-inline-size: 200px;"
label="Product ID Type"
placeholder="Select Product Type"
:items="['ISBN', 'UPC', 'EAN', 'JAN']"
/>
</VCol>
<VCol
cols="12"
sm="6"
md="5"
>
<VTextField
label="Product Id"
placeholder="100023"
type="number"
/>
</VCol>
</VRow>
</VWindowItem>
</VWindow>
</VCol>
</VRow>
</VCardText>
</VCard>
</VCol>
<VCol
md="4"
cols="12"
>
<!-- 👉 Pricing -->
<VCard
title="Pricing"
class="mb-6"
>
<VCardText>
<VTextField
label="Base Price"
placeholder="iPhone 14"
class="mb-6"
/>
<VTextField
label="Discounted Price"
placeholder="$499"
class="mb-4"
/>
<VCheckbox
v-model="isTaxable"
label="Charge Tax on this product"
/>
<VDivider class="my-2" />
<div class="d-flex flex-raw align-center justify-space-between ">
<span>In stock</span>
<VSwitch
v-model="inStock"
density="compact"
/>
</div>
</VCardText>
</VCard>
<!-- 👉 Organize -->
<VCard title="Organize">
<VCardText>
<div class="d-flex flex-column gap-y-4">
<VSelect
placeholder="Select Vendor"
label="Vendor"
:items="['Men\'s Clothing', 'Women\'s Clothing', 'Kid\'s Clothing']"
/>
<VSelect
placeholder="Select Category"
label="Category"
:items="['Household', 'Office', 'Electronics', 'Management', 'Automotive']"
>
<template #append>
<IconBtn
icon="ri-add-line"
variant="outlined"
color="primary"
rounded
/>
</template>
</VSelect>
<VSelect
placeholder="Select Collection"
label="Collection"
:items="['Men\'s Clothing', 'Women\'s Clothing', 'Kid\'s Clothing']"
/>
<VSelect
placeholder="Select Status"
label="Status"
:items="['Published', 'Inactive', 'Scheduled']"
/>
<VTextField
label="Tags"
placeholder="Fashion, Trending, Summer"
/>
</div>
</VCardText>
</VCard>
</VCol>
</VRow>
</div>
</template>
<style lang="scss" scoped>
.drop-zone {
border: 1px dashed rgba(var(--v-theme-on-surface), 0.12);
border-radius: 6px;
}
</style>
<style lang="scss">
.inventory-card{
.v-radio-group,
.v-checkbox {
.v-selection-control {
align-items: start !important;
}
.v-label.custom-input {
border: none !important;
}
}
}
.ProseMirror{
p{
margin-block-end: 0;
}
padding: 0.5rem;
outline: none;
p.is-editor-empty:first-child::before {
block-size: 0;
color: #adb5bd;
content: attr(data-placeholder);
float: inline-start;
pointer-events: none;
}
}
</style>

View File

@@ -0,0 +1,310 @@
<script setup>
import ECommerceAddCategoryDrawer from '@/views/apps/ecommerce/EcommerceAddCategoryDrawer.vue'
import product1 from '@images/ecommerce-images/product-1.png'
import product10 from '@images/ecommerce-images/product-10.png'
import product11 from '@images/ecommerce-images/product-11.png'
import product12 from '@images/ecommerce-images/product-12.png'
import product14 from '@images/ecommerce-images/product-14.png'
import product17 from '@images/ecommerce-images/product-17.png'
import product19 from '@images/ecommerce-images/product-19.png'
import product2 from '@images/ecommerce-images/product-2.png'
import product25 from '@images/ecommerce-images/product-25.png'
import product28 from '@images/ecommerce-images/product-28.png'
import product9 from '@images/ecommerce-images/product-9.png'
const categoryData = [
{
categoryTitle: 'Smart Phone',
description: 'Choose from wide range of smartphones online at best prices.',
totalProduct: 12548,
totalEarning: 98784,
image: product1,
},
{
categoryTitle: 'Clothing, Shoes, and jewellery',
description: 'Fashion for a wide selection of clothing, shoes, jewellery and watches.',
totalProduct: 4689,
totalEarning: 45627,
image: product9,
},
{
categoryTitle: 'Home and Kitchen',
description: 'Browse through the wide range of Home and kitchen products.',
totalProduct: 12548,
totalEarning: 98784,
image: product10,
},
{
categoryTitle: 'Beauty and Personal Care',
description: 'Explore beauty and personal care products, shop makeup and etc.',
totalProduct: 12548,
totalEarning: 98784,
image: product19,
},
{
categoryTitle: 'Books',
description: 'Over 25 million titles across categories such as business and etc.',
totalProduct: 12548,
totalEarning: 98784,
image: product25,
},
{
categoryTitle: 'Games',
description: 'Every month, get exclusive in-game loot, free games, a free subscription.',
totalProduct: 12548,
totalEarning: 98784,
image: product12,
},
{
categoryTitle: 'Baby Products',
description: 'Buy baby products across different categories from top brands.',
totalProduct: 12548,
totalEarning: 98784,
image: product14,
},
{
categoryTitle: 'Growsari',
description: 'Shop grocery Items through at best prices in India.',
totalProduct: 12548,
totalEarning: 98784,
image: product28,
},
{
categoryTitle: 'Computer Accessories',
description: 'Enhance your computing experience with our range of computer accessories.',
totalProduct: 9876,
totalEarning: 65421,
image: product17,
},
{
categoryTitle: 'Fitness Tracker',
description: 'Monitor your health and fitness goals with our range of advanced fitness trackers.',
totalProduct: 1987,
totalEarning: 32067,
image: product10,
},
{
categoryTitle: 'Smart Home Devices',
description: 'Transform your home into a smart home with our innovative smart home devices.',
totalProduct: 2345,
totalEarning: 87654,
image: product11,
},
{
categoryTitle: 'Audio Speakers',
description: 'Immerse yourself in rich audio quality with our wide range of speakers.',
totalProduct: 5678,
totalEarning: 32145,
image: product2,
},
]
const headers = [
{
title: 'Categories',
key: 'categoryTitle',
},
{
title: 'Total Products',
key: 'totalProduct',
align: 'end',
},
{
title: 'Total Earning',
key: 'totalEarning',
align: 'end',
},
{
title: 'Action',
key: 'actions',
sortable: false,
},
]
const itemsPerPage = ref(10)
const searchQuery = ref('')
const isAddProductDrawerOpen = ref(false)
const page = ref(1)
const updateOptions = options => {
page.value = options.page
}
</script>
<template>
<div>
<VCard>
<VCardText>
<div class="d-flex justify-md-space-between flex-wrap gap-4 justify-center">
<VTextField
v-model="searchQuery"
placeholder="Search"
density="compact"
style="max-inline-size: 280px; min-inline-size: 200px;"
/>
<div class="d-flex align-center flex-wrap gap-4">
<VBtn
prepend-icon="ri-upload-2-line"
color="secondary"
variant="outlined"
>
Export
</VBtn>
<VBtn
prepend-icon="ri-add-line"
@click="isAddProductDrawerOpen = !isAddProductDrawerOpen"
>
Add Category
</VBtn>
</div>
</div>
</VCardText>
<VDataTable
v-model:items-per-page="itemsPerPage"
:headers="headers"
:page="page"
:items="categoryData"
item-value="categoryTitle"
:search="searchQuery"
show-select
class="text-no-wrap category-table"
@update:options="updateOptions"
>
<template #item.actions>
<IconBtn size="small">
<VIcon icon="ri-edit-box-line" />
</IconBtn>
<MoreBtn
size="small"
:menu-list="[
{
title: 'Download',
value: 'download',
prependIcon: 'ri-download-line',
},
{
title: 'Edit',
value: 'edit',
prependIcon: 'ri-pencil-line',
},
{
title: 'Duplicate',
value: 'duplicate',
prependIcon: 'ri-stack-line',
},
]"
item-props
/>
</template>
<template #item.categoryTitle="{ item }">
<div class="d-flex gap-x-3">
<VAvatar
variant="tonal"
rounded
size="38"
>
<img
:src="item.image"
:alt="item.categoryTitle"
width="38"
height="38"
>
</VAvatar>
<div>
<p class="text-high-emphasis font-weight-medium mb-0">
{{ item.categoryTitle }}
</p>
<div class="text-body-2">
{{ item.description }}
</div>
</div>
</div>
</template>
<template #item.totalEarning="{ item }">
<div class="text-end pe-4">
{{ (item.totalEarning).toLocaleString("en-IN", { style: "currency", currency: 'USD' }) }}
</div>
</template>
<template #item.totalProduct="{ item }">
<div class="text-end pe-4">
{{ (item.totalProduct).toLocaleString() }}
</div>
</template>
<!-- Pagination -->
<template #bottom>
<VDivider />
<div class="d-flex justify-end flex-wrap gap-x-6 px-2 py-1">
<div class="d-flex align-center gap-x-2 text-medium-emphasis text-base">
Rows Per Page:
<VSelect
v-model="itemsPerPage"
class="per-page-select"
variant="plain"
:items="[10, 20, 25, 50, 100]"
/>
</div>
<p class="d-flex align-center text-base text-high-emphasis me-2 mb-0">
{{ paginationMeta({ page, itemsPerPage }, categoryData.length) }}
</p>
<div class="d-flex gap-x-2 align-center me-2">
<VBtn
class="flip-in-rtl"
icon="ri-arrow-left-s-line"
variant="text"
density="comfortable"
color="high-emphasis"
:disabled="page <= 1"
@click="page <= 1 ? page = 1 : page--"
/>
<VBtn
class="flip-in-rtl"
icon="ri-arrow-right-s-line"
density="comfortable"
variant="text"
color="high-emphasis"
:disabled="page >= Math.ceil(categoryData.length / itemsPerPage)"
@click="page >= Math.ceil(categoryData.length / itemsPerPage) ? page = Math.ceil(categoryData.length / itemsPerPage) : page++ "
/>
</div>
</div>
</template>
</VDataTable>
</VCard>
<ECommerceAddCategoryDrawer v-model:isDrawerOpen="isAddProductDrawerOpen" />
</div>
</template>
<style lang="scss">
.ProseMirror-focused{
border: none;
}
.category-table.v-table.v-data-table{
.v-table__wrapper{
table{
thead{
tr{
th.v-data-table-column--align-end{
.v-data-table-header__content{
flex-direction: row;
justify-content: end;
}
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,518 @@
<script setup>
const widgetData = ref([
{
title: 'In-Store Sales',
value: '$5,345',
icon: 'ri-home-6-line',
desc: '5k orders',
change: 5.7,
},
{
title: 'Website Sales',
value: '$74,347',
icon: 'ri-computer-line',
desc: '21k orders',
change: 12.4,
},
{
title: 'Discount',
value: '$14,235',
icon: 'ri-gift-line',
desc: '6k orders',
},
{
title: 'Affiliate',
value: '$8,345',
icon: 'ri-money-dollar-circle-line',
desc: '150 orders',
change: -3.5,
},
])
const headers = [
{
title: 'Product',
key: 'product',
},
{
title: 'Category',
key: 'category',
},
{
title: 'Stock',
key: 'stock',
sortable: false,
},
{
title: 'SKU',
key: 'sku',
},
{
title: 'Price',
key: 'price',
},
{
title: 'QTY',
key: 'qty',
},
{
title: 'Status',
key: 'status',
},
{
title: 'Actions',
key: 'actions',
sortable: false,
},
]
const selectedStatus = ref()
const selectedCategory = ref()
const selectedStock = ref()
const searchQuery = ref('')
const status = ref([
{
title: 'Scheduled',
value: 'Scheduled',
},
{
title: 'Publish',
value: 'Published',
},
{
title: 'Inactive',
value: 'Inactive',
},
])
const categories = ref([
{
title: 'Accessories',
value: 'Accessories',
},
{
title: 'Home Decor',
value: 'Home Decor',
},
{
title: 'Electronics',
value: 'Electronics',
},
{
title: 'Shoes',
value: 'Shoes',
},
{
title: 'Office',
value: 'Office',
},
{
title: 'Games',
value: 'Games',
},
])
const stockStatus = ref([
{
title: 'In Stock',
value: true,
},
{
title: 'Out of Stock',
value: false,
},
])
// Data table options
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
const updateOptions = options => {
page.value = options.page
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const resolveCategory = category => {
if (category === 'Accessories')
return {
color: 'error',
icon: 'ri-headphone-line',
}
if (category === 'Home Decor')
return {
color: 'info',
icon: 'ri-home-6-line',
}
if (category === 'Electronics')
return {
color: 'primary',
icon: 'ri-computer-line',
}
if (category === 'Shoes')
return {
color: 'success',
icon: 'ri-footprint-line',
}
if (category === 'Office')
return {
color: 'warning',
icon: 'ri-briefcase-line',
}
if (category === 'Games')
return {
color: 'primary',
icon: 'ri-gamepad-line',
}
}
const resolveStatus = statusMsg => {
if (statusMsg === 'Scheduled')
return {
text: 'Scheduled',
color: 'warning',
}
if (statusMsg === 'Published')
return {
text: 'Publish',
color: 'success',
}
if (statusMsg === 'Inactive')
return {
text: 'Inactive',
color: 'error',
}
}
const {
data: productsData,
execute: fetchProducts,
} = await useApi(createUrl('/apps/ecommerce/products', {
query: {
q: searchQuery,
stock: selectedStock,
category: selectedCategory,
status: selectedStatus,
page,
itemsPerPage,
sortBy,
orderBy,
},
}))
const products = computed(() => productsData.value.products)
const totalProduct = computed(() => productsData.value.total)
const deleteProduct = async id => {
await $api(`apps/ecommerce/products/${ id }`, { method: 'DELETE' })
fetchProducts()
}
</script>
<template>
<div>
<!-- 👉 widgets -->
<VCard class="mb-6">
<VCardText class="px-2">
<VRow>
<template
v-for="(data, index) in widgetData"
:key="index"
>
<VCol
cols="12"
sm="6"
md="3"
class="px-6"
>
<div
class="d-flex justify-space-between"
:class="$vuetify.display.xs ? 'product-widget' : $vuetify.display.sm ? index < 2 ? 'product-widget' : '' : ''"
>
<div class="d-flex flex-column gap-y-1">
<p class="text-capitalize mb-0">
{{ data.title }}
</p>
<h6 class="text-h4">
{{ data.value }}
</h6>
<div class="d-flex align-center">
<div class="text-no-wrap me-2">
{{ data.desc }}
</div>
<VChip
v-if="data.change"
size="small"
:color="data.change > 0 ? 'success' : 'error'"
>
{{ prefixWithPlus(data.change) }}%
</VChip>
</div>
</div>
<VAvatar
variant="tonal"
rounded
size="44"
>
<VIcon
:icon="data.icon"
size="28"
color="high-emphasis"
/>
</VAvatar>
</div>
</VCol>
<VDivider
v-if="$vuetify.display.mdAndUp ? index !== widgetData.length - 1 : $vuetify.display.smAndUp ? index % 2 === 0 : false"
vertical
inset
length="100"
/>
</template>
</VRow>
</VCardText>
</VCard>
<!-- 👉 products -->
<VCard title="Filters">
<VCardText>
<VRow>
<!-- 👉 Select Status -->
<VCol
cols="12"
sm="4"
>
<VSelect
v-model="selectedStatus"
label="Select Status"
placeholder="Select Status"
:items="status"
clearable
clear-icon="ri-close-line"
/>
</VCol>
<!-- 👉 Select Category -->
<VCol
cols="12"
sm="4"
>
<VSelect
v-model="selectedCategory"
label="Category"
placeholder="Select Category"
:items="categories"
clearable
clear-icon="ri-close-line"
/>
</VCol>
<!-- 👉 Select Stock Status -->
<VCol
cols="12"
sm="4"
>
<VSelect
v-model="selectedStock"
label="Stock"
placeholder="Stock"
:items="stockStatus"
clearable
clear-icon="ri-close-line"
/>
</VCol>
</VRow>
</VCardText>
<VDivider />
<VCardText class="d-flex flex-wrap gap-4">
<div class="d-flex align-center">
<!-- 👉 Search -->
<VTextField
v-model="searchQuery"
placeholder="Search Product"
style="inline-size: 200px;"
density="compact"
class="me-3"
/>
</div>
<VSpacer />
<div class="d-flex gap-x-4">
<!-- 👉 Export button -->
<div>
<VBtn
variant="outlined"
color="secondary"
prepend-icon="ri-external-link-line"
>
Export
</VBtn>
</div>
<VBtn
color="primary"
prepend-icon="ri-add-line"
@click="$router.push('/apps/ecommerce/product/add')"
>
Add Product
</VBtn>
</div>
</VCardText>
<!-- 👉 Datatable -->
<VDataTableServer
v-model:items-per-page="itemsPerPage"
v-model:page="page"
:headers="headers"
show-select
:items="products"
:items-length="totalProduct"
class="text-no-wrap rounded-0"
@update:options="updateOptions"
>
<!-- product -->
<template #item.product="{ item }">
<div class="d-flex align-center gap-x-4">
<VAvatar
v-if="item.image"
size="38"
variant="tonal"
rounded
:image="item.image"
/>
<div class="d-flex flex-column">
<span class="text-base text-high-emphasis font-weight-medium">{{ item.productName }}</span>
<span class="text-sm">{{ item.productBrand }}</span>
</div>
</div>
</template>
<!-- category -->
<template #item.category="{ item }">
<VAvatar
size="30"
variant="tonal"
:color="resolveCategory(item.category)?.color"
class="me-4"
>
<VIcon
:icon="resolveCategory(item.category)?.icon"
size="18"
/>
</VAvatar>
<span class="text-base text-high-emphasis">{{ item.category }}</span>
</template>
<!-- stock -->
<template #item.stock="{ item }">
<VSwitch :model-value="item.stock" />
</template>
<!-- status -->
<template #item.status="{ item }">
<VChip
v-bind="resolveStatus(item.status)"
size="small"
/>
</template>
<!-- Actions -->
<template #item.actions="{ item }">
<IconBtn size="small">
<VIcon icon="ri-edit-box-line" />
</IconBtn>
<IconBtn size="small">
<VIcon icon="ri-more-2-fill" />
<VMenu activator="parent">
<VList>
<VListItem
value="download"
prepend-icon="ri-download-line"
>
Download
</VListItem>
<VListItem
value="delete"
prepend-icon="ri-delete-bin-line"
@click="deleteProduct(item.id)"
>
Delete
</VListItem>
<VListItem
value="duplicate"
prepend-icon="ri-stack-line"
>
Duplicate
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<!-- Pagination -->
<template #bottom>
<VDivider />
<div class="d-flex justify-end flex-wrap gap-x-6 px-2 py-1">
<div class="d-flex align-center gap-x-2 text-medium-emphasis text-base">
Rows Per Page:
<VSelect
v-model="itemsPerPage"
class="per-page-select"
variant="plain"
:items="[10, 20, 25, 50, 100]"
/>
</div>
<p class="d-flex align-center text-base text-high-emphasis me-2 mb-0">
{{ paginationMeta({ page, itemsPerPage }, totalProduct) }}
</p>
<div class="d-flex gap-x-2 align-center me-2">
<VBtn
class="flip-in-rtl"
icon="ri-arrow-left-s-line"
variant="text"
density="comfortable"
color="high-emphasis"
:disabled="page <= 1"
@click="page <= 1 ? page = 1 : page--"
/>
<VBtn
class="flip-in-rtl"
icon="ri-arrow-right-s-line"
density="comfortable"
variant="text"
color="high-emphasis"
:disabled="page >= Math.ceil(totalProduct / itemsPerPage)"
@click="page >= Math.ceil(totalProduct / itemsPerPage) ? page = Math.ceil(totalProduct / itemsPerPage) : page++ "
/>
</div>
</div>
</template>
</VDataTableServer>
</VCard>
</div>
</template>
<style lang="scss" scoped>
.product-widget{
border-block-end: 1px solid rgba(var(--v-theme-on-surface), var(--v-border-opacity));
padding-block-end: 1rem;
}
</style>

View File

@@ -0,0 +1,384 @@
<script setup>
import paperImg from '@images/svg/paper.svg?raw'
import rocketImg from '@images/svg/rocket.svg?raw'
import userInfoImg from '@images/svg/user-info.svg?raw'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
const rocketIcon = h('div', {
innerHTML: rocketImg,
style: 'font-size: 2.625rem; color: rgb(var(--v-theme-primary))',
})
const userInfoIcon = h('div', {
innerHTML: paperImg,
style: 'font-size: 2.625rem; color: rgb(var(--v-theme-primary))',
})
const paperIcon = h('div', {
innerHTML: userInfoImg,
style: 'font-size: 2.625rem; color: rgb(var(--v-theme-primary))',
})
const widgetData = [
{
title: 'Total Earning',
value: '$24,983',
icon: 'ri-money-dollar-circle-line',
color: 'primary',
},
{
title: 'Unpaid Earning',
value: '$8,647',
icon: 'ri-gift-line',
color: 'success',
},
{
title: 'Signups',
value: '2,367',
icon: 'ri-group-line',
color: 'error',
},
{
title: 'Conversion Rate',
value: '4.5%',
icon: 'ri-refresh-line',
color: 'info',
},
]
const stepsData = [
{
icon: rocketIcon,
desc: 'Create & validate your referral link and get',
value: '$50',
},
{
icon: paperIcon,
desc: 'For every new signup you get',
value: '10%',
},
{
icon: userInfoIcon,
desc: 'Get other friends to generate link and get',
value: '$100',
},
]
// Data table options
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
// Data Table Headers
const headers = [
{
title: 'Users',
key: 'users',
},
{
title: 'Referred ID',
key: 'referred-id',
},
{
title: 'Status',
key: 'status',
},
{
title: 'Value',
key: 'value',
},
{
title: 'Earnings',
key: 'earning',
},
]
const updateOptions = options => {
page.value = options.page
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const { data: referralData } = await useApi(createUrl('/apps/ecommerce/referrals', {
query: {
page,
itemsPerPage,
sortBy,
orderBy,
},
}))
const referrals = computed(() => referralData.value.referrals)
const totalReferrals = computed(() => referralData.value.total)
const resolveStatus = status => {
if (status === 'Rejected')
return {
text: 'Rejected',
color: 'error',
}
if (status === 'Unpaid')
return {
text: 'Unpaid',
color: 'warning',
}
if (status === 'Paid')
return {
text: 'Paid',
color: 'success',
}
}
</script>
<template>
<div>
<!-- 👉 Header -->
<VRow class="match-height">
<!-- 👉 Widgets -->
<VCol
v-for="(data, index) in widgetData"
:key="index"
cols="12"
md="3"
sm="6"
>
<VCard>
<VCardText>
<div class="d-flex align-center justify-space-between">
<div class="d-flex flex-column">
<span class="text-h5 mb-1">{{ data.value }}</span>
<span class="text-sm">{{ data.title }}</span>
</div>
<VAvatar
:icon="data.icon"
variant="tonal"
:color="data.color"
/>
</div>
</VCardText>
</VCard>
</VCol>
<!-- 👉 Icon Steps -->
<VCol
cols="12"
md="8"
>
<VCard>
<VCardItem>
<VCardTitle> How to use</VCardTitle>
<VCardSubtitle>
Integrate your referral code in 3 easy steps.
</VCardSubtitle>
</VCardItem>
<VCardText>
<div class="d-flex gap-6 justify-space-between align-center flex-sm-row flex-column">
<div
v-for="(step, index) in stepsData"
:key="index"
class="d-flex flex-column align-center gap-y-2"
style="max-inline-size: 185px;"
>
<div class="icon-container">
<VNodeRenderer :nodes="step.icon" />
</div>
<span class="text-body-1 text-wrap text-center">
{{ step.desc }}
</span>
<span class="text-primary text-h6">{{ step.value }}</span>
</div>
</div>
</VCardText>
</VCard>
</VCol>
<!-- 👉 Invite -->
<VCol
cols="12"
md="4"
>
<VCard>
<VCardText>
<div class="mb-11">
<h5 class="text-h5 mb-5">
Invite your friends
</h5>
<div class="d-flex align-center flex-wrap gap-4">
<VTextField
placeholder="Email Addresss"
density="compact"
/>
<VBtn>
<VIcon
start
icon="ri-check-line"
/>
Submit
</VBtn>
</div>
</div>
<div>
<h5 class="text-h5 mb-5">
Share the referral link
</h5>
<div class="d-flex align-center flex-wrap gap-4">
<VTextField
placeholder="themeselection.com/?ref=6478"
density="compact"
/>
<div class="d-flex gap-x-2">
<VBtn
icon
class="rounded"
color="#3B5998"
>
<VIcon
color="white"
icon="ri-facebook-circle-line"
/>
</VBtn>
<VBtn
icon
class="rounded"
color="#55ACEE"
>
<VIcon
color="white"
icon="ri-twitter-line"
/>
</VBtn>
</div>
</div>
</div>
</VCardText>
</VCard>
</VCol>
<!-- 👉 Referral Table -->
<VCol cols="12">
<VCard>
<VCardText>
<div class="d-flex justify-space-between align-center flex-wrap gap-4">
<h5 class="text-h5">
Referred Users
</h5>
<VBtn prepend-icon="ri-upload-2-line">
Export
</VBtn>
</div>
</VCardText>
<VDataTableServer
v-model:items-per-page="itemsPerPage"
v-model:page="page"
:items="referrals"
:headers="headers"
:items-length="totalReferrals"
show-select
class="text-high-emphasis"
@update:options="updateOptions"
>
<template #item.users="{ item }">
<div class="d-flex align-center gap-x-4">
<VAvatar
:image="item.avatar"
:size="34"
/>
<div>
<h6 class="text-h6">
<RouterLink
class="text-high-emphasis"
:to="{ name: 'apps-ecommerce-customer-details-id', params: { id: 478426 } }"
>
{{ item.user }}
</RouterLink>
</h6>
<div class="text-sm text-medium-emphasis">
{{ item.email }}
</div>
</div>
</div>
</template>
<template #item.referred-id="{ item }">
{{ item.referredId }}
</template>
<template #item.status="{ item }">
<VChip
v-bind="resolveStatus(item.status)"
size="small"
/>
</template>
<!-- Pagination -->
<template #bottom>
<VDivider />
<div class="d-flex justify-end flex-wrap gap-x-6 px-2 py-1">
<div class="d-flex align-center gap-x-2 text-medium-emphasis text-base">
Rows Per Page:
<VSelect
v-model="itemsPerPage"
class="per-page-select"
variant="plain"
:items="[10, 20, 25, 50, 100]"
/>
</div>
<p class="d-flex align-center text-base text-high-emphasis me-2 mb-0">
{{ paginationMeta({ page, itemsPerPage }, totalReferrals) }}
</p>
<div class="d-flex gap-x-2 align-center me-2">
<VBtn
class="flip-in-rtl"
icon="ri-arrow-left-s-line"
variant="text"
density="comfortable"
color="high-emphasis"
:disabled="page <= 1"
@click="page <= 1 ? page = 1 : page--"
/>
<VBtn
class="flip-in-rtl"
icon="ri-arrow-right-s-line"
density="comfortable"
variant="text"
color="high-emphasis"
:disabled="page >= Math.ceil(totalReferrals / itemsPerPage)"
@click="page >= Math.ceil(totalReferrals / itemsPerPage) ? page = Math.ceil(totalReferrals / itemsPerPage) : page++ "
/>
</div>
</div>
</template>
</VDataTableServer>
</VCard>
</VCol>
</VRow>
</div>
</template>
<style lang="scss" scoped>
.icon-container {
display: flex;
align-items: center;
justify-content: center;
border: 1px dashed rgb(var(--v-theme-primary));
border-radius: 50%;
block-size: 70px;
inline-size: 70px;
}
.icon {
color: #000;
font-size: 42px;
}
</style>

View File

@@ -0,0 +1,99 @@
<script setup>
import SettingsCheckout from '@/views/apps/ecommerce/settings/SettingsCheckout.vue'
import SettingsLocations from '@/views/apps/ecommerce/settings/SettingsLocations.vue'
import SettingsNotifications from '@/views/apps/ecommerce/settings/SettingsNotifications.vue'
import SettingsPayment from '@/views/apps/ecommerce/settings/SettingsPayment.vue'
import SettingsShippingAndDelivery from '@/views/apps/ecommerce/settings/SettingsShippingAndDelivery.vue'
import SettingsStoreDetails from '@/views/apps/ecommerce/settings/SettingsStoreDetails.vue'
const tabsData = [
{
icon: 'ri-store-line',
title: 'Store Details',
},
{
icon: 'ri-bank-card-line',
title: 'Payments',
},
{
icon: 'ri-shopping-cart-line',
title: 'Checkout',
},
{
icon: 'ri-car-line',
title: 'Shipping & Delivery',
},
{
icon: 'ri-map-pin-line',
title: 'Location',
},
{
icon: 'ri-notification-3-line',
title: 'Notifications',
},
]
const activeTab = ref(null)
</script>
<template>
<VRow>
<VCol
cols="12"
md="4"
>
<h5 class="text-h5 mb-4">
Getting Started
</h5>
<VTabs
v-model="activeTab"
direction="vertical"
class="v-tabs-pill disable-tab-transition"
>
<VTab
v-for="(tabItem, index) in tabsData"
:key="index"
:prepend-icon="tabItem.icon"
>
{{ tabItem.title }}
</VTab>
</VTabs>
</VCol>
<VCol
cols="12"
md="8"
>
<VWindow
v-model="activeTab"
class="disable-tab-transition"
:touch="false"
>
<VWindowItem>
<SettingsStoreDetails />
</VWindowItem>
<VWindowItem>
<SettingsPayment />
</VWindowItem>
<VWindowItem>
<SettingsCheckout />
</VWindowItem>
<VWindowItem>
<SettingsShippingAndDelivery />
</VWindowItem>
<VWindowItem>
<SettingsLocations />
</VWindowItem>
<VWindowItem>
<SettingsNotifications />
</VWindowItem>
</VWindow>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,511 @@
<script setup>
import { PerfectScrollbar } from 'vue3-perfect-scrollbar'
import ComposeDialog from '@/views/apps/email/ComposeDialog.vue'
import EmailLeftSidebarContent from '@/views/apps/email/EmailLeftSidebarContent.vue'
import EmailView from '@/views/apps/email/EmailView.vue'
import { useEmail } from '@/views/apps/email/useEmail'
definePage({ meta: { layoutWrapperClasses: 'layout-content-height-fixed' } })
const { isLeftSidebarOpen } = useResponsiveLeftSidebar()
// Composables
const route = useRoute()
const { labels, resolveLabelColor, emailMoveToFolderActions, shallShowMoveToActionFor, moveSelectedEmailTo, updateEmails, updateEmailLabels } = useEmail()
// Compose dialog
const isComposeDialogVisible = ref(false)
// Ref
const q = ref('')
// Email Selection
// ------------------------------------------------
const selectedEmails = ref([])
const {
data: emailData,
execute: fetchEmails,
} = await useApi(createUrl('/apps/email', {
query: {
q,
filter: () => 'filter' in route.params ? route.params.filter : undefined,
label: () => 'label' in route.params ? route.params.label : undefined,
},
}))
const emails = computed(() => emailData.value.emails)
const toggleSelectedEmail = emailId => {
const emailIndex = selectedEmails.value.indexOf(emailId)
if (emailIndex === -1)
selectedEmails.value.push(emailId)
else
selectedEmails.value.splice(emailIndex, 1)
}
const selectAllEmailCheckbox = computed(() => emails.value.length && emails.value.length === selectedEmails.value.length)
const isSelectAllEmailCheckboxIndeterminate = computed(() => Boolean(selectedEmails.value.length) && emails.value.length !== selectedEmails.value.length)
const isAllMarkRead = computed(() => {
return selectedEmails.value.every(emailId => emails.value.find(email => email.id === emailId)?.isRead)
})
const selectAllCheckboxUpdate = () => {
selectedEmails.value = !selectAllEmailCheckbox.value ? emails.value.map(email => email.id) : []
}
// Email View
const openedEmail = ref(null)
const emailViewMeta = computed(() => {
const returnValue = {
hasNextEmail: false,
hasPreviousEmail: false,
}
if (openedEmail.value) {
const openedEmailIndex = emails.value.findIndex(e => e.id === openedEmail.value?.id)
returnValue.hasNextEmail = !!emails.value[openedEmailIndex + 1]
returnValue.hasPreviousEmail = !!emails.value[openedEmailIndex - 1]
}
return returnValue
})
const refreshOpenedEmail = async () => {
await fetchEmails()
if (openedEmail.value)
openedEmail.value = emails.value.find(e => e.id === openedEmail.value?.id)
}
/* You can optimize it so it doesn't fetch emails on each action.
Currently, if you just star the email, two API calls will get fired.
1. star the email
2. Fetch all latest emails
You can limit this to single API call by:
- making API to star the email
- modify the state (set that email's isStarred property to true/false) in the store instead of making API for fetching emails
😊 For simplicity of the code and possible of modification, we kept it simple.
*/
const handleActionClick = async (action, emailIds = selectedEmails.value) => {
selectedEmails.value = []
if (!emailIds.length)
return
if (action === 'trash')
await updateEmails(emailIds, { isDeleted: true })
else if (action === 'spam')
await updateEmails(emailIds, { folder: 'spam' })
else if (action === 'unread')
await updateEmails(emailIds, { isRead: false })
else if (action === 'read')
await updateEmails(emailIds, { isRead: true })
else if (action === 'star')
await updateEmails(emailIds, { isStarred: true })
else if (action === 'unstar')
await updateEmails(emailIds, { isStarred: false })
await fetchEmails()
if (openedEmail.value)
refreshOpenedEmail()
}
const handleMoveMailsTo = async action => {
moveSelectedEmailTo(action, selectedEmails.value)
await fetchEmails()
}
const changeOpenedEmail = dir => {
if (!openedEmail.value)
return
const openedEmailIndex = emails.value.findIndex(e => e.id === openedEmail.value?.id)
const newEmailIndex = dir === 'previous' ? openedEmailIndex - 1 : openedEmailIndex + 1
openedEmail.value = emails.value[newEmailIndex]
}
const openEmail = async email => {
openedEmail.value = email
await handleActionClick('read', [email.id])
}
watch(() => route.params, () => {
selectedEmails.value = []
}, { deep: true })
</script>
<template>
<VLayout
style="min-block-size: 100%;"
class="email-app-layout"
>
<VNavigationDrawer
v-model="isLeftSidebarOpen"
absolute
touchless
location="start"
:temporary="$vuetify.display.mdAndDown"
>
<EmailLeftSidebarContent @toggle-compose-dialog-visibility="isComposeDialogVisible = !isComposeDialogVisible" />
</VNavigationDrawer>
<EmailView
:email="openedEmail"
:email-meta="emailViewMeta"
@refresh="refreshOpenedEmail"
@navigated="changeOpenedEmail"
@close="openedEmail = null"
@remove="handleActionClick('trash', openedEmail ? [openedEmail.id] : [])"
@unread="handleActionClick('unread', openedEmail ? [openedEmail.id] : [])"
@star="handleActionClick('star', openedEmail ? [openedEmail.id] : [])"
@unstar="handleActionClick('unstar', openedEmail ? [openedEmail.id] : [])"
/>
<VMain>
<VCard
flat
class="email-content-list h-100 d-flex flex-column"
>
<div class="d-flex align-center">
<IconBtn
class="d-lg-none ms-3"
@click="isLeftSidebarOpen = true"
>
<VIcon icon="ri-menu-line" />
</IconBtn>
<!-- 👉 Search -->
<VTextField
v-model="q"
density="default"
class="email-search px-1 flex-grow-1"
placeholder="Search mail"
>
<template #prepend-inner>
<VIcon
color="disabled"
size="22"
icon="ri-search-line"
/>
</template>
</VTextField>
</div>
<VDivider />
<!-- 👉 Action bar -->
<div class="py-2 px-4 d-flex align-center">
<!-- TODO: Make checkbox primary on indeterminate state -->
<VCheckbox
:model-value="selectAllEmailCheckbox"
:indeterminate="isSelectAllEmailCheckboxIndeterminate"
class="me-1"
@update:model-value="selectAllCheckboxUpdate"
/>
<div
class="w-100 d-flex align-center action-bar-actions gap-1"
:style="{
visibility:
isSelectAllEmailCheckboxIndeterminate || selectAllEmailCheckbox
? undefined
: 'hidden',
}"
>
<!-- Trash -->
<IconBtn
v-show="('filter' in route.params ? route.params.filter !== 'trashed' : true)"
@click="handleActionClick('trash')"
>
<VIcon icon="ri-delete-bin-7-line" />
<VTooltip
activator="parent"
location="top"
>
Delete Mail
</VTooltip>
</IconBtn>
<!-- Mark unread/read -->
<IconBtn @click="isAllMarkRead ? handleActionClick('unread') : handleActionClick('read') ">
<VIcon :icon="isAllMarkRead ? 'ri-mail-line' : 'ri-mail-open-line'" />
<VTooltip
activator="parent"
location="top"
>
{{ isAllMarkRead ? 'Mark as Unread' : 'Mark as Read' }}
</VTooltip>
</IconBtn>
<!-- Move to folder -->
<IconBtn>
<VIcon icon="ri-folder-line" />
<VTooltip
activator="parent"
location="top"
>
Folder
</VTooltip>
<VMenu activator="parent">
<VList density="compact">
<template
v-for="moveTo in emailMoveToFolderActions"
:key="moveTo.title"
>
<VListItem
:class="
shallShowMoveToActionFor(moveTo.action) ? 'd-flex' : 'd-none'
"
class="align-center"
href="#"
@click="handleMoveMailsTo(moveTo.action)"
>
<template #prepend>
<VIcon
:icon="moveTo.icon"
class="me-2"
size="20"
/>
</template>
<VListItemTitle class="text-capitalize">
{{ moveTo.action }}
</VListItemTitle>
</VListItem>
</template>
</VList>
</VMenu>
</IconBtn>
<!-- Update labels -->
<IconBtn>
<VIcon icon="ri-price-tag-3-line" />
<VTooltip
activator="parent"
location="top"
>
Label
</VTooltip>
<VMenu activator="parent">
<VList density="compact">
<VListItem
v-for="label in labels"
:key="label.title"
href="#"
@click="updateEmailLabels(selectedEmails, label.title)"
>
<template #prepend>
<VBadge
inline
:color="resolveLabelColor(label.title)"
dot
/>
</template>
<VListItemTitle class="ms-2 text-capitalize">
{{ label.title }}
</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</IconBtn>
</div>
<VSpacer />
<IconBtn
class="me-1"
@click="fetchEmails"
>
<VIcon icon="ri-refresh-line" />
</IconBtn>
<MoreBtn />
</div>
<VDivider />
<!-- 👉 Emails list -->
<PerfectScrollbar
tag="ul"
:options="{ wheelPropagation: false }"
class="email-list"
>
<li
v-for="email in emails"
v-show="emails?.length"
:key="email.id"
class="email-item d-flex align-center pa-4 gap-2 cursor-pointer"
:class="[{ 'email-read': email.isRead }]"
@click="openEmail(email)"
>
<VCheckbox
:model-value="selectedEmails.includes(email.id)"
class="flex-shrink-0"
@update:model-value="toggleSelectedEmail(email.id)"
@click.stop
/>
<IconBtn
:color="email.isStarred ? 'warning' : 'default'"
@click.stop=" handleActionClick(email.isStarred ? 'unstar' : 'star', [email.id])"
>
<VIcon icon="ri-star-line" />
</IconBtn>
<VAvatar size="32">
<VImg
:src="email.from.avatar"
:alt="email.from.name"
/>
</VAvatar>
<h6 class="text-h6">
{{ email.from.name }}
</h6>
<span class="text-body-2 truncate">{{ email.subject }}</span>
<VSpacer />
<!-- 👉 Email meta -->
<div
class="email-meta align-center gap-2"
:class="$vuetify.display.xs ? 'd-none' : ''"
>
<VIcon
v-for="label in email.labels"
:key="label"
icon="ri-circle-fill"
size="10"
:color="resolveLabelColor(label)"
/>
<span class="text-sm text-disabled">
{{ formatDateToMonthShort(email.time) }}
</span>
</div>
<!-- 👉 Email actions -->
<div class="email-actions d-none">
<IconBtn @click.stop="handleActionClick('trash', [email.id])">
<VIcon icon="ri-delete-bin-7-line" />
<VTooltip
activator="parent"
location="top"
>
Delete Mail
</VTooltip>
</IconBtn>
<IconBtn
class="mx-2"
@click.stop=" handleActionClick(email.isRead ? 'unread' : 'read', [email.id])"
>
<VIcon :icon="email.isRead ? 'ri-mail-line' : 'ri-mail-open-line'" />
<VTooltip
activator="parent"
location="top"
>
{{ email.isRead ? 'Mark as Unread' : 'Mark as Read' }}
</VTooltip>
</IconBtn>
<IconBtn @click.stop="handleActionClick('spam', [email.id])">
<VIcon icon="ri-error-warning-line" />
<VTooltip
activator="parent"
location="top"
>
Move to Spam
</VTooltip>
</IconBtn>
</div>
</li>
<li
v-show="!emails.length"
class="py-4 px-5 text-center"
>
<span class="text-high-emphasis">No items found.</span>
</li>
</PerfectScrollbar>
</VCard>
<ComposeDialog
v-show="isComposeDialogVisible"
@close="isComposeDialogVisible = false"
/>
</VMain>
</VLayout>
</template>
<style lang="scss">
@use "@styles/variables/vuetify.scss";
@use "@core-scss/base/mixins.scss";
// Remove border. Using variant plain cause UI issue, caret isn't align in center
.email-search {
.v-field__outline {
display: none;
}
}
.email-app-layout {
border-radius: vuetify.$card-border-radius;
@include mixins.elevation(vuetify.$card-elevation);
$sel-email-app-layout: &;
@at-root {
.skin--bordered {
@include mixins.bordered-skin($sel-email-app-layout);
}
}
}
.email-content-list {
border-end-start-radius: 0;
border-start-start-radius: 0;
}
.email-list {
white-space: nowrap;
.email-item {
transition: all 0.2s ease-in-out;
will-change: transform, box-shadow;
&.email-read {
background-color: rgba(var(--v-theme-on-surface), var(--v-hover-opacity));
}
& + .email-item {
border-block-start: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
}
}
.email-item .email-meta {
display: flex;
}
.email-item:hover {
transform: translateY(-2px);
@include mixins.elevation(4);
// Don't show actions on hover on mobile & tablet devices
@media screen and (min-width: 1280px) {
.email-actions {
display: block !important;
}
.email-meta {
display: none;
}
}
+ .email-item {
border-color: transparent;
}
}
}
.email-compose-dialog {
position: absolute;
inset-block-end: 0;
inset-inline-end: 0;
min-inline-size: 100%;
@media only screen and (min-width: 800px) {
min-inline-size: 712px;
}
}
</style>

View File

@@ -0,0 +1,170 @@
<script setup>
import InvoiceEditable from '@/views/apps/invoice/InvoiceEditable.vue'
import InvoiceSendInvoiceDrawer from '@/views/apps/invoice/InvoiceSendInvoiceDrawer.vue'
// 👉 Default Blank Data
const invoiceData = ref({
invoice: {
id: 5037,
issuedDate: '',
service: '',
total: 0,
avatar: '',
invoiceStatus: '',
dueDate: '',
balance: 0,
client: {
address: '112, Lorem Ipsum, Florida',
company: 'Greeva Inc',
companyEmail: 'johndoe@greeva.com',
contact: '+1 123 3452 12',
country: 'USA',
name: 'John Doe',
},
},
paymentDetails: {
totalDue: '$12,110.55',
bankName: 'American Bank',
country: 'United States',
iban: 'ETD95476213',
swiftCode: 'BR91905',
},
purchasedProducts: [{
title: '',
cost: 0,
hours: 0,
description: '',
}],
note: '',
paymentMethod: '',
salesperson: '',
thanksNote: '',
})
const paymentTerms = ref(true)
const clientNotes = ref(false)
const paymentStub = ref(false)
const selectedPaymentMethod = ref('Bank Account')
const paymentMethods = [
'Bank Account',
'PayPal',
'UPI Transfer',
]
const isSendPaymentSidebarVisible = ref(false)
const addProduct = value => {
invoiceData.value?.purchasedProducts.push(value)
}
const removeProduct = id => {
invoiceData.value?.purchasedProducts.splice(id, 1)
}
</script>
<template>
<VRow>
<!-- 👉 InvoiceEditable -->
<VCol
cols="12"
md="9"
>
<InvoiceEditable
:data="invoiceData"
@push="addProduct"
@remove="removeProduct"
/>
</VCol>
<!-- 👉 Right Column: Invoice Action -->
<VCol
cols="12"
md="3"
>
<VCard class="mb-8">
<VCardText>
<!-- 👉 Send Invoice -->
<VBtn
block
prepend-icon="ri-send-plane-line"
class="mb-3"
@click="isSendPaymentSidebarVisible = true"
>
Send Invoice
</VBtn>
<!-- 👉 Preview -->
<VBtn
block
color="secondary"
variant="outlined"
class="mb-3"
:to="{ name: 'apps-invoice-preview-id', params: { id: '5036' } }"
>
Preview
</VBtn>
<!-- 👉 Save -->
<VBtn
block
color="secondary"
variant="outlined"
>
Save
</VBtn>
</VCardText>
</VCard>
<!-- 👉 Select payment method -->
<VSelect
v-model="selectedPaymentMethod"
:items="paymentMethods"
label="Accept Payment Via"
class="mb-6"
/>
<!-- 👉 Payment Terms -->
<div class="d-flex align-center justify-space-between mb-2">
<VLabel for="payment-terms">
Payment Terms
</VLabel>
<div>
<VSwitch
id="payment-terms"
v-model="paymentTerms"
/>
</div>
</div>
<!-- 👉 Client Notes -->
<div class="d-flex align-center justify-space-between mb-2">
<VLabel for="client-notes">
Client Notes
</VLabel>
<div>
<VSwitch
id="client-notes"
v-model="clientNotes"
/>
</div>
</div>
<!-- 👉 Payment Stub -->
<div class="d-flex align-center justify-space-between">
<VLabel for="payment-stub">
Payment Stub
</VLabel>
<div>
<VSwitch
id="payment-stub"
v-model="paymentStub"
/>
</div>
</div>
</VCol>
</VRow>
<!-- 👉 Send Invoice Sidebar -->
<InvoiceSendInvoiceDrawer v-model:isDrawerOpen="isSendPaymentSidebarVisible" />
</template>

View File

@@ -0,0 +1,166 @@
<script setup>
import InvoiceAddPaymentDrawer from '@/views/apps/invoice/InvoiceAddPaymentDrawer.vue'
import InvoiceEditable from '@/views/apps/invoice/InvoiceEditable.vue'
import InvoiceSendInvoiceDrawer from '@/views/apps/invoice/InvoiceSendInvoiceDrawer.vue'
const invoiceData = ref()
const route = useRoute('apps-invoice-edit-id')
const { data: invoiceDetails } = await useApi(`/apps/invoice/${ route.params.id }`)
invoiceData.value = {
invoice: invoiceDetails.value.invoice,
paymentDetails: invoiceDetails.value.paymentDetails,
purchasedProducts: [{
title: 'App Design',
cost: 24,
hours: 2,
description: 'Designed UI kit & app pages.',
}],
note: 'It was a pleasure working with you and your team. We hope you will keep us in mind for future freelance projects. Thank You!',
paymentMethod: 'Bank Account',
salesperson: 'Tom Cook',
thanksNote: 'Thanks for your business',
}
const addProduct = value => {
invoiceData.value?.purchasedProducts.push(value)
}
const removeProduct = id => {
invoiceData.value?.purchasedProducts.splice(id, 1)
}
const isSendSidebarActive = ref(false)
const isAddPaymentSidebarActive = ref(false)
const paymentTerms = ref(true)
const clientNotes = ref(false)
const paymentStub = ref(false)
const selectedPaymentMethod = ref('Bank Account')
const paymentMethods = [
'Bank Account',
'PayPal',
'UPI Transfer',
]
</script>
<template>
<VRow>
<!-- 👉 InvoiceEditable -->
<VCol
cols="12"
md="9"
>
<InvoiceEditable
v-if="invoiceData?.invoice"
:data="invoiceData"
@push="addProduct"
@remove="removeProduct"
/>
</VCol>
<!-- 👉 Right Column: Invoice Action -->
<VCol
cols="12"
md="3"
>
<VCard class="mb-8">
<VCardText>
<!-- 👉 Send Invoice Trigger button -->
<VBtn
block
prepend-icon="ri-send-plane-line"
class="mb-4"
@click="isSendSidebarActive = true"
>
Send Invoice
</VBtn>
<div class="d-flex flex-wrap gap-4">
<!-- 👉 Preview button -->
<VBtn
color="secondary"
variant="outlined"
class="flex-grow-1"
:to="{ name: 'apps-invoice-preview-id', params: { id: route.params.id } }"
>
Preview
</VBtn>
<!-- 👉 Save button -->
<VBtn
color="secondary"
variant="outlined"
class="mb-4 flex-grow-1"
>
Save
</VBtn>
</div>
<!-- 👉 Add Payment trigger button -->
<VBtn
block
color="success"
prepend-icon="ri-money-dollar-circle-line"
@click="isAddPaymentSidebarActive = true"
>
Add Payment
</VBtn>
</VCardText>
</VCard>
<!-- 👉 Accept payment via -->
<VSelect
v-model="selectedPaymentMethod"
:items="paymentMethods"
label="Accept Payment Via"
class="mb-6"
/>
<!-- 👉 Payment Terms -->
<div class="d-flex align-center justify-space-between mb-2">
<VLabel for="payment-terms">
Payment Terms
</VLabel>
<div>
<VSwitch
id="payment-terms"
v-model="paymentTerms"
/>
</div>
</div>
<!-- 👉 Client Notes -->
<div class="d-flex align-center justify-space-between mb-2">
<VLabel for="client-notes">
Client Notes
</VLabel>
<div>
<VSwitch
id="client-notes"
v-model="clientNotes"
/>
</div>
</div>
<!-- 👉 Payment Stub -->
<div class="d-flex align-center justify-space-between">
<VLabel for="payment-stub">
Payment Stub
</VLabel>
<div>
<VSwitch
id="payment-stub"
v-model="paymentStub"
/>
</div>
</div>
</VCol>
<!-- 👉 Invoice send drawer -->
<InvoiceSendInvoiceDrawer v-model:isDrawerOpen="isSendSidebarActive" />
<!-- 👉 Invoice add payment drawer -->
<InvoiceAddPaymentDrawer v-model:isDrawerOpen="isAddPaymentSidebarActive" />
</VRow>
</template>

View File

@@ -0,0 +1,454 @@
<script setup>
const searchQuery = ref('')
const selectedStatus = ref(null)
const selectedRows = ref([])
// Data table options
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
const updateOptions = options => {
page.value = options.page
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const widgetData = ref([
{
title: 'Clients',
value: 24,
icon: 'ri-user-line',
},
{
title: 'Invoices',
value: 165,
icon: 'ri-pages-line',
},
{
title: 'Paid',
value: '$2.46k',
icon: 'ri-wallet-line',
},
{
title: 'Unpaid',
value: '$876',
icon: 'ri-money-dollar-circle-line',
},
])
// 👉 headers
const headers = [
{
title: '#',
key: 'id',
},
{
title: 'Status',
key: 'trending',
sortable: false,
},
{
title: 'Client',
key: 'client',
},
{
title: 'Total',
key: 'total',
},
{
title: 'Issued Date',
key: 'date',
},
{
title: 'Balance',
key: 'balance',
},
{
title: 'Actions',
key: 'actions',
sortable: false,
},
]
const {
data: invoiceData,
execute: fetchInvoices,
} = await useApi(createUrl('/apps/invoice', {
query: {
q: searchQuery,
status: selectedStatus,
itemsPerPage,
page,
sortBy,
orderBy,
},
}))
const invoices = computed(() => invoiceData.value.invoices)
const totalInvoices = computed(() => invoiceData.value.totalInvoices)
// 👉 Invoice balance variant resolver
const resolveInvoiceBalanceVariant = (balance, total) => {
if (balance === total)
return {
status: 'Unpaid',
chip: { color: 'error' },
}
if (balance === 0)
return {
status: 'Paid',
chip: { color: 'success' },
}
return {
status: balance,
chip: { variant: 'text' },
}
}
const resolveInvoiceStatusVariantAndIcon = status => {
if (status === 'Partial Payment')
return {
variant: 'warning',
icon: 'ri-line-chart-line',
}
if (status === 'Paid')
return {
variant: 'success',
icon: 'ri-check-line',
}
if (status === 'Downloaded')
return {
variant: 'info',
icon: 'ri-arrow-down-line',
}
if (status === 'Draft')
return {
variant: 'secondary',
icon: 'ri-save-line',
}
if (status === 'Sent')
return {
variant: 'primary',
icon: 'ri-mail-line',
}
if (status === 'Past Due')
return {
variant: 'error',
icon: 'ri-error-warning-line',
}
return {
variant: 'secondary',
icon: 'ri-close-line',
}
}
const computedMoreList = computed(() => {
return paramId => [
{
title: 'Download',
value: 'download',
prependIcon: 'ri-download-line',
},
{
title: 'Edit',
value: 'edit',
prependIcon: 'ri-pencil-line',
to: {
name: 'apps-invoice-edit-id',
params: { id: paramId },
},
},
{
title: 'Duplicate',
value: 'duplicate',
prependIcon: 'ri-stack-line',
},
]
})
const deleteInvoice = async id => {
await $api(`/apps/invoice/${ id }`, { method: 'DELETE' })
fetchInvoices()
}
</script>
<template>
<section v-if="invoices">
<!-- 👉 Invoice Widgets -->
<VCard class="mb-6">
<VCardText class="px-2">
<VRow>
<template
v-for="(data, id) in widgetData"
:key="id"
>
<VCol
cols="12"
sm="6"
md="3"
class="px-6"
:class="id !== widgetData.length - 1 && $vuetify.display.width <= 600 ? 'border-b' : ''"
>
<div class="d-flex justify-space-between">
<div class="d-flex flex-column">
<h4 class="text-h4">
{{ data.value }}
</h4>
<span class="text-body-1 text-capitalize">{{ data.title }}</span>
</div>
<VAvatar
variant="tonal"
rounded
size="42"
>
<VIcon
:icon="data.icon"
size="26"
color="high-emphasis"
/>
</VAvatar>
</div>
</VCol>
<VDivider
v-if="$vuetify.display.mdAndUp ? id !== widgetData.length - 1
: $vuetify.display.smAndUp ? id % 2 === 0
: false"
vertical
inset
/>
</template>
</VRow>
</VCardText>
</VCard>
<VCard id="invoice-list">
<VCardText class="d-flex align-center flex-wrap gap-4">
<!-- 👉 Create invoice -->
<VBtn
prepend-icon="ri-add-line"
:to="{ name: 'apps-invoice-add' }"
>
Create invoice
</VBtn>
<VSpacer />
<div class="d-flex align-center flex-wrap gap-4">
<!-- 👉 Search -->
<div class="invoice-list-search">
<VTextField
v-model="searchQuery"
density="compact"
placeholder="Search Invoice"
/>
</div>
<div class="invoice-list-search">
<VSelect
v-model="selectedStatus"
placeholder="Invoice Status"
clearable
density="compact"
clear-icon="ri-close-line"
:items="['Downloaded', 'Draft', 'Sent', 'Paid', 'Partial Payment', 'Past Due']"
/>
</div>
</div>
</VCardText>
<!-- SECTION Datatable -->
<VDataTableServer
v-model="selectedRows"
v-model:items-per-page="itemsPerPage"
v-model:page="page"
show-select
:items-length="totalInvoices"
:headers="headers"
:items="invoices"
item-value="id"
class="text-no-wrap rounded-0"
@update:options="updateOptions"
>
<!-- id -->
<template #item.id="{ item }">
<RouterLink :to="{ name: 'apps-invoice-preview-id', params: { id: item.id } }">
#{{ item.id }}
</RouterLink>
</template>
<!-- trending -->
<template #item.trending="{ item }">
<VTooltip>
<template #activator="{ props }">
<VAvatar
:size="28"
v-bind="props"
:color="resolveInvoiceStatusVariantAndIcon(item.invoiceStatus).variant"
variant="tonal"
>
<VIcon
:size="16"
:icon="resolveInvoiceStatusVariantAndIcon(item.invoiceStatus).icon"
/>
</VAvatar>
</template>
<p class="mb-0">
{{ item.invoiceStatus }}
</p>
<p class="mb-0">
Balance: {{ item.balance }}
</p>
<p class="mb-0">
Due date: {{ item.dueDate }}
</p>
</VTooltip>
</template>
<!-- client -->
<template #item.client="{ item }">
<div class="d-flex align-center">
<VAvatar
size="34"
:color="!item.avatar.length ? resolveInvoiceStatusVariantAndIcon(item.invoiceStatus).variant : undefined"
:variant="!item.avatar.length ? 'tonal' : undefined"
class="me-3"
>
<VImg
v-if="item.avatar.length"
:src="item.avatar"
/>
<span v-else>{{ avatarText(item.client.name) }}</span>
</VAvatar>
<div class="d-flex flex-column">
<RouterLink
:to="{ name: 'pages-user-profile-tab', params: { tab: 'profile' } }"
class="text-h6 font-weight-medium mb-0"
>
{{ item.client.name }}
</RouterLink>
<span class="text-body-2">{{ item.client.companyEmail }}</span>
</div>
</div>
</template>
<!-- Total -->
<template #item.total="{ item }">
${{ item.total }}
</template>
<!-- Date -->
<template #item.date="{ item }">
{{ item.issuedDate }}
</template>
<!-- Balance -->
<template #item.balance="{ item }">
<VChip
v-if="typeof ((resolveInvoiceBalanceVariant(item.balance, item.total)).status) === 'string'"
:color="resolveInvoiceBalanceVariant(item.balance, item.total).chip.color"
size="small"
>
{{ (resolveInvoiceBalanceVariant(item.balance, item.total)).status }}
</VChip>
<h6
v-else
class="text-h6 font-weight-regular"
>
{{ Number((resolveInvoiceBalanceVariant(item.balance, item.total)).status) > 0 ? `$${(resolveInvoiceBalanceVariant(item.balance, item.total)).status}` : `-$${Math.abs(Number((resolveInvoiceBalanceVariant(item.balance, item.total)).status))}` }}
</h6>
</template>
<!-- Actions -->
<template #item.actions="{ item }">
<div class="text-no-wrap">
<IconBtn
size="small"
@click="deleteInvoice(item.id)"
>
<VIcon icon="ri-delete-bin-7-line" />
</IconBtn>
<IconBtn
size="small"
:to="{ name: 'apps-invoice-preview-id', params: { id: item.id } }"
>
<VIcon icon="ri-eye-line" />
</IconBtn>
<MoreBtn
size="small"
:menu-list="computedMoreList(item.id)"
item-props
/>
</div>
</template>
<!-- Pagination -->
<template #bottom>
<VDivider />
<div class="d-flex justify-end flex-wrap gap-x-6 px-2 py-1">
<div class="d-flex align-center gap-x-2 text-medium-emphasis text-base">
Rows Per Page:
<VSelect
v-model="itemsPerPage"
class="per-page-select"
variant="plain"
:items="[10, 20, 25, 50, 100]"
/>
</div>
<p class="d-flex align-center text-base text-high-emphasis me-2 mb-0">
{{ paginationMeta({ page, itemsPerPage }, totalInvoices) }}
</p>
<div class="d-flex gap-x-2 align-center me-2">
<VBtn
class="flip-in-rtl"
icon="ri-arrow-left-s-line"
variant="text"
density="comfortable"
color="high-emphasis"
:disabled="page <= 1"
@click="page <= 1 ? page = 1 : page--"
/>
<VBtn
class="flip-in-rtl"
icon="ri-arrow-right-s-line"
density="comfortable"
variant="text"
color="high-emphasis"
:disabled="page >= Math.ceil(totalInvoices / itemsPerPage)"
@click="page >= Math.ceil(totalInvoices / itemsPerPage) ? page = Math.ceil(totalInvoices / itemsPerPage) : page++ "
/>
</div>
</div>
</template>
</VDataTableServer>
<!-- !SECTION -->
</VCard>
</section>
<section v-else>
<VCard>
<VCardTitle>No Invoice Found</VCardTitle>
</VCard>
</section>
</template>
<style lang="scss">
#invoice-list {
.invoice-list-actions {
inline-size: 8rem;
}
.invoice-list-search {
inline-size: 12rem;
}
}
</style>

View File

@@ -0,0 +1,438 @@
<script setup>
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
import InvoiceAddPaymentDrawer from '@/views/apps/invoice/InvoiceAddPaymentDrawer.vue'
import InvoiceSendInvoiceDrawer from '@/views/apps/invoice/InvoiceSendInvoiceDrawer.vue'
const route = useRoute('apps-invoice-preview-id')
const isAddPaymentSidebarVisible = ref(false)
const isSendPaymentSidebarVisible = ref(false)
const { data: invoiceData } = await useApi(`/apps/invoice/${ Number(route.params.id) }`)
const invoice = invoiceData.value.invoice
const paymentDetails = invoiceData.value.paymentDetails
const purchasedProducts = [
{
name: 'Premium Branding Package',
description: 'Branding & Promotion',
qty: 1,
hours: 15,
price: 32,
},
{
name: 'SMM',
description: 'Social media templates',
qty: 1,
hours: 14,
price: 28,
},
{
name: 'Web Design',
description: 'Web designing package',
qty: 1,
hours: 12,
price: 24,
},
{
name: 'SEO',
description: 'Search engine optimization',
qty: 1,
hours: 5,
price: 22,
},
]
const printInvoice = () => {
window.print()
}
</script>
<template>
<section v-if="invoiceData">
<VRow>
<VCol
cols="12"
md="9"
>
<VCard class="invoice-preview-wrapper pa-12">
<!-- SECTION Header -->
<div class="invoice-header-preview d-flex flex-wrap justify-space-between flex-column flex-sm-row print-row bg-var-theme-background gap-6 rounded pa-6 mb-6">
<!-- 👉 Left Content -->
<div>
<div class="d-flex align-center mb-6">
<!-- 👉 Logo -->
<VNodeRenderer
:nodes="themeConfig.app.logo"
class="me-3"
/>
<!-- 👉 Title -->
<h6 class="text-xl leading-normal text-uppercase">
{{ themeConfig.app.title }}
</h6>
</div>
<!-- 👉 Address -->
<h6 class="text-h6 font-weight-regular">
Office 149, 450 South Brand Brooklyn
</h6>
<h6 class="text-h6 font-weight-regular">
San Diego County, CA 91905, USA
</h6>
<h6 class="text-h6 font-weight-regular">
+1 (123) 456 7891, +44 (876) 543 2198
</h6>
</div>
<!-- 👉 Right Content -->
<div>
<!-- 👉 Invoice ID -->
<h6 class="font-weight-medium text-lg mb-6">
Invoice #{{ invoice.id }}
</h6>
<!-- 👉 Issue Date -->
<h6 class="text-h6 font-weight-regular">
<span>Date Issued: </span>
<span>{{ new Date(invoice.issuedDate).toLocaleDateString('en-GB') }}</span>
</h6>
<!-- 👉 Due Date -->
<h6 class="text-h6 font-weight-regular">
<span>Due Date: </span>
<span>{{ new Date(invoice.dueDate).toLocaleDateString('en-GB') }}</span>
</h6>
</div>
</div>
<!-- !SECTION -->
<!-- 👉 Payment Details -->
<VRow class="print-row mb-6">
<VCol class="text-no-wrap">
<h6 class="text-h6 mb-4">
Invoice To:
</h6>
<p class="mb-0">
{{ invoice.client.name }}
</p>
<p class="mb-0">
{{ invoice.client.company }}
</p>
<p class="mb-0">
{{ invoice.client.address }}, {{ invoice.client.country }}
</p>
<p class="mb-0">
{{ invoice.client.contact }}
</p>
<p class="mb-0">
{{ invoice.client.companyEmail }}
</p>
</VCol>
<VCol class="text-no-wrap">
<h6 class="text-h6 mb-4">
Bill To:
</h6>
<table>
<tbody>
<tr>
<td class="pe-6">
Total Due:
</td>
<td>
{{ paymentDetails.totalDue }}
</td>
</tr>
<tr>
<td class="pe-6">
Bank Name:
</td>
<td>
{{ paymentDetails.bankName }}
</td>
</tr>
<tr>
<td class="pe-6">
Country:
</td>
<td>
{{ paymentDetails.country }}
</td>
</tr>
<tr>
<td class="pe-6">
IBAN:
</td>
<td>
{{ paymentDetails.iban }}
</td>
</tr>
<tr>
<td class="pe-6">
SWIFT Code:
</td>
<td>
{{ paymentDetails.swiftCode }}
</td>
</tr>
</tbody>
</table>
</VCol>
</VRow>
<!-- 👉 invoice Table -->
<VTable class="invoice-preview-table border text-high-emphasis overflow-hidden mb-6">
<thead>
<tr>
<th scope="col">
ITEM
</th>
<th scope="col">
DESCRIPTION
</th>
<th
scope="col"
class="text-center"
>
HOURS
</th>
<th
scope="col"
class="text-center"
>
QTY
</th>
<th
scope="col"
class="text-center"
>
TOTAL
</th>
</tr>
</thead>
<tbody>
<tr
v-for="item in purchasedProducts"
:key="item.name"
>
<td class="text-no-wrap">
{{ item.name }}
</td>
<td class="text-no-wrap">
{{ item.description }}
</td>
<td class="text-center">
{{ item.hours }}
</td>
<td class="text-center">
{{ item.qty }}
</td>
<td class="text-center">
${{ item.price }}
</td>
</tr>
</tbody>
</VTable>
<!-- 👉 Total -->
<div class="d-flex justify-space-between flex-column flex-sm-row print-row">
<div class="mb-2">
<div class="d-flex align-center mb-1">
<h6 class="text-h6 me-2">
Salesperson:
</h6>
<span>Jenny Parker</span>
</div>
<p>Thanks for your business</p>
</div>
<div>
<table class="w-100">
<tbody>
<tr>
<td class="pe-16">
Subtotal:
</td>
<td :class="$vuetify.locale.isRtl ? 'text-start' : 'text-end'">
<h6 class="text-sm">
$1800
</h6>
</td>
</tr>
<tr>
<td class="pe-16">
Discount:
</td>
<td :class="$vuetify.locale.isRtl ? 'text-start' : 'text-end'">
<h6 class="text-sm">
$28
</h6>
</td>
</tr>
<tr>
<td class="pe-16">
Tax:
</td>
<td :class="$vuetify.locale.isRtl ? 'text-start' : 'text-end'">
<h6 class="text-sm">
21%
</h6>
</td>
</tr>
</tbody>
</table>
<VDivider class="mt-4 mb-3" />
<table class="w-100">
<tbody>
<tr>
<td class="pe-16">
Total:
</td>
<td :class="$vuetify.locale.isRtl ? 'text-start' : 'text-end'">
<h6 class="text-sm">
$1690
</h6>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<VDivider class="my-6 border-dashed" />
<p class="mb-0">
<span class="text-high-emphasis font-weight-medium me-1">
Note:
</span>
<span>It was a pleasure working with you and your team. We hope you will keep us in mind for future freelance projects. Thank You!</span>
</p>
</VCard>
</VCol>
<VCol
cols="12"
md="3"
class="d-print-none"
>
<VCard>
<VCardText>
<!-- 👉 Send Invoice Trigger button -->
<VBtn
block
prepend-icon="ri-send-plane-line"
class="mb-4"
@click="isSendPaymentSidebarVisible = true"
>
Send Invoice
</VBtn>
<VBtn
block
color="secondary"
variant="outlined"
class="mb-4"
>
Download
</VBtn>
<div class="d-flex flex-wrap gap-4">
<VBtn
variant="outlined"
color="secondary"
class="flex-grow-1"
@click="printInvoice"
>
Print
</VBtn>
<VBtn
color="secondary"
variant="outlined"
class="mb-4 flex-grow-1"
:to="{ name: 'apps-invoice-edit-id', params: { id: route.params.id } }"
>
Edit
</VBtn>
</div>
<!-- 👉 Add Payment trigger button -->
<VBtn
block
prepend-icon="ri-money-dollar-circle-line"
color="success"
@click="isAddPaymentSidebarVisible = true"
>
Add Payment
</VBtn>
</VCardText>
</VCard>
</VCol>
</VRow>
<!-- 👉 Add Payment Sidebar -->
<InvoiceAddPaymentDrawer v-model:isDrawerOpen="isAddPaymentSidebarVisible" />
<!-- 👉 Send Invoice Sidebar -->
<InvoiceSendInvoiceDrawer v-model:isDrawerOpen="isSendPaymentSidebarVisible" />
</section>
</template>
<style lang="scss">
.invoice-preview-table {
--v-table-header-color: var(--v-theme-surface);
&.v-table .v-table__wrapper table thead tr th{
border-block-end: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)) !important;
}
}
@media print {
.v-theme--dark {
--v-theme-surface: 255, 255, 255;
--v-theme-on-surface: 94, 86, 105;
}
body {
background: none !important;
}
.invoice-header-preview,
.invoice-preview-wrapper {
padding: 0 !important;
}
.product-buy-now {
display: none;
}
.v-navigation-drawer,
.layout-vertical-nav,
.app-customizer-toggler,
.layout-footer,
.layout-navbar,
.layout-navbar-and-nav-container {
display: none;
}
.v-card {
box-shadow: none !important;
.print-row {
flex-direction: row !important;
}
}
.layout-content-wrapper {
padding-inline-start: 0 !important;
}
.v-table__wrapper {
overflow: hidden !important;
}
}
</style>

View File

@@ -0,0 +1,55 @@
<script setup>
import LogisticsCardStatistics from '@/views/apps/logistics/LogisticsCardStatistics.vue'
import LogisticsDeliveryExpectations from '@/views/apps/logistics/LogisticsDeliveryExpectations.vue'
import LogisticsDeliveryPerformance from '@/views/apps/logistics/LogisticsDeliveryPerformance.vue'
import LogisticsOrderByCountries from '@/views/apps/logistics/LogisticsOrderByCountries.vue'
import LogisticsOverviewTable from '@/views/apps/logistics/LogisticsOverviewTable.vue'
import LogisticsShipmentStatistics from '@/views/apps/logistics/LogisticsShipmentStatistics.vue'
import LogisticsVehicleOverview from '@/views/apps/logistics/LogisticsVehicleOverview.vue'
</script>
<template>
<VRow class="match-height">
<VCol cols="12">
<LogisticsCardStatistics />
</VCol>
<VCol
cols="12"
md="6"
>
<LogisticsVehicleOverview />
</VCol>
<VCol
cols="12"
md="6"
>
<LogisticsShipmentStatistics />
</VCol>
<VCol
cols="12"
md="4"
>
<LogisticsDeliveryPerformance />
</VCol>
<VCol
cols="12"
md="4"
>
<LogisticsDeliveryExpectations />
</VCol>
<VCol
cols="12"
md="4"
>
<LogisticsOrderByCountries />
</VCol>
<VCol cols="12">
<LogisticsOverviewTable />
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,378 @@
// ❗ WARNING please use your access token from mapbox.com
<script setup>
import mapboxgl from 'mapbox-gl'
import {
onMounted,
ref,
} from 'vue'
import { PerfectScrollbar } from 'vue3-perfect-scrollbar'
import { useDisplay } from 'vuetify'
import fleetImg from '@images/misc/fleet-car.png'
const { isLeftSidebarOpen } = useResponsiveLeftSidebar()
const accessToken = 'pk.eyJ1Ijoic29jaWFsZXhwbG9yZXIiLCJhIjoiREFQbXBISSJ9.dwFTwfSaWsHvktHrRtpydQ'
const map = ref()
const vuetifyDisplay = useDisplay()
definePage({ meta: { layoutWrapperClasses: 'layout-content-height-fixed' } })
const carImgs = ref([
fleetImg,
fleetImg,
fleetImg,
fleetImg,
])
const refCars = ref([])
const showPanel = ref([
true,
false,
false,
false,
])
const geojson = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
-73.999024,
40.75249842,
],
},
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
-74.03,
40.75699842,
],
},
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
-73.967524,
40.7599842,
],
},
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
-74.0325,
40.742992,
],
},
},
],
}
const activeIndex = ref(0)
onMounted(() => {
mapboxgl.accessToken = accessToken
map.value = new mapboxgl.Map({
container: 'mapContainer',
style: 'mapbox://styles/mapbox/light-v9',
center: [
-73.999024,
40.75249842,
],
zoom: 12.25,
})
for (let index = 0; index < geojson.features.length; index++)
new mapboxgl.Marker({ element: refCars.value[index] }).setLngLat(geojson.features[index].geometry.coordinates).addTo(map.value)
refCars.value[activeIndex.value].classList.add('marker-focus')
})
const vehicleTrackingData = [
{
name: 'VOL-342808',
location: 'Chelsea, NY, USA',
progress: 88,
driverName: 'Veronica Herman',
},
{
name: 'VOL-954784',
location: 'Lincoln Harbor, NY, USA',
progress: 90,
driverName: 'Myrtle Ullrich',
},
{
name: 'VOL-342808',
location: 'Midtown East, NY, USA',
progress: 60,
driverName: 'Barry Schowalter',
},
{
name: 'VOL-343908',
location: 'Hoboken, NY, USA',
progress: 28,
driverName: 'Helen Jacobs',
},
]
const flyToLocation = (geolocation, index) => {
activeIndex.value = index
showPanel.value.fill(false)
showPanel.value[index] = !showPanel.value[index]
if (vuetifyDisplay.mdAndDown.value)
isLeftSidebarOpen.value = false
map.value.flyTo({
center: geolocation,
zoom: 16,
})
}
watch(activeIndex, () => {
refCars.value.forEach((car, index) => {
if (index === activeIndex.value)
car.classList.add('marker-focus')
else
car.classList.remove('marker-focus')
})
})
</script>
<template>
<VLayout class="fleet-app-layout">
<VNavigationDrawer
v-model="isLeftSidebarOpen"
width="320"
absolute
touchless
:border="0"
location="start"
>
<VCard
class="h-100"
flat
>
<VCardItem>
<VCardTitle>
Fleet
</VCardTitle>
<template #append>
<IconBtn
class="d-lg-none"
@click="isLeftSidebarOpen = !isLeftSidebarOpen"
>
<VIcon icon="ri-close-line" />
</IconBtn>
</template>
</VCardItem>
<!-- 👉 Perfect Scrollbar -->
<PerfectScrollbar
:options="{ wheelPropagation: false, suppressScrollX: true }"
style="block-size: calc(100% - 60px);"
>
<VCardText class="pt-0">
<div
v-for="(vehicle, index) in vehicleTrackingData"
:key="index"
class="mb-6"
>
<div
class="d-flex align-center justify-space-between cursor-pointer"
@click="flyToLocation(geojson.features[index].geometry.coordinates, index)"
>
<div class="d-flex gap-x-4">
<VAvatar
icon="ri-car-line"
variant="tonal"
color="secondary"
/>
<div>
<h6 class="text-h6 font-weight-regular">
{{ vehicle.name }}
</h6>
<div class="text-body-1">
{{ vehicle.location }}
</div>
</div>
</div>
<IconBtn density="comfortable">
<VIcon :icon="showPanel[index] ? 'ri-arrow-down-s-line' : $vuetify.locale.isRtl ? 'ri-arrow-left-s-line' : 'ri-arrow-right-s-line'" />
</IconBtn>
</div>
<VExpandTransition mode="out-in">
<div v-show="showPanel[index]">
<div class="py-4 mb-4">
<div class="d-flex justify-space-between text-body-1 mb-1">
<span class="text-high-emphasis ">Delivery Process</span>
<span>{{ vehicle.progress }}%</span>
</div>
<VProgressLinear
:model-value="vehicle.progress"
color="primary"
rounded
height="6"
/>
</div>
<div>
<VTimeline
side="end"
align="start"
truncate-line="both"
density="compact"
class="v-timeline--variant-outlined"
>
<VTimelineItem
icon="ri-checkbox-circle-line"
dot-color="rgb(var(--v-theme-surface))"
icon-color="success"
fill-dot
size="20"
:elevation="0"
>
<div class="text-caption text-uppercase text-success">
TRACKING NUMBER CREATED
</div>
<div class="app-timeline-title">
{{ vehicle.driverName }}
</div>
<div class="text-body-2 mb-1">
Sep 01, 7:53 AM
</div>
</VTimelineItem>
<VTimelineItem
icon="ri-checkbox-circle-line"
dot-color="rgb(var(--v-theme-surface))"
icon-color="success"
fill-dot
size="20"
:elevation="0"
>
<div class="text-caption text-uppercase text-success">
OUT FOR DELIVERY
</div>
<div class="app-timeline-title">
Veronica Herman
</div>
<div class="app-timeline-text mb-1">
Sep 03, 8:02 AM
</div>
</VTimelineItem>
<VTimelineItem
icon="ri-map-pin-line"
dot-color="rgb(var(--v-theme-surface))"
icon-color="primary"
fill-dot
size="20"
:elevation="0"
>
<div class="text-caption text-uppercase text-primary">
ARRIVED
</div>
<div class="app-timeline-title">
Veronica Herman
</div>
<div class="app-timeline-text">
Sep 04, 8:18 AM
</div>
</VTimelineItem>
</VTimeline>
</div>
</div>
</VExpandTransition>
</div>
</VCardText>
</PerfectScrollbar>
</VCard>
</VNavigationDrawer>
<VMain>
<div class="h-100">
<IconBtn
class="d-lg-none navigation-toggle-btn rounded-sm"
variant="elevated"
@click="isLeftSidebarOpen = true"
>
<VIcon icon="ri-menu-line" />
</IconBtn>
<!-- 👉 Fleet map -->
<div
id="mapContainer"
class="basemap"
/>
<img
v-for="(car, index) in carImgs"
:key="index"
ref="refCars"
:src="car"
alt="car Img marker"
height="42"
width="20"
>
</div>
</VMain>
</VLayout>
</template>
<style lang="scss">
@use "@styles/variables/vuetify.scss";
@use "@core-scss/base/mixins.scss";
@import "mapbox-gl/dist/mapbox-gl.css";
.fleet-app-layout {
border-radius: vuetify.$card-border-radius;
@include mixins.elevation(vuetify.$card-elevation);
$sel-fleet-app-layout: &;
@at-root {
.skin--bordered {
@include mixins.bordered-skin($sel-fleet-app-layout);
}
}
}
.navigation-toggle-btn{
position: absolute;
z-index: 1;
inset-block-start: 1rem;
inset-inline-start: 1rem;
}
.navigation-close-btn{
position: absolute;
z-index: 1;
inset-block-start: 1rem;
inset-inline-end: 1rem;
}
.basemap {
block-size: 100%;
inline-size: 100%;
}
.marker-focus {
filter: drop-shadow(0 0 7px rgb(var(--v-theme-primary)));
}
.mapboxgl-ctrl-bottom-left,
.mapboxgl-ctrl-bottom-right {
display: none;
}
/* stylelint-disable-next-line selector-id-pattern */
#mapContainer {
block-size: 100vh !important;
}
</style>

View File

@@ -0,0 +1,203 @@
<script setup>
const headers = [
{
title: 'Name',
key: 'name',
},
{
title: 'Assigned To',
key: 'assignedTo',
sortable: false,
},
{
title: 'Created Date',
key: 'createdDate',
sortable: false,
},
{
title: 'Actions',
key: 'actions',
sortable: false,
},
]
const search = ref('')
// Data table options
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
const updateOptions = options => {
page.value = options.page
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const isPermissionDialogVisible = ref(false)
const isAddPermissionDialogVisible = ref(false)
const permissionName = ref('')
const colors = {
'support': {
color: 'info',
text: 'Support',
},
'users': {
color: 'success',
text: 'Users',
},
'manager': {
color: 'warning',
text: 'Manager',
},
'administrator': {
color: 'primary',
text: 'Administrator',
},
'restricted-user': {
color: 'error',
text: 'Restricted User',
},
}
const { data: permissionsData } = await useApi(createUrl('/apps/permissions', {
query: {
q: search,
itemsPerPage,
page,
sortBy,
orderBy,
},
}))
const permissions = computed(() => permissionsData.value.permissions)
const totalPermissions = computed(() => permissionsData.value.totalPermissions)
const editPermission = name => {
isPermissionDialogVisible.value = true
permissionName.value = name
}
</script>
<template>
<VCard>
<VCardText class="d-flex align-center justify-sm-space-between justify-start gap-4 flex-wrap">
<VTextField
v-model="search"
density="compact"
placeholder="Search Permission"
style="max-inline-size: 15rem;min-inline-size: 12rem;"
/>
<VBtn
density="default"
@click="isAddPermissionDialogVisible = true"
>
Add Permission
</VBtn>
</VCardText>
<VDataTableServer
v-model:items-per-page="itemsPerPage"
:items-length="totalPermissions"
:items-per-page-options="[
{ value: 5, title: '5' },
{ value: 10, title: '10' },
{ value: -1, title: '$vuetify.dataFooter.itemsPerPageAll' },
]"
:headers="headers"
:items="permissions"
item-value="name"
class="text-no-wrap"
@update:options="updateOptions"
>
<!-- Assigned To -->
<template #item.assignedTo="{ item }">
<div class="d-flex gap-2">
<VChip
v-for="text in item.assignedTo"
:key="text"
:color="colors[text].color"
size="small"
>
{{ colors[text].text }}
</VChip>
</div>
</template>
<!-- Name -->
<template #item.name="{ item }">
<h6 class="text-h6 font-weight-regular">
{{ item.name }}
</h6>
</template>
<template #item.createdDate="{ item }">
<span class="text-body-1">{{ item.createdDate }}</span>
</template>
<!-- Actions -->
<template #item.actions="{ item }">
<IconBtn
size="small"
@click="editPermission(item.name)"
>
<VIcon icon="ri-edit-box-line" />
</IconBtn>
<MoreBtn size="small" />
</template>
<!-- Pagination -->
<template #bottom>
<VDivider />
<div class="d-flex justify-end flex-wrap gap-x-6 px-2 py-1">
<div class="d-flex align-center gap-x-2 text-medium-emphasis text-base">
Rows Per Page:
<VSelect
v-model="itemsPerPage"
class="per-page-select"
variant="plain"
:items="[10, 20, 25, 50, 100]"
/>
</div>
<p class="d-flex align-center text-base text-high-emphasis me-2 mb-0">
{{ paginationMeta({ page, itemsPerPage }, totalPermissions) }}
</p>
<div class="d-flex gap-x-2 align-center me-2">
<VBtn
class="flip-in-rtl"
icon="ri-arrow-left-s-line"
variant="text"
density="comfortable"
color="high-emphasis"
:disabled="page <= 1"
@click="page <= 1 ? page = 1 : page--"
/>
<VBtn
class="flip-in-rtl"
icon="ri-arrow-right-s-line"
density="comfortable"
variant="text"
color="high-emphasis"
:disabled="page >= Math.ceil(totalPermissions / itemsPerPage)"
@click="page >= Math.ceil(totalPermissions / itemsPerPage) ? page = Math.ceil(totalPermissions / itemsPerPage) : page++ "
/>
</div>
</div>
</template>
</VDataTableServer>
</VCard>
<AddEditPermissionDialog
v-model:isDialogVisible="isPermissionDialogVisible"
v-model:permission-name="permissionName"
/>
<AddEditPermissionDialog v-model:isDialogVisible="isAddPermissionDialogVisible" />
</template>

View File

@@ -0,0 +1,34 @@
<script setup>
import RoleCards from '@/views/apps/roles/RoleCards.vue'
import UserList from '@/views/apps/roles/UserList.vue'
</script>
<template>
<VRow>
<VCol cols="12">
<h5 class="text-h5 mb-1">
Roles List
</h5>
<p class="text-body-1 mb-0">
A role provided access to predefined menus and features so that depending on assigned role an administrator can have access to what he need
</p>
</VCol>
<!-- 👉 Roles Cards -->
<VCol cols="12">
<RoleCards />
</VCol>
<VCol cols="12">
<h5 class="text-h5 mt-6">
Total users with their roles
</h5>
<p class="text-body-1 mb-6">
Find all of your company's administrator accounts and their associate roles.
</p>
<!-- 👉 User List -->
<UserList />
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,533 @@
<script setup>
import AddNewUserDrawer from '@/views/apps/user/list/AddNewUserDrawer.vue'
// 👉 Store
const searchQuery = ref('')
const selectedRole = ref()
const selectedPlan = ref()
const selectedStatus = ref()
// Data table options
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
const updateOptions = options => {
page.value = options.page
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
// Headers
const headers = [
{
title: 'User',
key: 'user',
},
{
title: 'Email',
key: 'email',
},
{
title: 'Role',
key: 'role',
},
{
title: 'Plan',
key: 'plan',
},
{
title: 'Status',
key: 'status',
},
{
title: 'Actions',
key: 'actions',
sortable: false,
},
]
const {
data: usersData,
execute: fetchUsers,
} = await useApi(createUrl('/apps/users', {
query: {
q: searchQuery,
status: selectedStatus,
plan: selectedPlan,
role: selectedRole,
itemsPerPage,
page,
sortBy,
orderBy,
},
}))
const users = computed(() => usersData.value.users)
const totalUsers = computed(() => usersData.value.totalUsers)
// 👉 search filters
const roles = [
{
title: 'Admin',
value: 'admin',
},
{
title: 'Author',
value: 'author',
},
{
title: 'Editor',
value: 'editor',
},
{
title: 'Maintainer',
value: 'maintainer',
},
{
title: 'Subscriber',
value: 'subscriber',
},
]
const plans = [
{
title: 'Basic',
value: 'basic',
},
{
title: 'Company',
value: 'company',
},
{
title: 'Enterprise',
value: 'enterprise',
},
{
title: 'Team',
value: 'team',
},
]
const status = [
{
title: 'Pending',
value: 'pending',
},
{
title: 'Active',
value: 'active',
},
{
title: 'Inactive',
value: 'inactive',
},
]
const resolveUserRoleVariant = role => {
const roleLowerCase = role.toLowerCase()
if (roleLowerCase === 'subscriber')
return {
color: 'success',
icon: 'ri-user-line',
}
if (roleLowerCase === 'author')
return {
color: 'error',
icon: 'ri-computer-line',
}
if (roleLowerCase === 'maintainer')
return {
color: 'info',
icon: 'ri-pie-chart-line',
}
if (roleLowerCase === 'editor')
return {
color: 'warning',
icon: 'ri-edit-box-line',
}
if (roleLowerCase === 'admin')
return {
color: 'primary',
icon: 'ri-vip-crown-line',
}
return {
color: 'success',
icon: 'ri-user-line',
}
}
const resolveUserStatusVariant = stat => {
const statLowerCase = stat.toLowerCase()
if (statLowerCase === 'pending')
return 'warning'
if (statLowerCase === 'active')
return 'success'
if (statLowerCase === 'inactive')
return 'secondary'
return 'primary'
}
const isAddNewUserDrawerVisible = ref(false)
const addNewUser = async userData => {
// userListStore.addUser(userData)
await $api('/apps/users', {
method: 'POST',
body: userData,
})
// refetch User
fetchUsers()
}
const deleteUser = async id => {
await $api(`/apps/users/${ id }`, { method: 'DELETE' })
// refetch User
fetchUsers()
}
const widgetData = ref([
{
title: 'Session',
value: '21,459',
change: 29,
desc: 'Total Users',
icon: 'ri-group-line',
iconColor: 'primary',
},
{
title: 'Paid Users',
value: '4,567',
change: 18,
desc: 'Last Week Analytics',
icon: 'ri-user-add-line',
iconColor: 'error',
},
{
title: 'Active Users',
value: '19,860',
change: -14,
desc: 'Last Week Analytics',
icon: 'ri-user-follow-line',
iconColor: 'success',
},
{
title: 'Pending Users',
value: '237',
change: 42,
desc: 'Last Week Analytics',
icon: 'ri-user-search-line',
iconColor: 'warning',
},
])
</script>
<template>
<section>
<!-- 👉 Widgets -->
<div class="d-flex mb-6">
<VRow>
<template
v-for="(data, id) in widgetData"
:key="id"
>
<VCol
cols="12"
md="3"
sm="6"
>
<VCard>
<VCardText>
<div class="d-flex justify-space-between">
<div class="d-flex flex-column gap-y-1">
<span class="text-base text-high-emphasis">{{ data.title }}</span>
<h4 class="text-h4 d-flex align-center gap-2">
{{ data.value }}
<span
class="text-base font-weight-regular"
:class="data.change > 0 ? 'text-success' : 'text-error'"
>({{ prefixWithPlus(data.change) }}%)</span>
</h4>
<p class="text-sm mb-0">
{{ data.desc }}
</p>
</div>
<VAvatar
:color="data.iconColor"
variant="tonal"
rounded
size="42"
>
<VIcon
:icon="data.icon"
size="26"
/>
</VAvatar>
</div>
</VCardText>
</VCard>
</VCol>
</template>
</VRow>
</div>
<VCard
title="Filters"
class="mb-6"
>
<VCardText>
<VRow>
<!-- 👉 Select Role -->
<VCol
cols="12"
sm="4"
>
<VSelect
v-model="selectedRole"
label="Select Role"
placeholder="Select Role"
:items="roles"
clearable
clear-icon="ri-close-line"
/>
</VCol>
<!-- 👉 Select Plan -->
<VCol
cols="12"
sm="4"
>
<VSelect
v-model="selectedPlan"
label="Select Plan"
placeholder="Select Plan"
:items="plans"
clearable
clear-icon="ri-close-line"
/>
</VCol>
<!-- 👉 Select Status -->
<VCol
cols="12"
sm="4"
>
<VSelect
v-model="selectedStatus"
label="Select Status"
placeholder="Select Status"
:items="status"
clearable
clear-icon="ri-close-line"
/>
</VCol>
</VRow>
</VCardText>
<VDivider />
<VCardText class="d-flex flex-wrap gap-4">
<!-- 👉 Export button -->
<VBtn
variant="outlined"
color="secondary"
prepend-icon="ri-upload-2-line"
>
Export
</VBtn>
<VSpacer />
<div class="app-user-search-filter d-flex align-center">
<!-- 👉 Search -->
<VTextField
v-model="searchQuery"
placeholder="Search User"
density="compact"
class="me-4"
/>
<!-- 👉 Add user button -->
<VBtn @click="isAddNewUserDrawerVisible = true">
Add New User
</VBtn>
</div>
</VCardText>
<!-- SECTION datatable -->
<VDataTableServer
v-model:items-per-page="itemsPerPage"
v-model:page="page"
:items="users"
item-value="id"
:items-length="totalUsers"
:headers="headers"
show-select
class="text-no-wrap rounded-0"
@update:options="updateOptions"
>
<!-- User -->
<template #item.user="{ item }">
<div class="d-flex align-center">
<VAvatar
size="34"
:variant="!item.avatar ? 'tonal' : undefined"
:color="!item.avatar ? resolveUserRoleVariant(item.role).color : undefined"
class="me-3"
>
<VImg
v-if="item.avatar"
:src="item.avatar"
/>
<span v-else>{{ avatarText(item.fullName) }}</span>
</VAvatar>
<div class="d-flex flex-column">
<RouterLink
:to="{ name: 'apps-user-view-id', params: { id: item.id } }"
class="text-h6 font-weight-medium user-list-name"
>
{{ item.fullName }}
</RouterLink>
<span class="text-sm text-medium-emphasis">@{{ item.username }}</span>
</div>
</div>
</template>
<!-- Role -->
<template #item.role="{ item }">
<div class="d-flex gap-4">
<VIcon
:icon="resolveUserRoleVariant(item.role).icon"
:color="resolveUserRoleVariant(item.role).color"
/>
<span class="text-capitalize text-high-emphasis">{{ item.role }}</span>
</div>
</template>
<!-- Plan -->
<template #item.plan="{ item }">
<span class="text-capitalize text-high-emphasis">{{ item.currentPlan }}</span>
</template>
<!-- Status -->
<template #item.status="{ item }">
<VChip
:color="resolveUserStatusVariant(item.status)"
size="small"
class="text-capitalize"
>
{{ item.status }}
</VChip>
</template>
<!-- Actions -->
<template #item.actions="{ item }">
<IconBtn
size="small"
@click="deleteUser(item.id)"
>
<VIcon icon="ri-delete-bin-7-line" />
</IconBtn>
<IconBtn
size="small"
:to="{ name: 'apps-user-view-id', params: { id: item.id } }"
>
<VIcon icon="ri-eye-line" />
</IconBtn>
<IconBtn
size="small"
color="medium-emphasis"
>
<VIcon
size="24"
icon="ri-more-2-line"
/>
<VMenu activator="parent">
<VList>
<VListItem link>
<template #prepend>
<VIcon icon="ri-download-line" />
</template>
<VListItemTitle>Download</VListItemTitle>
</VListItem>
<VListItem link>
<template #prepend>
<VIcon icon="ri-edit-box-line" />
</template>
<VListItemTitle>Edit</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<!-- Pagination -->
<template #bottom>
<VDivider />
<div class="d-flex justify-end flex-wrap gap-x-6 px-2 py-1">
<div class="d-flex align-center gap-x-2 text-medium-emphasis text-base">
Rows Per Page:
<VSelect
v-model="itemsPerPage"
class="per-page-select"
variant="plain"
:items="[10, 20, 25, 50, 100]"
/>
</div>
<p class="d-flex align-center text-base text-high-emphasis me-2 mb-0">
{{ paginationMeta({ page, itemsPerPage }, totalUsers) }}
</p>
<div class="d-flex gap-x-2 align-center me-2">
<VBtn
class="flip-in-rtl"
icon="ri-arrow-left-s-line"
variant="text"
density="comfortable"
color="high-emphasis"
:disabled="page <= 1"
@click="page <= 1 ? page = 1 : page--"
/>
<VBtn
class="flip-in-rtl"
icon="ri-arrow-right-s-line"
density="comfortable"
variant="text"
color="high-emphasis"
:disabled="page >= Math.ceil(totalUsers / itemsPerPage)"
@click="page >= Math.ceil(totalUsers / itemsPerPage) ? page = Math.ceil(totalUsers / itemsPerPage) : page++ "
/>
</div>
</div>
</template>
</VDataTableServer>
<!-- SECTION -->
</VCard>
<!-- 👉 Add New User -->
<AddNewUserDrawer
v-model:isDrawerOpen="isAddNewUserDrawerVisible"
@user-data="addNewUser"
/>
</section>
</template>
<style lang="scss">
.app-user-search-filter {
inline-size: 24.0625rem;
}
.text-capitalize {
text-transform: capitalize;
}
.user-list-name:not(:hover) {
color: rgba(var(--v-theme-on-background), var(--v-high-emphasis-opacity));
}
</style>

View File

@@ -0,0 +1,101 @@
<script setup>
import UserBioPanel from '@/views/apps/user/view/UserBioPanel.vue'
import UserTabBillingsPlans from '@/views/apps/user/view/UserTabBillingsPlans.vue'
import UserTabConnections from '@/views/apps/user/view/UserTabConnections.vue'
import UserTabNotifications from '@/views/apps/user/view/UserTabNotifications.vue'
import UserTabOverview from '@/views/apps/user/view/UserTabOverview.vue'
import UserTabSecurity from '@/views/apps/user/view/UserTabSecurity.vue'
const route = useRoute('apps-user-view-id')
const userTab = ref(null)
const tabs = [
{
icon: 'ri-group-line',
title: 'Overview',
},
{
icon: 'ri-lock-2-line',
title: 'Security',
},
{
icon: 'ri-bookmark-line',
title: 'Billing & Plan',
},
{
icon: 'ri-notification-4-line',
title: 'Notifications',
},
{
icon: 'ri-link-m',
title: 'Connections',
},
]
const { data: userData } = await useApi(`/apps/users/${ route.params.id }`)
</script>
<template>
<VRow v-if="userData">
<VCol
cols="12"
md="5"
lg="4"
>
<UserBioPanel :user-data="userData" />
</VCol>
<VCol
cols="12"
md="7"
lg="8"
>
<VTabs
v-model="userTab"
class="v-tabs-pill"
>
<VTab
v-for="tab in tabs"
:key="tab.icon"
>
<VIcon
start
:icon="tab.icon"
/>
<span>{{ tab.title }}</span>
</VTab>
</VTabs>
<VWindow
v-model="userTab"
class="mt-6 disable-tab-transition"
:touch="false"
>
<VWindowItem>
<UserTabOverview />
</VWindowItem>
<VWindowItem>
<UserTabSecurity />
</VWindowItem>
<VWindowItem>
<UserTabBillingsPlans />
</VWindowItem>
<VWindowItem>
<UserTabNotifications />
</VWindowItem>
<VWindowItem>
<UserTabConnections />
</VWindowItem>
</VWindow>
</VCol>
</VRow>
<VCard v-else>
<VCardTitle class="text-center">
No User Found
</VCardTitle>
</VCard>
</template>

View File

@@ -0,0 +1,246 @@
<script setup>
import ApexChartAreaChart from '@/views/charts/apex-chart/ApexChartAreaChart.vue'
import ApexChartBalance from '@/views/charts/apex-chart/ApexChartBalance.vue'
import ApexChartDailySalesStates from '@/views/charts/apex-chart/ApexChartDailySalesStates.vue'
import ApexChartDataScience from '@/views/charts/apex-chart/ApexChartDataScience.vue'
import ApexChartExpenseRatio from '@/views/charts/apex-chart/ApexChartExpenseRatio.vue'
import ApexChartHorizontalBar from '@/views/charts/apex-chart/ApexChartHorizontalBar.vue'
import ApexChartMobileComparison from '@/views/charts/apex-chart/ApexChartMobileComparison.vue'
import ApexChartNewTechnologiesData from '@/views/charts/apex-chart/ApexChartNewTechnologiesData.vue'
import ApexChartStatistics from '@/views/charts/apex-chart/ApexChartStatistics.vue'
import ApexChartStocksPrices from '@/views/charts/apex-chart/ApexChartStocksPrices.vue'
</script>
<template>
<VRow id="apex-chart-wrapper">
<!-- 👉 Area chart -->
<VCol cols="12">
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Area Chart</VCardTitle>
<VCardSubtitle>Commercial networks</VCardSubtitle>
<template #append>
<div class="date-picker-wrapper">
<AppDateTimePicker
:model-value="new Date().toISOString()"
prepend-inner-icon="ri-calendar-line"
placeholder="Select Date"
:config="{ position: 'auto right' }"
/>
</div>
</template>
</VCardItem>
<VCardText>
<ApexChartAreaChart />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Data Science -->
<VCol cols="12">
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Data Science</VCardTitle>
<template #append>
<div class="date-picker-wrapper">
<AppDateTimePicker
:model-value="new Date().toISOString()"
prepend-inner-icon="ri-calendar-line"
placeholder="Select Date"
:config="{ position: 'auto right' }"
/>
</div>
</template>
</VCardItem>
<VCardText>
<ApexChartDataScience />
</VCardText>
</VCard>
</VCol>
<!-- 👉 New Technologies Data -->
<VCol cols="12">
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>New Technologies Data</VCardTitle>
<template #append>
<VBtnGroup
density="compact"
color="primary"
variant="outlined"
divided
>
<VBtn>Daily</VBtn>
<VBtn>Monthly</VBtn>
<VBtn>Yearly</VBtn>
</VBtnGroup>
</template>
</VCardItem>
<VCardText>
<ApexChartNewTechnologiesData />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Balance Line Chart -->
<VCol cols="12">
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Balance</VCardTitle>
<VCardSubtitle>Commercial networks &amp; enterprises</VCardSubtitle>
<template #append>
<div class="d-flex align-center">
<h6 class="text-h6 me-3">
$221,267
</h6>
<VChip
label
color="success"
class="font-weight-bold"
>
<VIcon
start
icon="ri-arrow-up-line"
size="15"
/>
<span>22%</span>
</VChip>
</div>
</template>
</VCardItem>
<VCardText>
<ApexChartBalance />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Balance Horizontal Bar -->
<VCol
cols="12"
md="6"
>
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Balance</VCardTitle>
<VCardSubtitle>$74,382.72</VCardSubtitle>
<template #append>
<div class="date-picker-wrapper">
<AppDateTimePicker
:model-value="new Date().toISOString()"
prepend-inner-icon="ri-calendar-line"
placeholder="Select Date"
:config="{ position: 'auto right' }"
/>
</div>
</template>
</VCardItem>
<VCardText>
<ApexChartHorizontalBar />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Stocks Prices -->
<VCol
cols="12"
md="6"
>
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Stocks Prices</VCardTitle>
<VCardSubtitle>$50,863.98</VCardSubtitle>
<template #append>
<div class="date-picker-wrapper">
<AppDateTimePicker
:model-value="new Date().toISOString()"
prepend-inner-icon="ri-calendar-line"
placeholder="Select Date"
:config="{ position: 'auto right' }"
/>
</div>
</template>
</VCardItem>
<VCardText>
<ApexChartStocksPrices />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Daily Sales States -->
<VCol
cols="12"
md="6"
>
<VCard title="Daily Sales States">
<VCardText>
<ApexChartDailySalesStates />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Statistics -->
<VCol
cols="12"
md="6"
>
<VCard title="Statistics">
<VCardText>
<ApexChartStatistics />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Mobile Comparison -->
<VCol
cols="12"
md="6"
>
<VCard title="Mobile Comparison">
<VCardText>
<ApexChartMobileComparison />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Expense Ratio Chart -->
<VCol
cols="12"
md="6"
>
<VCard
title="Expense Ratio"
subtitle="Spending on various categories"
>
<VCardText>
<ApexChartExpenseRatio />
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/libs/apex-chart.scss";
.date-picker-wrapper {
inline-size: 10.5rem;
}
#apex-chart-wrapper {
.v-card-item__append {
padding-inline-start: 0;
}
}
</style>

View File

@@ -0,0 +1,203 @@
<script setup>
import ChartJsBarChart from '@/views/charts/chartjs/ChartJsBarChart.vue'
import ChartJsBubbleChart from '@/views/charts/chartjs/ChartJsBubbleChart.vue'
import ChartJsHorizontalBarChart from '@/views/charts/chartjs/ChartJsHorizontalBarChart.vue'
import ChartJsLineAreaChart from '@/views/charts/chartjs/ChartJsLineAreaChart.vue'
import ChartJsLineChart from '@/views/charts/chartjs/ChartJsLineChart.vue'
import ChartJsPolarAreaChart from '@/views/charts/chartjs/ChartJsPolarAreaChart.vue'
import ChartJsRadarChart from '@/views/charts/chartjs/ChartJsRadarChart.vue'
import ChartJsScatterChart from '@/views/charts/chartjs/ChartJsScatterChart.vue'
const chartJsCustomColors = {
white: '#fff',
yellow: '#ffe802',
primary: '#836af9',
areaChartBlue: '#2c9aff',
barChartYellow: '#ffcf5c',
polarChartGrey: '#4f5d70',
polarChartInfo: '#299aff',
lineChartYellow: '#d4e157',
polarChartGreen: '#28dac6',
lineChartPrimary: '#9e69fd',
lineChartWarning: '#ff9800',
horizontalBarInfo: '#26c6da',
polarChartWarning: '#ff8131',
scatterChartGreen: '#28c76f',
warningShade: '#ffbd1f',
areaChartBlueLight: '#84d0ff',
areaChartGreyLight: '#edf1f4',
scatterChartWarning: '#ff9f43',
}
</script>
<template>
<VRow id="chartjs-wrapper">
<!-- 👉 Statistics Line Chart -->
<VCol cols="12">
<VCard
title="Statistics"
subtitle="Commercial networks and enterprises"
>
<VCardText>
<ChartJsLineChart :colors="chartJsCustomColors" />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Radar Chart -->
<VCol
cols="12"
md="6"
>
<VCard title="Radar Chart">
<VCardText>
<ChartJsRadarChart />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Average Skills Polar Area Chart -->
<VCol
cols="12"
md="6"
>
<VCard title="Average Skills">
<VCardText>
<ChartJsPolarAreaChart :colors="chartJsCustomColors" />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Bubble Chart -->
<VCol cols="12">
<VCard title="Bubble Chart">
<template #append>
<span class="text-body-2">$ 100,000</span>
</template>
<VCardText>
<ChartJsBubbleChart :colors="chartJsCustomColors" />
</VCardText>
</VCard>
</VCol>
<!-- 👉 New Product Data Scatter Chart -->
<VCol cols="12">
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>New Product Data</VCardTitle>
<template #append>
<VBtnGroup
color="primary"
variant="outlined"
density="compact"
divided
>
<VBtn>Daily</VBtn>
<VBtn>Monthly</VBtn>
<VBtn>Yearly</VBtn>
</VBtnGroup>
</template>
</VCardItem>
<VCardText>
<ChartJsScatterChart :colors="chartJsCustomColors" />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Data Science Area Line Chart -->
<VCol cols="12">
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>
Data Science
</VCardTitle>
<template #append>
<div class="date-picker-wrapper">
<AppDateTimePicker
:model-value="new Date().toISOString()"
prepend-inner-icon="ri-calendar-line"
placeholder="Select Date"
:config="{ position: 'auto right' }"
/>
</div>
</template>
</VCardItem>
<VCardText>
<ChartJsLineAreaChart :colors="chartJsCustomColors" />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Latest Statistics -->
<VCol
cols="12"
md="6"
>
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Latest Statistics</VCardTitle>
<template #append>
<div class="date-picker-wrapper">
<AppDateTimePicker
:model-value="new Date().toISOString()"
prepend-inner-icon="ri-calendar-line"
placeholder="Select Date"
:config="{ position: 'auto right' }"
/>
</div>
</template>
</VCardItem>
<VCardText>
<ChartJsBarChart :colors="chartJsCustomColors" />
</VCardText>
</VCard>
</VCol>
<!-- 👉 Balance Horizontal Bar Chart -->
<VCol
cols="12"
md="6"
>
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Balance</VCardTitle>
<VCardSubtitle>$74,123</VCardSubtitle>
<template #append>
<div class="date-picker-wrapper">
<AppDateTimePicker
:model-value="new Date().toISOString()"
prepend-inner-icon="ri-calendar-line"
placeholder="Select Date"
:config="{ position: 'auto right' }"
/>
</div>
</template>
</VCardItem>
<VCardText>
<ChartJsHorizontalBarChart :colors="chartJsCustomColors" />
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
.date-picker-wrapper {
inline-size: 10.5rem;
}
#chartjs-wrapper {
.v-card-item__append {
padding-inline-start: 0;
}
}
</style>

View File

@@ -0,0 +1,161 @@
<script setup>
import * as demoCode from '@/views/demos/components/alert/demoCodeAlert'
</script>
<template>
<VRow>
<VCol cols="12">
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<DemoAlertBasic />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Colors -->
<AppCardCode
title="Colors"
:code="demoCode.colors"
>
<p>The <code>color</code> prop is used to change the background color of the alert.</p>
<DemoAlertColors />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Icons -->
<AppCardCode
title="Icons"
:code="demoCode.icons"
>
<p>The <code>icon</code> prop allows you to add an icon to the beginning of the alert component. If a <code>type</code> is provided, this will override the default type icon. Additionally, setting the <code>icon</code> prop to false will remove the icon altogether.</p>
<DemoAlertIcons />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Border -->
<AppCardCode
title="Border"
:code="demoCode.border"
>
<p>The <code>border</code> prop adds a simple border to one of the 4 sides of the alert. This can be combined with props like <code>color</code>, <code>type</code> and <code>icon</code> to provide unique accents to the alert.</p>
<DemoAlertBorder />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Colored Border -->
<AppCardCode
title="Colored Border"
:code="demoCode.coloredBorder"
>
<p>The <code>colored-border</code> prop removes the alert background in order to accent the <code>border</code> prop. If a type is set, it will use the type's default color. If no <code>color</code> or <code>type</code> is set, the color will default to the inverted color of the applied theme (black for light and white/gray for dark).</p>
<DemoAlertColoredBorder />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Density -->
<AppCardCode
title="Density"
:code="demoCode.density"
>
<p>The <code>density</code> prop decreases the height of the alert based upon 1 of 3 levels of density. <code>default</code>, <code>comfortable</code>, and <code>compact</code>.</p>
<DemoAlertDensity />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Type -->
<AppCardCode
title="Type"
:code="demoCode.type"
>
<p>The <code>type</code> prop provides 4 default v-alert styles: <code>success</code>, <code>info</code>, <code>warning</code>, and <code>error</code>. Each of these styles provide a default icon and color.</p>
<DemoAlertType />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Closable -->
<AppCardCode
title="Closable"
:code="demoCode.closable"
>
<p>The <code>closable</code> prop adds a close button to the end of the alert component. Clicking this button will set its value to false and effectively hide the alert.</p>
<DemoAlertClosable />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 V-model support -->
<AppCardCode
title="v-model support"
:code="demoCode.vModelSupport"
>
<p>Clicking this button will set its value to <code>false</code> and effectively hide the alert. You can restore the alert by binding <code>v-model</code> and setting it to true.</p>
<DemoAlertVModelSupport />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Outlined -->
<AppCardCode
title="Outlined"
:code="demoCode.outlined"
>
<p>The <code>variant="outlined"</code> prop inverts the style of an alert, inheriting the currently applied <code>color</code>, applying it to the text and border, and making its background transparent.</p>
<DemoAlertOutlined />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Tonal -->
<AppCardCode
title="Tonal"
:code="demoCode.tonal"
>
<p>The <code>variant</code> prop provides an easy way to change the overall style of your alerts. The <code>variant="tonal"</code> prop is a simple alert variant that applies a reduced opacity background of the provided color.</p>
<DemoAlertTonal />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Elevation -->
<AppCardCode
title="Elevation"
:code="demoCode.elevation"
>
<p>Use <code>elevation</code> prop to set a box-shadow to alert.</p>
<DemoAlertElevation />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Prominent -->
<AppCardCode
title="Prominent"
:code="demoCode.prominent"
>
<p>The <code>prominent</code> prop provides a more pronounced alert by increasing the size of the icon.</p>
<DemoAlertProminent />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,112 @@
<script setup>
import * as demoCode from '@/views/demos/components/avatar/demoCodeAvatar'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Colors -->
<AppCardCode
title="Colors"
:code="demoCode.colors"
>
<p>Use <code>color</code> prop to change the background color of avatar.</p>
<DemoAvatarColors />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Initials -->
<AppCardCode
title="Tonal"
:code="demoCode.tonal"
>
<p>Use <code>variant="tonal"</code> to create light background avatars.</p>
<DemoAvatarTonal />
</AppCardCode>
</VCol>
<VCol
md="6"
cols="12"
>
<!-- 👉 Sizes -->
<AppCardCode
title="Sizes"
:code="demoCode.sizes"
>
<p>The <code>size</code> prop allows you to change the height and width of the avatar.</p>
<DemoAvatarSizes />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Icons -->
<AppCardCode
title="Icons"
:code="demoCode.icons"
>
<p>You can use <code>icon</code> prop of <code>v-avatar</code> component for rendering icons.</p>
<DemoAvatarIcons />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Rounded -->
<AppCardCode
title="Rounded"
:code="demoCode.rounded"
>
<p>The <code>rounded</code> prop can be used to change the border radius of <code>v-avatar</code>.</p>
<DemoAvatarRounded />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Images -->
<AppCardCode
title="Images"
:code="demoCode.images"
>
<p>You can use <code>image</code> prop of <code>v-avatar</code> component for rendering image.</p>
<DemoAvatarImages />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Group -->
<AppCardCode
title="Group"
:code="demoCode.group"
>
<p>Use <code>v-avatar-group</code> class as a wrapper of avatars.</p>
<DemoAvatarGroup />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,156 @@
<script setup>
import * as demoCode from '@/views/demos/components/badge/demoCodeBadge'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Style -->
<AppCardCode
title="Style"
:code="demoCode.style"
>
<p>You can use various props like <code>bordered</code>, <code>dot</code>, <code>inline</code>, <code>rounded</code> etc. to style the badge.</p>
<DemoBadgeStyle />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Color -->
<AppCardCode
title="Color"
:code="demoCode.color"
>
<p>Use <code>color</code> prop to create various background badges.</p>
<DemoBadgeColor />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Position -->
<AppCardCode
title="Position"
:code="demoCode.position"
>
<p>You can use <code>location</code> prop to change the position of the badge. Possible values are <code>top-end</code>, <code>bottom-end</code>, <code>bottom-start</code>, <code>top-start</code>.</p>
<DemoBadgePosition />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Icon -->
<AppCardCode
title="Icon"
:code="demoCode.icon"
>
<p>You can use <code>icon</code> prop or use <code>slot</code> to render the icon</p>
<DemoBadgeIcon />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Avatar Status -->
<AppCardCode
title="Avatar Status"
:code="demoCode.avatarStatus"
>
<p>You can use badge with avatar as status.</p>
<DemoBadgeAvatarStatus />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Dynamic notifications -->
<AppCardCode
title="Dynamic notifications"
:code="demoCode.dynamicNotifications"
>
<p>You can incorporate badges with dynamic content to make things such as a notification system.</p>
<DemoBadgeDynamicNotifications />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Show on hover -->
<AppCardCode
title="Show on hover"
:code="demoCode.showOnHover"
>
<p>You can do many things with visibility control, for example, show badge on hover.</p>
<DemoBadgeShowOnHover />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Tabs -->
<AppCardCode
title="Tabs"
:code="demoCode.tabs"
>
<p>Badges help convey information to the user in a variety of ways.</p>
<DemoBadgeTabs />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Maximum value content -->
<AppCardCode
title="Maximum Value"
:code="demoCode.maximumValue"
>
<p>Use <code>max</code> prop to cap the value of the badge content</p>
<DemoBadgeMaximumValue />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Tonal"
:code="demoCode.tonal"
>
<p>Use class <code>v-badge--tonal</code> for using tonal variant badge.</p>
<DemoBadgeTonal />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,189 @@
<script setup>
import * as demoCode from '@/views/demos/components/button/demoCodeButton'
</script>
<template>
<VRow>
<VCol cols="12">
<!-- 👉 Colors -->
<AppCardCode
title="Colors"
:code="demoCode.colors"
>
<p>The <code>color</code> prop is used to change the background color of the alert.</p>
<DemoButtonColors />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Outlined -->
<AppCardCode
title="Outlined"
:code="demoCode.outlined"
>
<p>The <code>outlined</code> variant option is used to create outlined buttons.</p>
<DemoButtonOutlined />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Flat -->
<AppCardCode
title="Flat"
:code="demoCode.flat"
>
<p>The <code>flat</code> buttons still maintain their background color, but have no box shadow.</p>
<DemoButtonFlat />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Rounded -->
<AppCardCode
title="Rounded"
:code="demoCode.rounded"
>
<p>Use the <code>rounded</code> prop to control the border radius of buttons.</p>
<DemoButtonRounded />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Text -->
<AppCardCode
title="Text"
:code="demoCode.text"
>
<p>Use <code>text</code> variant option to create text button. Text buttons have no box shadow and no background.</p>
<DemoButtonText />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Plain -->
<AppCardCode
title="Plain"
:code="demoCode.plain"
>
<p>Use <code>plain</code> variant option to a create a plain button. Plain buttons have a lower baseline opacity that reacts to hover and focus.</p>
<DemoButtonPlain />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Tonal -->
<AppCardCode
title="Tonal"
:code="demoCode.tonal"
>
<p>Use <code>tonal</code> variant option to a create a light background button.</p>
<DemoButtonTonal />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Icon -->
<AppCardCode
title="Icon"
:code="demoCode.icon"
>
<p>Icons can be used inside of buttons to add emphasis to the action.</p>
<DemoButtonIcon />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Icon Only -->
<AppCardCode
title="Icon Only"
:code="demoCode.iconOnly"
>
<p>Use <code>icon</code> prop to create button with icon only. This property makes the button rounded and applies the text prop styles.</p>
<DemoButtonIconOnly />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Sizing -->
<AppCardCode
title="Sizing"
:code="demoCode.sizing"
>
<p>Buttons can be given different sizing options to fit a multitude of scenarios.</p>
<DemoButtonSizing />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Block -->
<AppCardCode
title="Block"
:code="demoCode.block"
>
<p>The <code>block</code> prop allows buttons to extend the full available width.</p>
<DemoButtonBlock />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Loaders -->
<AppCardCode
title="Loaders"
:code="demoCode.loaders"
>
<p>Using the <code>loading</code> prop, you can notify a user that there is processing taking place. The default behavior is to use a <code>v-progress-circular</code> component but this can be customized.</p>
<DemoButtonLoaders />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Router -->
<AppCardCode
title="Router"
:code="demoCode.router"
>
<p>Use <code>to</code> prop to create button with router support.</p>
<VAlert
color="warning"
variant="tonal"
class="mb-4"
>
Note: On click of the link button, You will get redirected to another page.
</VAlert>
<DemoButtonRouter />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Link -->
<AppCardCode
title="Link"
:code="demoCode.link"
>
<p>Designates that the component is a link. This is automatic when using the <code>href</code> or <code>to</code> prop.</p>
<VAlert
color="warning"
variant="tonal"
class="mb-4"
>
Note: On click of the link button, You will get redirected to another page.
</VAlert>
<DemoButtonLink />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Button Group -->
<AppCardCode
title="Group"
:code="demoCode.group"
>
<p>
Wrap buttons with the <code>v-btn-toggle</code> component to create a group button. You can add a visual divider between buttons with the <code>divided</code> prop.
</p>
<DemoButtonGroup />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,157 @@
<script setup>
import * as demoCode from '@/views/demos/components/chip/demoCodeChip'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Color -->
<AppCardCode
title="Color"
:code="demoCode.color"
>
<p>Use <code>color</code> prop to change the background color of chips.</p>
<DemoChipColor />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Elevated -->
<AppCardCode
title="Elevated"
:code="demoCode.elevated"
>
<p>Use <code>elevated</code> variant option to create filled chips.</p>
<DemoChipElevated />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Outlined -->
<AppCardCode
title="Outlined"
:code="demoCode.outlined"
>
<p>Use <code>outlined</code> variant option to create outline border chips.</p>
<DemoChipOutlined />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Label -->
<AppCardCode
title="Label"
:code="demoCode.label"
>
<p>Label chips use the <code>v-card</code> border-radius. Use <code>label</code> prop to create label chips.</p>
<DemoChipLabel />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Closable -->
<AppCardCode
title="Closable"
:code="demoCode.closable"
>
<p>Closable chips can be controlled with a <code>v-model</code>.</p>
<DemoChipClosable />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Chip With Icon -->
<AppCardCode
title="With Icon"
:code="demoCode.withIcon"
>
<p>Chips can use text or any icon available in the Material Icons font library.</p>
<DemoChipWithIcon />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Avatar -->
<AppCardCode
title="With Avatar"
:code="demoCode.withAvatar"
>
<p>Use <code>pill</code> prop to remove the <code>v-avatar</code> padding.</p>
<DemoChipWithAvatar />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Sizes -->
<AppCardCode
title="Sizes"
:code="demoCode.sizes"
>
<p>The <code>v-chip</code> component can have various sizes from <code>x-small</code> to <code>x-large</code>.</p>
<DemoChipSizes />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 In Selects -->
<AppCardCode
title="In Selects"
:code="demoCode.inSelects"
>
<p>Selects can use <code>chips</code> to display the selected data. Try adding your own tags below.</p>
<DemoChipInSelects />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Expandable -->
<AppCardCode
title="Expandable"
:code="demoCode.expandable"
>
<p>Chips can be combined with <code>v-menu</code> to enable a specific set of actions for a chip.</p>
<DemoChipExpandable />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,127 @@
<script setup>
import * as demoCode from '@/views/demos/components/dialog/demoCodeDialog'
</script>
<template>
<VRow>
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>A dialog contains two slots, one for its activator and one for its content (default). Good for Privacy Policies.</p>
<DemoDialogBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Persistent -->
<AppCardCode
title="Persistent"
:code="demoCode.persistent"
>
<p>Use <code>persistent</code> prop to create persistent dialog.</p>
<DemoDialogPersistent />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Scrollable -->
<AppCardCode
title="Scrollable"
:code="demoCode.scrollable"
>
<p>Use <code>scrollable</code> prop to create scrollable dialog.</p>
<DemoDialogScrollable />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Loader -->
<AppCardCode
title="Loader"
:code="demoCode.loader"
>
<p>The <code>v-dialog</code> component makes it easy to create a customized loading experience for your application.</p>
<DemoDialogLoader />
</AppCardCode>
</VCol>
<VCol
col="12"
md="6"
>
<!-- 👉 Nesting -->
<AppCardCode
title="Nesting"
:code="demoCode.nesting"
>
<p>Dialogs can be nested: you can open one dialog from another.</p>
<DemoDialogNesting />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Overflowed -->
<AppCardCode
title="Overflowed"
:code="demoCode.overflowed"
>
<p>Modals that do not fit within the available window space will scroll the container.</p>
<DemoDialogOverflowed />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Fullscreen -->
<AppCardCode
title="Fullscreen"
:code="demoCode.fullscreen"
>
<p>Due to limited space, full-screen dialogs may be more appropriate for mobile devices than dialogs used on devices with larger screens.</p>
<DemoDialogFullscreen />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Form -->
<AppCardCode
title="Form"
:code="demoCode.form"
>
<p>Just a simple example of a form in a dialog.</p>
<DemoDialogForm />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,119 @@
<script setup>
import * as demoCode from '@/views/demos/components/expansion-panel/demoCodeExpansionPanel'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
variant="outlined"
:code="demoCode.basic"
>
<p>Expansion panels in their simplest form display a list of expandable items. However, with the <code>multiple</code> prop, the expansion-panel can remain open until explicitly closed.</p>
<DemoExpansionPanelBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Accordion -->
<AppCardCode
title="Accordion"
variant="outlined"
:code="demoCode.accordion"
>
<p>Use <code>accordion</code> variant option to create <strong>Accordion</strong> Panels. Accordion expansion-panel hasn't got margins around active panel.</p>
<DemoExpansionPanelAccordion />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Inset -->
<AppCardCode
title="Inset"
variant="outlined"
:code="demoCode.inset"
>
<p>Use <code>inset</code> variant option to create Inset Panels. The Inset expansion-panel becomes smaller when activated.</p>
<DemoExpansionPanelInset />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Popout -->
<AppCardCode
title="Popout"
variant="outlined"
:code="demoCode.popout"
>
<p>
Use <code>popout</code> variant option to create expansion-panel with popout design. With it, expansion-panel is enlarged when activated.
</p>
<DemoExpansionPanelPopout />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Custom Icon -->
<AppCardCode
title="Custom Icon"
variant="outlined"
:code="demoCode.customIcon"
>
<p>Expand action icon can be customized with <code>expand-icon</code> prop or the <code>actions</code> slot.</p>
<DemoExpansionPanelCustomIcon />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Model -->
<AppCardCode
title="Model"
variant="outlined"
:code="demoCode.model"
>
<p>Expansion panels can be controlled externally by modifying the <code>v-model</code>. If <code>multiple</code> prop is used then it is an array containing the indices of the open items.</p>
<DemoExpansionPanelModel />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Customized Accordion -->
<AppCardCode
title="Customized Accordion"
variant="outlined"
:code="demoCode.customizedAccordion"
>
<DemoExpansionPanelCustomizedAccordion />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,205 @@
<script setup>
import * as demoCode from '@/views/demos/components/list/demoCodeList'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
no-padding
:code="demoCode.basic"
>
<VCardText><code>v-list</code> component can contain an avatar, content, actions and much more.</VCardText>
<VCardText>
<DemoListBasic />
</VCardText>
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Rounded -->
<AppCardCode
title="Rounded"
no-padding
:code="demoCode.rounded"
>
<VCardText>You can make <code>v-list-item</code> rounded using <code>rounded</code> prop.</VCardText>
<VCardText>
<DemoListRounded />
</VCardText>
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Density -->
<AppCardCode
title="Density"
:code="demoCode.density"
no-padding
>
<VCardText>Use <code>density</code> prop to adjusts the spacing within the component. Available options are: <code>default</code>, <code>comfortable</code>, and <code>compact</code>.</VCardText>
<VCardText>
<DemoListDensity />
</VCardText>
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Nav -->
<AppCardCode
title="Nav"
no-padding
:code="demoCode.nav"
>
<VCardText>Lists can receive an alternative <code>nav</code> styling that reduces the width <code>v-list-item</code> takes up as well as adding a border radius.</VCardText>
<VCardText>
<DemoListNav />
</VCardText>
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Action and item group -->
<AppCardCode
title="Action and item group"
no-padding
:code="demoCode.actionAndItemGroup"
>
<VCardText>A <code>three-line</code> list with actions. Utilizing <code>v-list-group</code>, easily connect actions to your tiles.</VCardText>
<VCardText>
<DemoListActionAndItemGroup />
</VCardText>
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Sub Group -->
<AppCardCode
title="Sub Group"
no-padding
:code="demoCode.subGroup"
>
<VCardText>
Using the <code>v-list-group</code> component you can create up to 2 levels in depth using the sub-group prop.
</VCardText>
<VCardText>
<DemoListSubGroup />
</VCardText>
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Two lines and Subheader -->
<AppCardCode
title="Two lines and subheader"
no-padding
:code="demoCode.twoLinesAndSubheader"
>
<VCardText>Lists can contain subheaders, dividers, and can contain 1 or more lines. The subtitle will overflow with ellipsis if it extends past one line.</VCardText>
<VCardText>
<DemoListTwoLinesAndSubheader />
</VCardText>
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Three Line -->
<AppCardCode
title="Three Line"
no-padding
:code="demoCode.threeLine"
>
<VCardText>For three line lists, the subtitle will clamp vertically at 2 lines and then ellipsis. This feature uses line-clamp and is not supported in all browsers.</VCardText>
<VCardText>
<DemoListThreeLine />
</VCardText>
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Users List -->
<AppCardCode
title="User List"
no-padding
:code="demoCode.userList"
>
<VCardText>
<DemoListUserList />
</VCardText>
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Progress List -->
<AppCardCode
title="Progress List"
no-padding
:code="demoCode.progressList"
>
<VCardText>
<DemoListProgressList />
</VCardText>
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Shaped -->
<AppCardCode
title="Shaped"
no-padding
:code="demoCode.shaped"
>
<VCardText>
Shaped lists have rounded borders on one side of the <code>v-list-item</code>.
</VCardText>
<VCardText>
<DemoListShaped />
</VCardText>
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,99 @@
<script setup>
import * as demoCode from '@/views/demos/components/menu/demoCodeMenu'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>
Remember to put the element that activates the menu in the activator slot.
</p>
<DemoMenuBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Custom transitions -->
<AppCardCode
title="Custom transitions"
:code="demoCode.customTransitions"
>
<p>Vuetify comes with 3 standard transitions, <code>scale</code>, <code>slide-x</code> and <code>slide-y</code>. Use <code>transition</code> prop to add transition to a menu.</p>
<DemoMenuCustomTransitions />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Location -->
<AppCardCode
title="Location"
:code="demoCode.location"
>
<p>Menu can be offset relative to the activator by using the <code>location</code> prop.</p>
<DemoMenuLocation />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Open on hover -->
<AppCardCode
title="Open on hover"
:code="demoCode.openOnHover"
>
<p>Menus can be accessed using hover instead of clicking with the <code>open-on-hover</code> prop.</p>
<DemoMenuOpenOnHover />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Popover -->
<AppCardCode
title="Popover"
:code="demoCode.popover"
>
<p>A menu can be configured to be static when opened, allowing it to function as a popover. This can be useful when there are multiple interactive items within the menu contents.</p>
<DemoMenuPopover />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Activator and tooltip -->
<AppCardCode
title="Activator and tooltip"
:code="demoCode.activatorAndTooltip"
>
<p>With the new <code>v-slot</code> syntax, nested activators such as those seen with a <code>v-menu</code> and <code>v-tooltip</code> attached to the same activator button, need a particular setup in order to function correctly</p>
<DemoMenuActivatorAndTooltip />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,157 @@
<script setup>
import * as demoCode from '@/views/demos/components/pagination/demoCodePagination'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>The <code>v-pagination</code> component is used to separate long sets of data.</p>
<DemoPaginationBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 outline basic variant -->
<AppCardCode
title="Outline"
:code="demoCode.outline"
>
<p>The <code>variant='outline'</code> prop is used to give outline to pagination item.</p>
<DemoPaginationOutline />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Circle -->
<AppCardCode
title="Circle"
:code="demoCode.circle"
>
<p>The <code>rounded</code> prop allows you to render pagination buttons with alternative styles.</p>
<DemoPaginationCircle />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 outline circle variant -->
<AppCardCode
title="Outline Circle"
:code="demoCode.outlineCircle"
>
<p>The <code>variant='outline'</code> and <code>rounded</code> prop is used to give rounded outline to pagination item.</p>
<DemoPaginationOutlineCircle />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Disabled -->
<AppCardCode
title="Disabled"
:code="demoCode.disabled"
>
<p>Pagination items can be manually deactivated using the <code>disabled</code> prop.</p>
<DemoPaginationDisabled />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Icons -->
<AppCardCode
title="Icons"
:code="demoCode.icons"
>
<p>Previous and next page icons can be customized with the <code>prev-icon</code> and <code>next-icon</code> props.</p>
<DemoPaginationIcons />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Length -->
<AppCardCode
title="Length"
:code="demoCode.length"
>
<p>Using the <code>length</code> prop you can set the length of <code>v-pagination</code>, if the number of page buttons exceeds the parent container, it will truncate the list.</p>
<DemoPaginationLength />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Total visible -->
<AppCardCode
title="Total visible"
:code="demoCode.totalVisible"
>
<p>You can also manually set the maximum number of visible page buttons with the <code>total-visible</code> prop.</p>
<DemoPaginationTotalVisible />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Color -->
<AppCardCode
title="Color"
:code="demoCode.color"
>
<p>Use <code>color</code> prop for create different color pagination.</p>
<DemoPaginationColor />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Size -->
<AppCardCode
title="Size"
:code="demoCode.size"
>
<p>Use <code>size</code> prop to sets the height and width of the component. Default unit is px. Can also use the following predefined sizes: <strong>x-small</strong>, <strong>small</strong>, <strong>default</strong>, <strong>large</strong>, and <strong>x-large</strong>.</p>
<DemoPaginationSize />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,67 @@
<script setup>
import * as demoCode from '@/views/demos/components/progress-circular/demoCodeProgressCircular'
</script>
<template>
<VRow class="match-height">
<!-- 👉 Progress circular color -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="color"
:code="demoCode.color"
>
<p>Alternate colors can be applied to <code>v-progress-circular</code> using the <code>color</code> prop.</p>
<DemoProgressCircularColor />
</AppCardCode>
</VCol>
<!-- 👉 Progress circular Indeterminate -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Indeterminate"
:code="demoCode.indeterminate"
>
<p>Using the <code>indeterminate</code> prop, a <code>v-progress-circular</code> continues to animate indefinitely.</p>
<DemoProgressCircularIndeterminate />
</AppCardCode>
</VCol>
<!-- 👉 Progress circular Rotate -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Rotate"
:code="demoCode.rotate"
>
<p>The <code>rotate</code> prop gives you the ability to customize the <code>v-progress-circular</code>'s origin.</p>
<DemoProgressCircularRotate />
</AppCardCode>
</VCol>
<!-- 👉 Progress circular Size and Width -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Size"
:code="demoCode.size"
>
<p>The <code>size</code> and <code>width</code> props allow you to easily alter the size and width of the <code>v-progress-circular</code> component.</p>
<DemoProgressCircularSize />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,110 @@
<script setup>
import * as demoCode from '@/views/demos/components/progress-linear/demoCodeProgressLinear'
</script>
<template>
<VRow class="match-height">
<!-- 👉 Progress linear color -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Color"
:code="demoCode.color"
>
<p>Use the props <code>color</code> and <code>background-color</code> to set colors.</p>
<DemoProgressLinearColor />
</AppCardCode>
</VCol>
<!-- 👉 Progress linear Buffering -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Buffering"
:code="demoCode.buffering"
>
<p>The primary value is controlled by <code>v-model</code>, whereas the buffer is controlled by the <code>buffer-value</code> prop.</p>
<DemoProgressLinearBuffering />
</AppCardCode>
</VCol>
<!-- 👉 Progress linear indeterminate -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Indeterminate"
:code="demoCode.indeterminate"
>
<p>for continuously animating progress bar,use prop <code>indeterminate</code>. This indicates continuous process. </p>
<DemoProgressLinearIndeterminate />
</AppCardCode>
</VCol>
<!-- 👉 Progress linear Reversed -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Reversed"
:code="demoCode.reversed"
>
<p>Use prop <code>reverse</code> to animate continuously in reverse direction. The component also has RTL support.</p>
<DemoProgressLinearReversed />
</AppCardCode>
</VCol>
<!-- 👉 Progress linear Rounded -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Rounded"
:code="demoCode.rounded"
>
<p>The <code> rounded </code> prop is used to apply a border radius to the v-progress-linear component.</p>
<DemoProgressLinearRounded />
</AppCardCode>
</VCol>
<!-- 👉 Progress linear Slot -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Slots"
:code="demoCode.slots"
>
<p>we can bind user input using <code>v-model</code>.You can also use the default slot for the same.</p>
<DemoProgressLinearSlots />
</AppCardCode>
</VCol>
<!-- 👉 Progress Linear Striped -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Striped"
:code="demoCode.striped"
>
<p> The <code>striped</code> prop is used to apply striped background.</p>
<DemoProgressLinearStriped />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,126 @@
<script setup>
import * as demoCode from '@/views/demos/components/snackbar/demoCodeSnackbar'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>The <code>v-snackbar</code> component is used to display a quick message to a user. Snackbars support positioning, removal delay, and callbacks.</p>
<DemoSnackbarBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 With Action -->
<AppCardCode
title="With Action"
:code="demoCode.withAction"
>
<p>Use <code>actions</code> slot to add action button. A <code>v-snackbar</code> in its simplest form displays a temporary and closable notification to the user.</p>
<DemoSnackbarWithAction />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Multi line -->
<AppCardCode
title="Multi Line"
:code="demoCode.multiLine"
>
<p>The <code>multi-line</code> property extends the height of the <code>v-snackbar</code> to give you a little more room for content.</p>
<DemoSnackbarMultiLine />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Timeout -->
<AppCardCode
title="Timeout"
:code="demoCode.timeout"
>
<p>The <code>timeout</code> property lets you customize the delay before the <code>v-snackbar</code> is hidden.</p>
<DemoSnackbarTimeout />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Vertical -->
<AppCardCode
title="Vertical"
:code="demoCode.vertical"
>
<p>The <code>vertical</code> property allows you to stack the content of your <code>v-snackbar</code>.</p>
<DemoSnackbarVertical />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Position -->
<AppCardCode
title="Position"
:code="demoCode.position"
>
<p>Use <code>location</code> prop to change the position of snackbar.</p>
<DemoSnackbarPosition />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Variants -->
<AppCardCode
title="Variants"
:code="demoCode.variants"
>
<p>Apply different styles to the snackbar using props such as <code>shaped</code>, <code>rounded</code>, <code>color</code>, <code>text</code>, <code>outlined</code>, <code>tile</code> and more.</p>
<DemoSnackbarVariants />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Transition -->
<AppCardCode
title="Transition"
:code="demoCode.transition"
>
<p>Use transition prop to sets the component transition.</p>
<DemoSnackbarTransition />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,179 @@
<script setup>
import * as demoCode from '@/views/demos/components/tabs/demoCodeTabs'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>The <code>v-tabs</code> component is used for hiding content behind a selectable item.</p>
<DemoTabsBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic Pill -->
<AppCardCode
title="Basic Pill"
:code="demoCode.basicPill"
>
<p>Use our custom class <code>.v-tabs-pill</code> along with <code>v-tabs</code> component to style pill tabs.</p>
<DemoTabsBasicPill />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Stacked -->
<AppCardCode
title="Stacked"
:code="demoCode.stacked"
>
<p>Using <code>stacked</code> prop you can have buttons that use both icons and text.</p>
<DemoTabsStacked />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Vertical -->
<AppCardCode
title="Vertical"
:code="demoCode.vertical"
>
<p>The <code>vertical</code> prop allows for <code>v-tab</code> components to stack vertically.</p>
<DemoTabsVertical />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Vertical Pill -->
<AppCardCode
title="Vertical Pill"
:code="demoCode.verticalPill"
>
<p>Use our custom class .v-tabs-pill along with v-tabs component to style pill tabs.</p>
<DemoTabsVerticalPill />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Alignment -->
<AppCardCode
title="Alignment"
:code="demoCode.alignment"
>
<p>Use <code>align-tabs</code> prop to change the tabs alignment.</p>
<DemoTabsAlignment />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Pagination -->
<AppCardCode
title="Pagination"
:code="demoCode.pagination"
>
<p>If the tab items overflow their container, pagination controls will appear on desktop.</p>
<DemoTabsPagination />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Custom Icons -->
<AppCardCode
title="Custom Icons"
:code="demoCode.customIcons"
>
<p><code>prev-icon</code> and <code>next-icon</code> props can be used for applying custom pagination icons.</p>
<DemoTabsCustomIcons />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Fixed tabs -->
<AppCardCode
title="Fixed"
:code="demoCode.fixed"
>
<p>The <code>fixed-tabs</code> prop forces <code>v-tab</code> to take up all available space up to the maximum width (300px).</p>
<DemoTabsFixed />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Grow -->
<AppCardCode
title="Grow"
:code="demoCode.grow"
>
<p>The <code>grow</code> prop will make the tab items take up all available space with no limit.</p>
<DemoTabsGrow />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Programmatic Navigation -->
<AppCardCode
title="Programmatic Navigation"
:code="demoCode.programmaticNavigation"
>
<DemoTabsProgrammaticNavigation />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Dynamic Tabs -->
<AppCardCode
title="Dynamic"
:code="demoCode.dynamic"
>
<p>Tabs can be dynamically added and removed. This allows you to update to any number and the <code>v-tabs</code> component will react.</p>
<DemoTabsDynamic />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,21 @@
<template>
<VRow>
<VCol
cols="12"
md="6"
>
<TimelineBasic />
</VCol>
<VCol
cols="12"
md="6"
>
<TimelineOutlined />
</VCol>
<VCol cols="12">
<TimelineWithIcons />
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,77 @@
<script setup>
import * as demoCode from '@/views/demos/components/tooltip/demoCodeTooltip'
</script>
<template>
<VRow>
<VCol cols="12">
<!-- 👉 Location -->
<AppCardCode
title="Location"
:code="demoCode.location"
>
<p>Use the <code>location</code> prop to specify on which side of the element the tooltip should show</p>
<DemoTooltipLocation />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Events -->
<AppCardCode
title="Events"
:code="demoCode.events"
>
<DemoTooltipEvents />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Delay On Hover -->
<AppCardCode
title="Delay On Hover"
:code="demoCode.delayOnHover"
>
<p>Delay (in ms) after which tooltip opens (when <code>open-on-hover</code> prop is set to true)</p>
<DemoTooltipDelayOnHover />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 V-model Support -->
<AppCardCode
title="V-Model Support"
:code="demoCode.vModelSupport"
>
<p>Tooltip visibility can be programmatically changed using <code>v-model</code>.</p>
<DemoTooltipVModelSupport />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Transition -->
<AppCardCode
title="Transition"
:code="demoCode.transition"
>
<p>Use <code>transition</code> prop to sets the component transition.</p>
<DemoTooltipTransition />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Tooltip on Various Elements -->
<AppCardCode
title="Tooltip on Various Elements"
:code="demoCode.tooltipOnVariousElements"
>
<p>Tooltips can wrap any element.</p>
<DemoTooltipTooltipOnVariousElements />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,131 @@
<script setup>
import AnalyticsAward from '@/views/dashboards/analytics/AnalyticsAward.vue'
import AnalyticsDepositWithdraw from '@/views/dashboards/analytics/AnalyticsDepositWithdraw.vue'
import AnalyticsPerformance from '@/views/dashboards/analytics/AnalyticsPerformance.vue'
import AnalyticsSalesByCountries from '@/views/dashboards/analytics/AnalyticsSalesByCountries.vue'
import AnalyticsSessionBarCharts from '@/views/dashboards/analytics/AnalyticsSessionsBarCharts.vue'
import AnalyticsTotalEarning from '@/views/dashboards/analytics/AnalyticsTotalEarning.vue'
import AnalyticsTotalProfit from '@/views/dashboards/analytics/AnalyticsTotalProfit.vue'
import AnalyticsTransactions from '@/views/dashboards/analytics/AnalyticsTransactions.vue'
import AnalyticsUserTable from '@/views/dashboards/analytics/AnalyticsUserTable.vue'
import AnalyticsWeeklyOverview from '@/views/dashboards/analytics/AnalyticsWeeklyOverview.vue'
const totalProfit = {
title: 'Total Profit',
color: 'secondary',
icon: 'ri-pie-chart-2-line',
stats: '$25.6k',
change: 42,
subtitle: 'Weekly Project',
}
const newProject = {
title: 'New Project',
color: 'primary',
icon: 'ri-file-word-2-line',
stats: '862',
change: -18,
subtitle: 'Yearly Project',
}
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="4"
>
<AnalyticsAward />
</VCol>
<VCol
cols="12"
md="8"
>
<AnalyticsTransactions />
</VCol>
<VCol
cols="12"
md="4"
sm="6"
>
<AnalyticsWeeklyOverview />
</VCol>
<VCol
cols="12"
md="4"
sm="6"
>
<AnalyticsTotalEarning />
</VCol>
<VCol
cols="12"
md="4"
>
<VRow class="match-height">
<VCol
cols="12"
sm="6"
>
<AnalyticsTotalProfit />
</VCol>
<VCol
cols="12"
sm="6"
>
<CardStatisticsVertical v-bind="totalProfit" />
</VCol>
<VCol
cols="12"
sm="6"
>
<CardStatisticsVertical v-bind="newProject" />
</VCol>
<VCol
cols="12"
sm="6"
>
<AnalyticsSessionBarCharts />
</VCol>
</VRow>
</VCol>
<VCol
cols="12"
md="4"
>
<AnalyticsPerformance />
</VCol>
<VCol
cols="12"
md="8"
>
<AnalyticsDepositWithdraw />
</VCol>
<VCol
cols="12"
md="4"
>
<AnalyticsSalesByCountries />
</VCol>
<VCol
cols="12"
md="8"
>
<AnalyticsUserTable />
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/libs/apex-chart.scss";
</style>

View File

@@ -0,0 +1,147 @@
<script setup>
import CrmActivityTimeline from '@/views/dashboards/crm/CrmActivityTimeline.vue'
import CrmDeveloperMeetup from '@/views/dashboards/crm/CrmDeveloperMeetup.vue'
import CrmMeetingSchedule from '@/views/dashboards/crm/CrmMeetingSchedule.vue'
import CrmRevenueReport from '@/views/dashboards/crm/CrmRevenueReport.vue'
import CrmSalesOverview from '@/views/dashboards/crm/CrmSalesOverview.vue'
import CrmTotalGrowthCharts from '@/views/dashboards/crm/CrmTotalGrowthCharts.vue'
import CrmTotalSales from '@/views/dashboards/crm/CrmTotalSales.vue'
import CrmTransactions from '@/views/dashboards/crm/CrmTransactions.vue'
import CrmUpgradeYourPlan from '@/views/dashboards/crm/CrmUpgradeYourPlan.vue'
import CrmWeeklySales from '@/views/dashboards/crm/CrmWeeklySales.vue'
import illustration1 from '@images/cards/illustration-1.png'
import illustration2 from '@images/cards/illustration-2.png'
const cardStatisticsWithImages = [
{
title: 'Ratings',
subtitle: 'Year of 2021',
stats: '13k',
change: 15.6,
image: illustration1,
color: 'primary',
},
{
title: 'Sessions',
subtitle: 'Last Week',
stats: '24.5k',
change: -20,
image: illustration2,
color: 'secondary',
},
]
const statistic = {
title: 'New Project',
color: 'primary',
icon: 'ri-file-word-2-line',
stats: '862',
change: -18,
subtitle: 'Yearly Project',
}
</script>
<template>
<section>
<VRow class="match-height">
<VCol
v-for="statistics in cardStatisticsWithImages"
:key="statistics.title"
class="pt-8 pt-sm-3"
cols="12"
md="3"
sm="6"
>
<CardStatisticsWithImages v-bind="statistics" />
</VCol>
<VCol
cols="12"
md="6"
>
<CrmTransactions />
</VCol>
<VCol
cols="12"
sm="6"
md="3"
>
<CrmTotalSales />
</VCol>
<VCol
cols="12"
sm="6"
md="3"
>
<CrmRevenueReport />
</VCol>
<VCol
cols="12"
md="6"
>
<CrmSalesOverview />
</VCol>
<VCol
cols="12"
md="6"
>
<CrmActivityTimeline />
</VCol>
<VCol
cols="12"
sm="8"
md="4"
>
<CrmWeeklySales />
</VCol>
<VCol
cols="12"
md="2"
sm="4"
>
<VRow class="match-height">
<VCol
cols="6"
sm="12"
>
<CrmTotalGrowthCharts />
</VCol>
<VCol
cols="6"
sm="12"
>
<CardStatisticsVertical v-bind="statistic" />
</VCol>
</VRow>
</VCol>
<VCol
cols="12"
md="4"
>
<CrmUpgradeYourPlan />
</VCol>
<VCol
cols="12"
md="4"
>
<CrmMeetingSchedule />
</VCol>
<VCol
cols="12"
md="4"
>
<CrmDeveloperMeetup />
</VCol>
</VRow>
</section>
</template>

View File

@@ -0,0 +1,147 @@
<script setup>
import ECommerceCongratulations from '@/views/dashboards/ecommerce/ECommerceCongratulations.vue'
import ECommerceInvoiceTable from '@/views/dashboards/ecommerce/ECommerceInvoiceTable.vue'
import ECommerceMeetingSchedule from '@/views/dashboards/ecommerce/ECommerceMeetingSchedule.vue'
import ECommerceNewVisitors from '@/views/dashboards/ecommerce/ECommerceNewVisitors.vue'
import ECommerceTotalProfit from '@/views/dashboards/ecommerce/ECommerceTotalProfit.vue'
import ECommerceTotalRevenue from '@/views/dashboards/ecommerce/ECommerceTotalRevenue.vue'
import ECommerceTotalSalesChart from '@/views/dashboards/ecommerce/ECommerceTotalSalesChart.vue'
import ECommerceTotalSalesRadial from '@/views/dashboards/ecommerce/ECommerceTotalSalesRadial.vue'
import ECommerceTransactions from '@/views/dashboards/ecommerce/ECommerceTransactions.vue'
import ECommerceWebsiteTransactions from '@/views/dashboards/ecommerce/ECommerceWebsiteTransactions.vue'
const statisticsVertical = [
{
title: 'Revenue',
color: 'success',
icon: 'ri-money-dollar-circle-line',
stats: '95.2k',
change: 12,
subtitle: 'Revenue Increase',
},
{
title: 'Transactions',
color: 'info',
icon: 'ri-bank-card-line',
stats: '$1.2k',
change: 38,
subtitle: 'Daily Transactions',
},
]
const statisticsVerticalTwo = [
{
title: 'Logistics',
color: 'error',
icon: 'ri-car-line',
stats: '44.10k',
change: 12,
subtitle: 'Revenue Increase',
},
{
title: 'Reports',
color: 'warning',
icon: 'ri-file-chart-line',
stats: '268',
change: -28,
subtitle: 'System Bugs',
},
]
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="8"
class="d-flex flex-column align-self-end"
>
<ECommerceCongratulations />
</VCol>
<VCol
v-for="statistic in statisticsVertical"
:key="statistic.title"
cols="12"
sm="6"
md="2"
>
<CardStatisticsVertical v-bind="statistic" />
</VCol>
<VCol
cols="12"
md="8"
>
<ECommerceTotalProfit />
</VCol>
<VCol
cols="12"
md="4"
>
<VRow class="match-height">
<VCol cols="12">
<ECommerceTotalSalesChart />
</VCol>
<VCol cols="6">
<ECommerceTotalRevenue />
</VCol>
<VCol cols="6">
<ECommerceTotalSalesRadial />
</VCol>
</VRow>
</VCol>
<VCol
cols="12"
md="4"
>
<ECommerceTransactions />
</VCol>
<VCol
cols="12"
md="4"
>
<VRow>
<VCol
v-for="statistics in statisticsVerticalTwo"
:key="statistics.title"
cols="6"
>
<CardStatisticsVertical v-bind="statistics" />
</VCol>
<VCol cols="12">
<ECommerceNewVisitors />
</VCol>
</VRow>
</VCol>
<VCol
cols="12"
md="4"
>
<ECommerceWebsiteTransactions />
</VCol>
<VCol
cols="12"
md="8"
>
<ECommerceInvoiceTable />
</VCol>
<VCol
cols="12"
md="4"
>
<ECommerceMeetingSchedule />
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/libs/apex-chart.scss";
</style>

View File

@@ -0,0 +1,156 @@
<script setup>
import * as demoCode from '@/views/demos/components/swiper/demoCodeSwiper'
</script>
<template>
<VRow>
<VCol>
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<DemoSwiperBasic />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Navigation"
:code="demoCode.navigation"
>
<DemoSwiperNavigation />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Pagination"
:code="demoCode.pagination"
>
<DemoSwiperPagination />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Progress"
:code="demoCode.progress"
>
<DemoSwiperProgress />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Multiple Slides Per View"
:code="demoCode.multipleSlidesPerView"
>
<DemoSwiperMultipleSlidesPerView />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Grid"
:code="demoCode.grid"
>
<DemoSwiperGrid />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
variant="text"
title="Centered Slides Option 1"
:code="demoCode.centeredSlidesOption1"
>
<DemoSwiperCenteredSlidesOption1 />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Centered Slides Option 2"
:code="demoCode.centeredSlidesOption2"
>
<DemoSwiperCenteredSlidesOption2 />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Fade"
:code="demoCode.fade"
>
<DemoSwiperFade />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Cube Effect"
:code="demoCode.cubeEffect"
>
<DemoSwiperCubeEffect />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Coverflow Effect"
:code="demoCode.coverflowEffect"
>
<DemoSwiperCoverflowEffect />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Autoplay"
:code="demoCode.autoplay"
>
<DemoSwiperAutoplay />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Gallery"
:code="demoCode.gallery"
>
<DemoSwiperGallery />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Lazy Loading"
:code="demoCode.lazyLoading"
>
<DemoSwiperLazyLoading />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Responsive Breakpoints"
:code="demoCode.responsiveBreakpoints"
>
<DemoSwiperResponsiveBreakpoints />
</AppCardCode>
</VCol>
<VCol>
<AppCardCode
title="Virtual Slides"
:code="demoCode.virtualSlides"
>
<DemoSwiperVirtualSlides />
</AppCardCode>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/libs/swiper.scss"
</style>

View File

@@ -0,0 +1,134 @@
<script setup>
import { useShepherd } from 'vue-shepherd'
const route = useRoute()
// 👉 Hotkey
// eslint-disable-next-line camelcase
const { ctrl_k, meta_k } = useMagicKeys()
// 👉 Tour initialization
let tour = null
// 👉 watch command palette and route change
/* eslint-disable camelcase */
watch([
ctrl_k,
meta_k,
() => route.path,
], () => {
if (tour.isActive())
tour.cancel()
})
/* eslint-enable */
onMounted(() => {
const navbar = document.querySelector('.layout-navbar')
tour = useShepherd({
useModalOverlay: true,
stepsContainer: document.querySelector('.layout-wrapper'),
modelContainer: document.querySelector('.layout-wrapper'),
defaultStepOptions: {
cancelIcon: { enabled: true },
modalOverlayOpeningPadding: 2,
modalOverlayOpeningRadius: 5,
},
})
// 👉 Tour steps
tour.addSteps([
{
id: 'welcome',
title: 'Welcome',
arrow: true,
attachTo: {
element: navbar,
on: 'bottom',
},
text: 'Welcome to our tour page, Guide users to the key features of the product.',
buttons: [
{
action: tour.cancel,
classes: 'backBtnClass',
text: 'Back',
},
{
action: tour.next,
text: 'Next',
classes: 'nextBtnClass',
},
],
},
{
id: 'notification',
title: 'Notifications',
arrow: true,
attachTo: {
element: document.querySelector('#notification-btn'),
on: 'bottom',
},
text: 'Manage your notifications and stay up-to-date with latest updates.',
buttons: [
{
label: 'Back',
text: 'Back',
action: tour.back,
classes: 'backBtnClass',
},
{
label: 'Next',
text: 'Next',
action: tour.next,
classes: 'nextBtnClass',
},
],
},
{
id: 'footer',
title: 'Footer',
arrow: true,
attachTo: {
element: document.querySelector('.layout-footer'),
on: 'bottom',
},
text: 'Footer section of the page.',
buttons: [
{
label: 'Back',
text: 'Back',
action: tour.back,
classes: 'backBtnClass',
},
{
label: 'Finish',
text: 'Finish',
action: tour.complete,
classes: 'nextBtnClass',
},
],
},
])
})
</script>
<template>
<div>
<VCard title="Tour">
<VCardText>
<VBtn
variant="outlined"
@click="() => { tour && tour.start() }"
>
Start Tour
</VBtn>
</VCardText>
</VCard>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/libs/shepherd.scss";
</style>

View File

@@ -0,0 +1,132 @@
<script setup>
import authV2ForgotPasswordIllustrationBorderedDark from '@images/pages/auth-v2-forgot-password-illustration-dark-border.png'
import authV2ForgotPasswordIllustrationDark from '@images/pages/auth-v2-forgot-password-illustration-dark.png'
import authV2ForgotPasswordIllustrationBorderedLight from '@images/pages/auth-v2-forgot-password-illustration-light-border.png'
import authV2ForgotPasswordIllustrationLight from '@images/pages/auth-v2-forgot-password-illustration-light.png'
import tree1 from '@images/misc/tree1.png'
import authV2MaskDark from '@images/pages/mask-v2-dark.png'
import authV2MaskLight from '@images/pages/mask-v2-light.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
const authThemeImg = useGenerateImageVariant(authV2ForgotPasswordIllustrationLight, authV2ForgotPasswordIllustrationDark, authV2ForgotPasswordIllustrationBorderedLight, authV2ForgotPasswordIllustrationBorderedDark, true)
const authThemeMask = useGenerateImageVariant(authV2MaskLight, authV2MaskDark)
const email = ref('')
definePage({
meta: {
layout: 'blank',
unauthenticatedOnly: true,
},
})
</script>
<template>
<RouterLink to="/">
<div class="auth-logo d-flex align-center gap-x-3">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
</RouterLink>
<VRow
class="auth-wrapper"
no-gutters
>
<VCol
md="8"
class="d-none d-md-flex position-relative"
>
<div class="d-flex align-center justify-end w-100 h-100 pa-10 pe-0">
<VImg
width="853"
height="684"
:src="authThemeImg"
class="auth-illustration"
/>
</div>
<img
class="auth-footer-mask"
height="360"
:src="authThemeMask"
>
<img
:src="tree1"
alt="tree image"
height="140"
class="auth-footer-tree"
>
</VCol>
<VCol
cols="12"
md="4"
class="auth-card-v2 d-flex align-center justify-center"
style="background-color: rgb(var(--v-theme-surface));"
>
<VCard
flat
:max-width="500"
class="mt-12 mt-sm-0 pa-4"
>
<VCardText>
<h4 class="text-h4 mb-1">
Forgot Password? 🔒
</h4>
<p class="mb-0">
Enter your email and we'll send you instructions to reset your password
</p>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- email -->
<VCol cols="12">
<VTextField
v-model="email"
autofocus
label="Email"
placeholder="johndoe@email.com"
type="email"
/>
</VCol>
<!-- Reset link -->
<VCol cols="12">
<VBtn
block
type="submit"
>
Send Reset Link
</VBtn>
</VCol>
<!-- back to login -->
<VCol cols="12">
<RouterLink
class="d-flex align-center justify-center"
:to="{ name: 'pages-authentication-login-v2' }"
>
<VIcon
icon="ri-arrow-left-s-line"
class="flip-in-rtl"
/>
<span>Back to login</span>
</RouterLink>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,180 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/autocomplete/demoCodeAutocomplete'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>
The <code> v-autocomplete </code> component offers simple and flexible type-ahead functionality. This is useful when searching large sets of data or even dynamically fetching information from an API.
</p>
<DemoAutocompleteBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Density -->
<AppCardCode
title="Density"
:code="demoCode.density"
>
<p>
You can use <code> density </code> prop to adjusts vertical spacing within the component. Available options are: <code>default</code>, <code>comfortable</code>, and <code>compact</code>.
</p>
<DemoAutocompleteDensity />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="12"
>
<!-- 👉 Variant -->
<AppCardCode
title="Variant"
:code="demoCode.variant"
>
<p>Use <code>solo</code>, <code>outlined</code>, <code>underlined</code>, <code>filled</code> and <code>plain</code> options of <code>variant</code> prop to change the look of Autocomplete. </p>
<DemoAutocompleteVariant />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Multiple Select -->
<AppCardCode
title="Multiple"
:code="demoCode.multiple"
>
<p>Use <code>multiple</code> prop to select multiple. Accepts array for value</p>
<DemoAutocompleteMultiple />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Clearable input -->
<AppCardCode
title="Clearable"
:code="demoCode.clearable"
>
<p>Use <code>clearable</code> prop to add input clear functionality.</p>
<DemoAutocompleteClearable />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Chips -->
<AppCardCode
title="Chips"
:code="demoCode.chips"
>
<p>Use <code> chips </code> prop to use chips in select.</p>
<DemoAutocompleteChips />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Custom Filter -->
<AppCardCode
title="Custom-Filter"
:code="demoCode.customFilter"
>
<p>The <code> custom-filter </code> prop can be used to filter each individual item with custom logic.In example we will filter state based on their name and abbreviations </p>
<DemoAutocompleteCustomFilter />
</AppCardCode>
</VCol>
<!--
<VCol
cols="12"
md="6"
>
// 👉 With slots
<AppCardCode
title="Slots"
:code="demoCode.slots"
>
<p>With the power of slots, you can customize the visual output of the select. In this example we add a profile picture for both the chips and list items using their props. </p>
<DemoAutocompleteSlots />
</AppCardCode>
</VCol>
-->
<VCol
cols="12"
md="6"
>
<!-- 👉 Asynchronous Items -->
<AppCardCode
title="Async items"
:code="demoCode.asyncItems"
>
<p>Sometimes you need to load data externally based upon a search query. </p>
<DemoAutocompleteAsyncItems />
</AppCardCode>
</VCol>
<!--
<VCol
cols="12"
md="6"
>
// 👉 State Selector
<AppCardCode
title="State Selector"
:code="demoCode.stateSelector"
>
<p>Using a combination of v-autocomplete slots and transitions, you can create a stylish toggle able autocomplete field such as below state selector.</p>
<DemoAutocompleteStateSelector />
</AppCardCode>
</VCol>
-->
<VCol
cols="12"
md="6"
>
<!-- 👉 Validation -->
<AppCardCode
title="validation"
:code="demoCode.validation"
>
<p>Use <code>rules</code> prop to validate autocomplete. Accepts a mixed array of types function, boolean and string. Functions pass an input value as an argument and must return either true / false or a string containing an error message.</p>
<DemoAutocompleteValidation />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,142 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/checkbox/demoCodeCheckbox'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p><code>v-checkbox</code> in its simplest form provides a toggle between 2 values.</p>
<DemoCheckboxBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Density -->
<AppCardCode
title="Density"
:code="demoCode.density"
>
<p>Use <code>density</code> prop to reduces the input height. Available options are: <code>default</code>, <code>comfortable</code>, and <code>compact</code>.</p>
<DemoCheckboxDensity />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Colors -->
<AppCardCode
title="Colors"
:code="demoCode.colors"
>
<p>Checkboxes can be colored by using any of the builtin colors and contextual names using the <code>color</code> prop.</p>
<DemoCheckboxColors />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Model as array -->
<AppCardCode
title="Model as array"
:code="demoCode.modelAsArray"
>
<p>Multiple <code>v-checkbox</code>'s can share the same <code>v-model</code> by using an array.</p>
<DemoCheckboxModelAsArray />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Icon -->
<AppCardCode
title="Icon"
:code="demoCode.icon"
>
<p>Use <code>false-icon</code> and <code>true-icon</code> prop to change the icon on the checkbox.</p>
<DemoCheckboxIcon />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Checkbox Value -->
<AppCardCode
title="Checkbox Value"
:code="demoCode.checkboxValue"
>
<p>Use <code>false-value</code> and <code>true-value</code> prop to sets value for truthy and falsy state</p>
<DemoCheckboxCheckboxValue />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 States -->
<AppCardCode
title="States"
:code="demoCode.states"
>
<p><code>v-checkbox</code> can have different states such as <code>default</code>, <code>disabled</code>, and <code>indeterminate</code>.</p>
<DemoCheckboxStates />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Label Slot -->
<AppCardCode
title="Label Slot"
:code="demoCode.labelSlot"
>
<p>Checkbox labels can be defined in <code>label</code> slot - that will allow to use HTML content.</p>
<DemoCheckboxLabelSlot />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Inline text-field -->
<AppCardCode
title="Inline text-field"
:code="demoCode.inlineTextField"
>
<p>You can place <code>v-checkbox</code> in line with other components such as <code>v-text-field</code>.</p>
<DemoCheckboxInlineTextField />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,102 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/combobox/demoCodeCombobox'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>With Combobox, you can allow a user to create new values that may not be present in a provided items list.</p>
<DemoComboboxBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Density -->
<AppCardCode
title="Density"
:code="demoCode.density"
>
<p>
You can use <code>Density</code> prop to reduce combobox height and lower max height of list items. Available options are: <code>default</code>, <code>comfortable</code>, and <code>compact</code>.
</p>
<DemoComboboxDensity />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Variant -->
<AppCardCode
title="Variant"
:code="demoCode.variant"
>
<p>Use <code>solo</code>, <code>outlined</code>, <code>underlined</code>, <code>filled</code> and <code>plain</code> options of <code>variant</code> prop to change the look of combobox. </p>
<DemoComboboxVariant />
</AppCardCode>
</VCol>
<!--
<VCol
cols="12"
md="6"
>
// 👉 Multiple
<AppCardCode
title="Multiple"
:code="demoCode.multiple"
>
<p>Previously known as tags - user is allowed to enter more than 1 value</p>
<DemoComboboxMultiple />
</AppCardCode>
</VCol>
-->
<VCol
cols="12"
md="6"
>
<!-- 👉 No data with chips -->
<AppCardCode
title="No data with chips"
:code="demoCode.noDataWithChips"
>
<p>Previously known as tags - user is allowed to enter more than 1 value</p>
<DemoComboboxNoDataWithChips />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Clearable -->
<AppCardCode
title="Clearable"
:code="demoCode.clearable"
>
<p>Use <code>clearable</code> prop to clear combobox.</p>
<DemoComboboxClearable />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,85 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/custom-input/demoCodeCustomInput'
</script>
<template>
<VRow>
<!-- 👉 Custom Radios -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Custom Radios"
:code="demoCode.customRadios"
>
<DemoCustomInputCustomRadios />
</AppCardCode>
</VCol>
<!-- 👉 Custom Checkboxes -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Custom Checkboxes"
:code="demoCode.customCheckboxes"
>
<DemoCustomInputCustomCheckboxes />
</AppCardCode>
</VCol>
<!-- 👉 Custom Radios With Icon -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Custom Radios With Icon"
:code="demoCode.customRadiosWithIcon"
>
<DemoCustomInputCustomRadiosWithIcon />
</AppCardCode>
</VCol>
<!-- 👉 Custom Checkboxes with icon -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Custom Checkboxes With Icon"
:code="demoCode.customCheckboxesWithIcon"
>
<DemoCustomInputCustomCheckboxesWithIcon />
</AppCardCode>
</VCol>
<!-- 👉 Custom Radios with image -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Custom Radios With Image"
:code="demoCode.customRadiosWithImage"
>
<DemoCustomInputCustomRadiosWithImage />
</AppCardCode>
</VCol>
<!-- 👉 Custom Checkboxes with Image -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Custom Checkboxes With Image"
:code="demoCode.customCheckboxesWithImage"
>
<DemoCustomInputCustomCheckboxesWithImage />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,111 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/date-time-picker/demoCodeDateTimePicker'
</script>
<template>
<VRow>
<!-- 👉 Basic -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<DemoDateTimePickerBasic />
</AppCardCode>
</VCol>
<!-- 👉 Time Picker -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Time Picker"
:code="demoCode.timePicker"
>
<DemoDateTimePickerTimePicker />
</AppCardCode>
</VCol>
<!-- 👉 Date & TIme -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Date and Time"
:code="demoCode.dateAndTime"
>
<DemoDateTimePickerDateAndTime />
</AppCardCode>
</VCol>
<!-- 👉 Multiple Dates -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Multiple Dates"
:code="demoCode.multipleDates"
>
<DemoDateTimePickerMultipleDates />
</AppCardCode>
</VCol>
<!-- 👉 Range -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Range"
:code="demoCode.range"
>
<DemoDateTimePickerRange />
</AppCardCode>
</VCol>
<!-- 👉 Human Friendly -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Human Friendly"
:code="demoCode.humanFriendly"
>
<DemoDateTimePickerHumanFriendly />
</AppCardCode>
</VCol>
<!-- 👉 Disabled Range -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Disabled Range"
:code="demoCode.disabledRange"
>
<DemoDateTimePickerDisabledRange />
</AppCardCode>
</VCol>
<!-- 👉 Inline -->
<VCol
cols="12"
md="6"
>
<AppCardCode
title="Inline"
:code="demoCode.inline"
>
<DemoDateTimePickerInline />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,26 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/editor/demoCodeEditor'
</script>
<template>
<VRow>
<!-- 👉 Basic Editor -->
<VCol cols="12">
<AppCardCode
title="Basic Editor"
:code="demoCode.basicEditor"
>
<DemoEditorBasicEditor />
</AppCardCode>
</VCol>
<VCol cols="12">
<AppCardCode
title="Custom Editor"
:code="demoCode.customEditor"
>
<DemoEditorCustomEditor />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,188 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/file-input/demoCodeFileInput'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>The <code>v-file-input</code> component is used to selecting files.</p>
<DemoFileInputBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Density -->
<AppCardCode
title="Density"
:code="demoCode.density"
>
<p>You can reduces the file input height with <code>density</code> prop. Available options are: <code>default</code>, <code>comfortable</code>, and <code>compact</code>.</p>
<DemoFileInputDensity />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Variant -->
<AppCardCode
title="Variant"
:code="demoCode.variant"
>
<p>use <code>solo</code>, <code>filled</code>, <code>outlined</code>, <code>plain</code> and <code>underlined</code> option of <code>variant</code> prop to change the look of file input.</p>
<DemoFileInputVariant />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Accept -->
<AppCardCode
title="Accept"
:code="demoCode.accept"
>
<p><code>v-file-input</code> component can accept only specific media formats/file types if you want.</p>
<DemoFileInputAccept />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Chips -->
<AppCardCode
title="Chips"
:code="demoCode.chips"
>
<p>Use <code>chip</code> prop to display the selected file as a chip.</p>
<DemoFileInputChips />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Counter -->
<AppCardCode
title="Counter"
:code="demoCode.counter"
>
<p>When using the <code>show-size</code> property along with <code>counter</code>, the total number of files and size will be displayed under the input.</p>
<DemoFileInputCounter />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Multiple -->
<AppCardCode
title="Multiple"
:code="demoCode.multiple"
>
<p>
The <code>v-file-input</code> can contain multiple files at the same time when using the <code>multiple</code> prop.
</p>
<DemoFileInputMultiple />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Prepend icon -->
<AppCardCode
title="Prepend icon"
:code="demoCode.prependIcon"
>
<p>
The <code>v-file-input</code> has a default <code>prepend-icon</code> that can be set on the component or adjusted globally.
</p>
<DemoFileInputPrependIcon />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Show size -->
<AppCardCode
title="Show size"
:code="demoCode.showSize"
>
<p>The displayed size of the selected file(s) can be configured with the <code>show-size</code> property.</p>
<DemoFileInputShowSize />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Validation -->
<AppCardCode
title="Validation"
:code="demoCode.validation"
>
<p>You can use the <code>rules</code> prop to create your own custom validation parameters.</p>
<DemoFileInputValidation />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Selection slot -->
<AppCardCode
title="Selection slot"
:code="demoCode.selectionSlot"
>
<p>Using the <code>selection</code> slot, you can customize the appearance of your input selections.</p>
<DemoFileInputSelectionSlot />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Loading -->
<AppCardCode
title="Loading"
:code="demoCode.loading"
>
<p>Use <code>loading</code> prop to displays linear progress bar.</p>
<DemoFileInputLoading />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,120 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-layout/demoCodeFormLayout'
</script>
<template>
<div>
<VRow>
<VCol
cols="12"
md="6"
>
<!-- 👉 Horizontal Form -->
<AppCardCode
title="Horizontal Form"
:code="demoCode.horizontalForm"
>
<DemoFormLayoutHorizontalForm />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Horizontal Form with Icons -->
<AppCardCode
title="Horizontal Form with Icons"
:code="demoCode.horizontalFormWithIcons"
>
<DemoFormLayoutHorizontalFormWithIcons />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Vertical Form -->
<AppCardCode
title="Vertical Form"
:code="demoCode.verticalForm"
>
<DemoFormLayoutVerticalForm />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Vertical Form with Icons -->
<AppCardCode
title="Vertical Form with Icons"
:code="demoCode.verticalFormWithIcons"
>
<DemoFormLayoutVerticalFormWithIcons />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Multiple Column -->
<AppCardCode
title="Multiple Column"
:code="demoCode.multipleColumn"
>
<DemoFormLayoutMultipleColumn />
</AppCardCode>
</VCol>
</VRow>
<VRow class="match-height my-3">
<VCol
cols="12"
md="6"
>
<!-- 👉 Form Hint -->
<AppCardCode
title="Form Hint"
:code="demoCode.formHint"
>
<DemoFormLayoutFormHint />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Form Validation -->
<AppCardCode
title="Form Validation"
:code="demoCode.formValidation"
>
<DemoFormLayoutFormValidation />
</AppCardCode>
</VCol>
</VRow>
<VRow>
<VCol cols="12">
<!-- 👉 Form with Tabs -->
<AppCardCode
title="Form with Tabs"
no-padding
:code="demoCode.formWithTabs"
>
<DemoFormLayoutFormWithTabs />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Collapsible Section -->
<h4 class="text-h6 font-weight-medium mb-5">
Collapsible Section
</h4>
<DemoFormLayoutCollapsible />
</VCol>
<VCol cols="12">
<!-- 👉 Sticky Section -->
<h4 class="text-h6 font-weight-medium mb-5">
Sticky Section
</h4>
<DemoFormLayoutFormSticky />
</VCol>
</VRow>
</div>
</template>

View File

@@ -0,0 +1,39 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-validation/demoCodeFormValidation'
</script>
<template>
<VRow>
<VCol cols="12">
<!-- 👉 Simple Form Validation -->
<AppCardCode
title="Simple Form Validation"
:code="demoCode.simpleFormValidation"
>
<p>Use <code>Rules</code> prop to validate the input.</p>
<DemoFormValidationSimpleFormValidation />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Validating Multiple Rules -->
<AppCardCode
title="Validating Multiple Rules"
:code="demoCode.validatingMultipleRules"
>
<DemoFormValidationValidatingMultipleRules />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Validation Types -->
<AppCardCode
title="Validation Types"
:code="demoCode.validationTypes"
>
<DemoFormValidationValidationTypes />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,43 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-wizard/form-wizard-icons/demoCodeFormWizardIcons'
import DemoFormWizardIconsBasic from '@/views/demos/forms/form-wizard/form-wizard-icons/DemoFormWizardIconsBasic.vue'
import DemoFormWizardIconsValidation from '@/views/demos/forms/form-wizard/form-wizard-icons/DemoFormWizardIconsValidation.vue'
import DemoFormWizardIconsVertical from '@/views/demos/forms/form-wizard/form-wizard-icons/DemoFormWizardIconsVertical.vue'
</script>
<template>
<VRow>
<!-- 👉 Basic -->
<VCol cols="12">
<AppCardCode
variant="outlined"
title="Basic"
:code="demoCode.basic"
>
<DemoFormWizardIconsBasic />
</AppCardCode>
</VCol>
<!-- 👉 Validation -->
<VCol cols="12">
<AppCardCode
variant="outlined"
title="Validation"
:code="demoCode.validation"
>
<DemoFormWizardIconsValidation />
</AppCardCode>
</VCol>
<!-- 👉 Vertical -->
<VCol cols="12">
<AppCardCode
variant="outlined"
title="Vertical"
:code="demoCode.vertical"
>
<DemoFormWizardIconsVertical />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,43 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-wizard/form-wizard-numbered/demoCodeFormWizardNumbered'
import DemoFormWizardNumberedBasic from '@/views/demos/forms/form-wizard/form-wizard-numbered/DemoFormWizardNumberedBasic.vue'
import DemoFormWizardNumberedValidation from '@/views/demos/forms/form-wizard/form-wizard-numbered/DemoFormWizardNumberedValidation.vue'
import DemoFormWizardNumberedVertical from '@/views/demos/forms/form-wizard/form-wizard-numbered/DemoFormWizardNumberedVertical.vue'
</script>
<template>
<VRow>
<!-- 👉 Basic -->
<VCol cols="12">
<AppCardCode
variant="outlined"
title="Basic"
:code="demoCode.basic"
>
<DemoFormWizardNumberedBasic />
</AppCardCode>
</VCol>
<!-- 👉 Validation -->
<VCol cols="12">
<AppCardCode
variant="outlined"
title="Validation"
:code="demoCode.validation"
>
<DemoFormWizardNumberedValidation />
</AppCardCode>
</VCol>
<!-- 👉 Vertical -->
<VCol cols="12">
<AppCardCode
variant="outlined"
title="Vertical"
:code="demoCode.vertical"
>
<DemoFormWizardNumberedVertical />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,112 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/radio/demoCodeRadio'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>The <code>v-radio</code> component is a simple radio button.</p>
<DemoRadioBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Colors -->
<AppCardCode
title="Colors"
:code="demoCode.colors"
>
<p>Radios can be colored by using any of the built-in colors and contextual names using the <code>color</code> prop.</p>
<DemoRadioColors />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Inline -->
<AppCardCode
title="Inline"
:code="demoCode.inline"
>
<p>Use <code>inline</code> prop to displays radio buttons in row.</p>
<DemoRadioInline />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Density -->
<AppCardCode
title="Density"
:code="demoCode.density"
>
<p>Use <code>density</code> prop to adjusts the spacing within the component. Available options are: <code>default</code>, <code>comfortable</code>, and <code>compact</code>.</p>
<DemoRadioDensity />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Label Slot -->
<AppCardCode
title="Label Slot"
:code="demoCode.labelSlot"
>
<p>Radio Group labels can be defined in <code>label</code> slot - that will allow to use HTML content.</p>
<DemoRadioLabelSlot />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Icon -->
<AppCardCode
title="Icon"
:code="demoCode.icon"
>
<p>Use <code>false-icon</code> and <code>true-icon</code> prop to set icon on inactive and active state.</p>
<DemoRadioIcon />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Validation -->
<AppCardCode
title="Validation"
:code="demoCode.validation"
>
<p>Use <code>rules</code> prop to validate a radio. Accepts a mixed array of types <code>function</code>, <code>boolean</code> and <code>string</code>. Functions pass an input value as an argument and must return either <code>true</code> / <code>false</code> or a string containing an error message.</p>
<DemoRadioValidation />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,99 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/range-slider/demoCodeRangeSlider'
</script>
<template>
<VRow>
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>The <code>v-slider</code> component is a better visualization of the number input.</p>
<DemoRangeSliderBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Disabled -->
<AppCardCode
title="Disabled"
:code="demoCode.disabled"
>
<p>You cannot interact with <code>disabled</code> sliders.</p>
<DemoRangeSliderDisabled />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Color -->
<AppCardCode
title="Color"
:code="demoCode.color"
>
<p>Use <code>color</code> prop to the sets the slider color. <code>track-color</code> prop to sets the color of slider's unfilled track.</p>
<DemoRangeSliderColor />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Step -->
<AppCardCode
title="Step"
:code="demoCode.step"
>
<p><code>v-range-slider</code> can have steps other than 1. This can be helpful for some applications where you need to adjust values with more or less accuracy.</p>
<DemoRangeSliderStep />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Thumb label -->
<AppCardCode
title="Thumb label"
:code="demoCode.thumbLabel"
>
<p>
Using the <code>tick-labels</code> prop along with the <code>thumb-label</code> slot, you can create a very customized solution.
</p>
<DemoRangeSliderThumbLabel />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Vertical -->
<AppCardCode
title="Vertical"
:code="demoCode.vertical"
>
<p>You can use the <code>vertical</code> prop to switch sliders to a vertical orientation. If you need to change the height of the slider, use css.</p>
<DemoRangeSliderVertical />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,157 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/rating/demoCodeRating'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>The <code>v-rating</code> component provides a simple interface for gathering user feedback.</p>
<DemoRatingBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Density -->
<AppCardCode
title="Density"
:code="demoCode.density"
>
<p>Control the space occupied by <code>v-rating</code> items using the <code>density</code> prop.</p>
<DemoRatingDensity />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Colors -->
<AppCardCode
title="Colors"
:code="demoCode.colors"
>
<p>The <code>v-rating</code> component can be colored as you want, you can set both selected and not selected colors.</p>
<DemoRatingColors />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Size -->
<AppCardCode
title="Size"
:code="demoCode.size"
>
<p>Utilize the same sizing classes available in <code>v-icon</code> or provide your own with the <code>size</code> prop.</p>
<DemoRatingSize />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Length -->
<AppCardCode
title="Length"
:code="demoCode.length"
>
<p>Change the number of items by modifying the the <code>length</code> prop.</p>
<DemoRatingLength />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Clearable -->
<AppCardCode
title="Clearable"
:code="demoCode.clearable"
>
<p>Use <code>clearable</code> prop to allows for the component to be cleared. Triggers when the icon containing the current value is clicked.</p>
<DemoRatingClearable />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Readonly -->
<AppCardCode
title="Readonly"
:code="demoCode.readonly"
>
<p>For ratings that are not meant to be changed you can use <code>readonly</code> prop.</p>
<DemoRatingReadonly />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Hover -->
<AppCardCode
title="Hover"
:code="demoCode.hover"
>
<p>Provides visual feedback when hovering over icons</p>
<DemoRatingHover />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Incremented -->
<AppCardCode
title="Incremented"
:code="demoCode.incremented"
>
<p>The <code>half-increments</code> prop increases the granularity of the ratings, allow for .5 values as well.</p>
<DemoRatingIncremented />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Item slot -->
<AppCardCode
title="Item slot"
:code="demoCode.itemSlot"
>
<p>Slots enable advanced customization possibilities and provide you with more freedom in how you display the rating.</p>
<DemoRatingItemSlot />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,141 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/select/demoCodeSelect'
</script>
<template>
<VRow>
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>Select fields components are used for collecting user provided information from a list of options.</p>
<DemoSelectBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Density -->
<AppCardCode
title="Density"
:code="demoCode.density"
>
<p>You can use <code>density</code> prop to reduce the field height and lower max height of list items.</p>
<DemoSelectDensity />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Variant -->
<AppCardCode
title="Variant"
:code="demoCode.variant"
>
<p>
Use <code>filled</code>, <code>outlined</code>, <code>solo</code>, <code>underlined</code> and <code>plain</code> options of <code>variant</code> prop to change appearance of select.
</p>
<DemoSelectVariant />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Custom text and value -->
<AppCardCode
title="Custom text and value"
:code="demoCode.customTextAndValue"
>
<p>You can specify the specific properties within your items array that correspond to the title and value fields. In this example we also use the return-object prop which will return the entire object of the selected item on selection.</p>
<DemoSelectCustomTextAndValue />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Icons -->
<AppCardCode
title="Icons"
:code="demoCode.icons"
>
<p>Use a custom <code>prepend</code> or <code>appended</code> icon.</p>
<DemoSelectIcons />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Chips -->
<AppCardCode
title="Chips"
:code="demoCode.chips"
>
<p>Use <code>chips</code> prop to make selected option as chip.</p>
<DemoSelectChips />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Menu Props -->
<AppCardCode
title="Menu Props"
:code="demoCode.menuProps"
>
<p>Custom props can be passed directly to <code>v-menu</code> using <code>menuProps</code> prop.</p>
<DemoSelectMenuProps />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Multiple -->
<AppCardCode
title="Multiple"
:code="demoCode.multiple"
>
<p>Use <code>multiple</code> prop to select multiple option.</p>
<DemoSelectMultiple />
</AppCardCode>
</VCol>
<!--
<VCol
cols="12"
md="6"
>
// 👉 Selection slot
<AppCardCode
title="Selection slot"
:code="demoCode.selectionSlot"
>
<p>The <code>selection</code> slot can be used to customize the way selected values are shown in the input.</p>
<DemoSelectSelectionSlot />
</AppCardCode>
</VCol>
-->
</VRow>
</template>

View File

@@ -0,0 +1,204 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/slider/demoCodeSlider'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>The <code>v-slider</code> component is a better visualization of the number input.</p>
<DemoSliderBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Disabled and Readonly -->
<AppCardCode
title="Disabled and Readonly"
:code="demoCode.disabledAndReadonly"
>
<p>You cannot interact with <code>disabled</code> and <code>readonly</code> sliders.</p>
<DemoSliderDisabledAndReadonly />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Colors -->
<AppCardCode
title="Colors"
:code="demoCode.colors"
>
<p>You can set the colors of the slider using the props <code>color</code>, <code>track-color</code> and <code>thumb-color</code>.</p>
<DemoSliderColors />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Icons -->
<AppCardCode
title="Icons"
:code="demoCode.icons"
>
<p>You can add icons to the slider with the <code>append-icon</code> and <code>prepend-icon</code> props.</p>
<DemoSliderIcons />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Step -->
<AppCardCode
title="Step"
:code="demoCode.step"
>
<p>Using the <code>step</code> prop you can control the precision of the slider, and how much it should move each step.</p>
<DemoSliderStep />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Validation -->
<AppCardCode
title="Validation"
:code="demoCode.validation"
>
<p>Vuetify includes simple validation through the <code>rules</code> prop.</p>
<DemoSliderValidation />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Min and Max -->
<AppCardCode
title="Min and Max"
:code="demoCode.minAndMax"
>
<p>You can set <code>min</code> and <code>max</code> values of sliders.</p>
<DemoSliderMinAndMax />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Size -->
<AppCardCode
title="Size"
:code="demoCode.size"
>
<p>Use <code>thumb-size</code>, <code>tick-size</code>, and <code>track-size</code> prop to increase and decrease the size of thumb, tick and track. </p>
<DemoSliderSize />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Thumb -->
<AppCardCode
title="Thumb"
:code="demoCode.thumb"
>
<p>You can display a thumb label while sliding or always with the <code>thumb-label</code> prop.</p>
<DemoSliderThumb />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Ticks -->
<AppCardCode
title="Ticks"
:code="demoCode.ticks"
>
<p>Tick marks represent predetermined values to which the user can move the slider.</p>
<DemoSliderTicks />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Vertical -->
<AppCardCode
title="Vertical"
:code="demoCode.vertical"
>
<p>
You can use the <code>vertical</code> prop to switch sliders to a vertical orientation.
</p>
<DemoSliderVertical />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Append text field -->
<AppCardCode
title="Append text field"
:code="demoCode.appendTextField"
>
<p>Sliders can be combined with other components in its <code>append</code> slot, such as <code>v-text-field</code>, to add additional functionality to the component.</p>
<DemoSliderAppendTextField />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Append and prepend -->
<AppCardCode
title="Append and prepend"
:code="demoCode.appendAndPrepend"
>
<p>Use slots such as <code>append</code> and <code>prepend</code> to easily customize the <code>v-slider</code> to fit any situation.</p>
<DemoSliderAppendAndPrepend />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,114 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/switch/demoCodeSwitch'
</script>
<template>
<VRow>
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>A <code>v-switch</code> in its simplest form provides a toggle between 2 values.</p>
<DemoSwitchBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Inset -->
<AppCardCode
title="Inset"
:code="demoCode.inset"
>
<p>To change the default <code>inset</code> switch, simply modify the inset prop to a <code>false</code> value.</p>
<DemoSwitchInset />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Colors -->
<AppCardCode
title="Colors"
:code="demoCode.colors"
>
<p>Switches can be colored by using any of the builtin colors and contextual names using the <code>color</code> prop.</p>
<DemoSwitchColors />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Model as array -->
<AppCardCode
title="Model as array"
:code="demoCode.modelAsArray"
>
<p>Multiple <code>v-switch</code>'s can share the same <code>v-model</code> by using an array.</p>
<DemoSwitchModelAsArray />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Label slot -->
<AppCardCode
title="Label slot"
:code="demoCode.labelSlot"
>
<p>Switch labels can be defined in <code>label</code> slot - that will allow to use HTML content.</p>
<DemoSwitchLabelSlot />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 True and False Value -->
<AppCardCode
title="True and False Value"
:code="demoCode.trueAndFalseValue"
>
<p>
Use <code>false-value</code> and <code>true-value</code> prop to sets value for truthy and falsy state
</p>
<DemoSwitchTrueAndFalseValue />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 States -->
<AppCardCode
title="States"
:code="demoCode.states"
>
<p><code>v-switch</code> can have different states such as <code>default</code>, <code>disabled</code>, and <code>loading</code>.</p>
<DemoSwitchStates />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,175 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/textarea/demoCodeTextarea'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>
v-textarea in its simplest form is a multi-line text-field, useful for larger amounts of text.
</p>
<DemoTextareaBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Auto Grow -->
<AppCardCode
title="Auto Grow"
:code="demoCode.autoGrow"
>
<p>When using the <code>auto-grow</code> prop, textarea's will automatically increase in size when the contained text exceeds its size.</p>
<DemoTextareaAutoGrow />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Variant -->
<AppCardCode
title="Variant"
:code="demoCode.variant"
>
<p>Use <code>filled</code>, <code>plain</code>, <code>outlined</code>, <code>solo</code> and <code>underlined</code> option of <code>variant</code> prop to change the look of file input.</p>
<DemoTextareaVariant />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 States -->
<AppCardCode
title="States"
:code="demoCode.states"
>
<p>Use <code>disabled</code> and <code>readonly</code> prop to change the state of textarea.</p>
<DemoTextareaStates />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Browser autocomplete -->
<AppCardCode
title="Browser autocomplete"
:code="demoCode.browserAutocomplete"
>
<p>
The <code>autocomplete</code> prop gives you the option to enable the browser to predict user input.
</p>
<DemoTextareaBrowserAutocomplete />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Clearable -->
<AppCardCode
title="Clearable"
:code="demoCode.clearable"
>
<p>You can clear the text from a <code>v-textarea</code> by using the <code>clearable</code> prop, and customize the icon used with the <code>clearable-icon</code> prop.</p>
<DemoTextareaClearable />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Counter -->
<AppCardCode
title="Counter"
:code="demoCode.counter"
>
<p>
The <code>counter</code> prop informs the user of a character limit for the <code>v-textarea</code>.
</p>
<DemoTextareaCounter />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Icons -->
<AppCardCode
title="Icons"
:code="demoCode.icons"
>
<p>The <code>append-icon</code>, <code>prepend-icon</code>, <code>append-inner-icon</code> and <code>prepend-inner-icon</code> props help add context to v-textarea.</p>
<DemoTextareaIcons />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Rows -->
<AppCardCode
title="Rows"
:code="demoCode.rows"
>
<p>The <code>rows</code> prop allows you to define how many rows the textarea has, when combined with the <code>row-height</code> prop you can further customize your rows by defining their height.</p>
<DemoTextareaRows />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 No resize -->
<AppCardCode
title="No resize"
:code="demoCode.noResize"
>
<p><code>v-textarea</code>'s have the option to remain the same size regardless of their content's size, using the <code>no-resize</code> prop.</p>
<DemoTextareaNoResize />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Validation -->
<AppCardCode
title="Validation"
:code="demoCode.validation"
>
<p>Use <code>rules</code> prop to validate the textarea.</p>
<DemoTextareaValidation />
</AppCardCode>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,233 @@
<script setup>
import * as demoCode from '@/views/demos/forms/form-elements/textfield/demoCodeTextfield'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
>
<!-- 👉 Basic -->
<AppCardCode
title="Basic"
:code="demoCode.basic"
>
<p>Text fields components are used for collecting user provided information.</p>
<DemoTextfieldBasic />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Density -->
<AppCardCode
title="Density"
:code="demoCode.density"
>
<p>The <code>density</code> prop decreases the height of the text field based upon 1 of 3 levels of density; <code>default</code>, <code>comfortable</code>, and <code>compact</code>.</p>
<DemoTextfieldDensity />
</AppCardCode>
</VCol>
<VCol cols="12">
<!-- 👉 Variant -->
<AppCardCode
title="Variant"
:code="demoCode.variant"
>
<p>Use <code>solo</code>, <code>filled</code>, <code>outlined</code>, <code>plain</code> and <code>underlined</code> option of <code>variant</code> prop to change the look of the textfield. </p>
<DemoTextfieldVariant />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 State -->
<AppCardCode
title="State"
:code="demoCode.state"
>
<p>Text fields can be disabled or readonly.</p>
<DemoTextfieldState />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Counter -->
<AppCardCode
title="Counter"
:code="demoCode.counter"
>
<p>Use a <code>counter</code> prop to inform a user of the character limit.</p>
<DemoTextfieldCounter />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Clearable -->
<AppCardCode
title="Clearable"
:code="demoCode.clearable"
>
<p>When clearable, you can customize the clear icon with clear-icon.</p>
<DemoTextfieldClearable />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Custom Colors -->
<AppCardCode
title="Custom Colors"
:code="demoCode.customColors"
>
<p>Use <code>color</code> prop to change the input border color.</p>
<DemoTextfieldCustomColors />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Icons -->
<AppCardCode
title="Icons"
:code="demoCode.icons"
>
<p>You can add icons to the text field with <code>prepend-icon</code>, <code>append-icon</code> and <code>append-inner-icon</code> and <code>prepend-inner-icon</code> props.</p>
<DemoTextfieldIcons />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Prefixes and suffixes -->
<AppCardCode
title="Prefixes and suffixes"
:code="demoCode.prefixesAndSuffixes"
>
<p>The <code>prefix</code> and <code>suffix</code> properties allows you to prepend and append inline non-modifiable text next to the text field.</p>
<DemoTextfieldPrefixesAndSuffixes />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Single line -->
<AppCardCode
title="Single line"
:code="demoCode.singleLine"
>
<p><code>single-line</code> text fields do not float their label on focus or with data.</p>
<DemoTextfieldSingleLine />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Validation -->
<AppCardCode
title="Validation"
:code="demoCode.validation"
>
<p>Vuetify includes simple validation through the <code>rules</code> prop.</p>
<DemoTextfieldValidation />
</AppCardCode>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Icon events -->
<AppCardCode
title="Icon events"
:code="demoCode.iconEvents"
>
<p><code>click:prepend</code>, <code>click:append</code>, <code>click:append-inner</code>, and <code>click:clear</code> will be emitted when you click on the respective icon</p>
<DemoTextfieldIconEvents />
</AppCardCode>
</VCol>
<!--
<VCol
cols="12"
md="6"
>
// 👉 Label Slot
<AppCardCode
title="Label Slot"
:code="demoCode.labelSlot"
>
<p>Text field label can be defined in <code>label</code> slot - that will allow to use HTML content.</p>
<DemoTextfieldLabelSlot />
</AppCardCode>
</VCol>
-->
<VCol
cols="12"
md="6"
>
<!-- 👉 Password input -->
<AppCardCode
title="Password input"
:code="demoCode.passwordInput"
>
<p>Using the HTML input <code>type</code> password can be used with an appended icon and callback to control the visibility.</p>
<DemoTextfieldPasswordInput />
</AppCardCode>
</VCol>
<!--
<VCol
cols="12"
md="6"
>
// 👉 Icon slots
<AppCardCode
title="Icon slots"
:code="demoCode.iconSlots"
>
<p>Instead of using <code>prepend</code>/<code>append</code>/<code>append-inner</code> icons you can use slots to extend input's functionality.</p>
<DemoTextfieldIconSlots />
</AppCardCode>
</VCol>
-->
</VRow>
</template>

View File

@@ -0,0 +1,153 @@
<script setup>
import Footer from '@/views/front-pages/front-page-footer.vue'
import Navbar from '@/views/front-pages/front-page-navbar.vue'
import AddressContent from '@/views/wizard-examples/checkout/Address.vue'
import CartContent from '@/views/wizard-examples/checkout/Cart.vue'
import ConfirmationContent from '@/views/wizard-examples/checkout/Confirmation.vue'
import PaymentContent from '@/views/wizard-examples/checkout/Payment.vue'
import googleHome from '@images/pages/google-home.png'
import iphone11 from '@images/pages/iphone-11.png'
import customAddress from '@images/svg/address.svg'
import customCart from '@images/svg/cart.svg'
import customPayment from '@images/svg/payment.svg'
import customTrending from '@images/svg/trending.svg'
definePage({ meta: { layout: 'blank' } })
const checkoutSteps = [
{
title: 'Cart',
icon: customCart,
},
{
title: 'Address',
icon: customAddress,
},
{
title: 'Payment',
icon: customPayment,
},
{
title: 'Confirmation',
icon: customTrending,
},
]
const checkoutData = ref({
cartItems: [
{
id: 1,
name: 'Google - Google Home - White',
seller: 'Google',
inStock: true,
rating: 4,
price: 299,
discountPrice: 359,
image: googleHome,
quantity: 1,
estimatedDelivery: '18th Nov 2021',
},
{
id: 2,
name: 'Apple iPhone 11 (64GB, Black)',
seller: 'Apple',
inStock: true,
rating: 4,
price: 899,
discountPrice: 999,
image: iphone11,
quantity: 1,
estimatedDelivery: '20th Nov 2021',
},
],
promoCode: '',
orderAmount: 1198,
deliveryAddress: 'home',
deliverySpeed: 'free',
deliveryCharges: 0,
addresses: [
{
title: 'John Doe (Default)',
desc: '4135 Parkway Street, Los Angeles, CA, 90017',
subtitle: '1234567890',
value: 'home',
},
{
title: 'ACME Inc.',
desc: '87 Hoffman Avenue, New York, NY, 10016',
subtitle: '1234567890',
value: 'office',
},
],
})
const currentStep = ref(0)
</script>
<template>
<div class="checkout-page">
<Navbar />
<VContainer>
<div class="checkout-card">
<VCard>
<VCardText>
<!-- 👉 Stepper -->
<AppStepper
v-model:current-step="currentStep"
class="checkout-stepper"
:items="checkoutSteps"
:direction="$vuetify.display.mdAndUp ? 'horizontal' : 'vertical'"
align="center"
/>
</VCardText>
<VDivider />
<VCardText>
<!-- 👉 stepper content -->
<VWindow
v-model="currentStep"
class="disable-tab-transition"
:touch="false"
>
<VWindowItem>
<CartContent
v-model:current-step="currentStep"
v-model:checkout-data="checkoutData"
/>
</VWindowItem>
<VWindowItem>
<AddressContent
v-model:current-step="currentStep"
v-model:checkout-data="checkoutData"
/>
</VWindowItem>
<VWindowItem>
<PaymentContent
v-model:current-step="currentStep"
v-model:checkout-data="checkoutData"
/>
</VWindowItem>
<VWindowItem>
<ConfirmationContent v-model:checkout-data="checkoutData" />
</VWindowItem>
</VWindow>
</VCardText>
</VCard>
</div>
</VContainer>
<Footer />
</div>
</template>
<style lang="scss">
.checkout-card {
margin-block: 9.25rem 5.25rem;
}
@media (max-width: 960px) and (min-width: 600px) {
.checkout-page {
.v-container {
padding-inline: 2rem !important;
}
}
}
</style>

View File

@@ -0,0 +1,140 @@
<script setup>
import Footer from '@/views/front-pages/front-page-footer.vue'
import Navbar from '@/views/front-pages/front-page-navbar.vue'
const breadCrumbItems = [
{
title: 'Help Center',
to: { name: 'front-pages-help-center' },
},
{ title: 'Buying and item support' },
{ title: 'Template kits' },
]
definePage({ meta: { layout: 'blank' } })
const articleData = ref()
const { data, error } = await useApi('/pages/help-center/article')
if (error.value)
console.log(error.value)
else
articleData.value = data.value
</script>
<!-- eslint-disable vue/no-v-html -->
<template>
<div class="bg-surface help-center-article">
<!-- 👉 Navbar -->
<Navbar />
<!-- 👉 Content -->
<VContainer>
<div
class="d-flex gap-6 flex-lg-row flex-column"
style=" margin-block: 9.25rem 5.25rem"
>
<div>
<div>
<VBreadcrumbs
class="px-0 py-2 flex-wrap"
:items="breadCrumbItems"
>
<template #item="{ item, index }">
<div class="d-flex align-center">
<VIcon
size="20"
icon="ri-star-fill"
class="me-1"
:class="index === breadCrumbItems.length - 1 ? 'text-high-emphasis' : 'text-medium-emphasis'"
/>
<div
class="text-body-1"
:class="index === breadCrumbItems.length - 1 ? 'text-high-emphasis' : 'text-medium-emphasis'"
>
{{ item.title }}
</div>
</div>
</template>
</VBreadcrumbs>
<h4 class="text-h4 mb-2">
{{ articleData?.title }}
</h4>
<div class="text-body-1">
{{ articleData?.lastUpdated }}
</div>
</div>
<VDivider class="my-6" />
<div
class="mb-6"
v-html="articleData?.productContent"
/>
<VImg :src="articleData?.productImg" />
<p class="my-6 text-body-1">
{{ articleData?.checkoutContent }}
</p>
<VImg :src="articleData?.checkoutImg" />
</div>
<div style="min-inline-size: 300px;">
<VTextField
prepend-inner-icon="ri-search-line"
placeholder="Search..."
class="pt-2 mb-6"
/>
<div>
<!-- 👉 Article List -->
<h5
class="text-h5 px-4 py-2 mb-4 rounded"
style="background: rgba(var(--v-theme-on-surface),var(--v-hover-opacity));"
>
Articles in this section
</h5>
<VList class="card-list">
<VListItem
v-for="(item, index) in articleData?.articleList"
:key="index"
link
>
<template #append>
<VIcon
size="20"
icon="ri-arrow-right-s-line"
color="disabled"
/>
</template>
<VListItemTitle>
{{ item }}
</VListItemTitle>
</VListItem>
</VList>
</div>
</div>
</div>
</VContainer>
<!-- 👉 Footer -->
<Footer />
</div>
</template>
<style lang="scss" scoped>
.card-list {
--v-card-list-gap: 1rem;
}
.help-center-article{
@media (min-width: 600px) and (max-width: 960px) {
.v-container {
padding-inline: 2rem !important;
}
}
}
</style>

View File

@@ -0,0 +1,97 @@
<script setup>
import Footer from '@/views/front-pages/front-page-footer.vue'
import Navbar from '@/views/front-pages/front-page-navbar.vue'
import HelpCenterLandingArticlesOverview from '@/views/pages/help-center/HelpCenterLandingArticlesOverview.vue'
import HelpCenterLandingFooter from '@/views/pages/help-center/HelpCenterLandingFooter.vue'
import HelpCenterLandingKnowledgeBase from '@/views/pages/help-center/HelpCenterLandingKnowledgeBase.vue'
// fetching data from fake-api
definePage({ meta: { layout: 'blank' } })
const { data: faqData } = await useApi('/pages/help-center')
const apiData = faqData.value
</script>
<template>
<div class="help-center-page">
<Navbar />
<div v-if="apiData">
<AppSearchHeader
title="Hello, how can we help?"
subtitle="Common troubleshooting topics: eCommerce, Blogging to payment"
custom-class="rounded-0"
/>
<!-- 👉 Popular Articles -->
<div class="bg-surface help-center-section">
<VContainer>
<h4 class="text-h4 text-center mb-6">
Popular Articles
</h4>
<HelpCenterLandingArticlesOverview :articles="apiData.popularArticles" />
</VContainer>
</div>
<!-- 👉 Knowledge Base -->
<div class="help-center-section">
<VContainer>
<h4 class="text-h4 text-center mb-6">
Knowledge Base
</h4>
<HelpCenterLandingKnowledgeBase :categories="apiData.allArticles" />
</VContainer>
</div>
<!-- 👉 Keep Learning -->
<div class="bg-surface help-center-section">
<VContainer>
<h4 class="text-h4 text-center mb-6">
Keep Learning
</h4>
<HelpCenterLandingArticlesOverview :articles="apiData.keepLearning" />
</VContainer>
</div>
<!-- 👉 Still need help? -->
<HelpCenterLandingFooter />
<Footer />
</div>
</div>
</template>
<style lang="scss">
.help-center-page {
.search-header {
background-size: cover !important;
padding-block-start: 9rem !important;
}
}
@media (max-width: 960px) and (min-width: 600px) {
.help-center-page {
.v-container {
padding-inline: 2rem !important;
}
.search-header {
padding: 5rem !important;
}
}
}
@media (max-width: 599px) {
.help-center-page {
.search-header {
padding-block: 4rem 2rem !important;
padding-inline: 2rem !important;
}
}
}
</style>
<style lang="scss" scoped>
.help-center-section{
padding-block: 5.25rem;
}
</style>

View File

@@ -0,0 +1,83 @@
<script setup>
import Footer from '@/views/front-pages/front-page-footer.vue'
import Navbar from '@/views/front-pages/front-page-navbar.vue'
import Banner from '@/views/front-pages/landing-page/banner.vue'
import ContactUs from '@/views/front-pages/landing-page/contact-us.vue'
import CustomersReview from '@/views/front-pages/landing-page/customers-review.vue'
import FaqSection from '@/views/front-pages/landing-page/faq-section.vue'
import Features from '@/views/front-pages/landing-page/features.vue'
import HeroSection from '@/views/front-pages/landing-page/hero-section.vue'
import OurTeam from '@/views/front-pages/landing-page/our-team.vue'
import PricingPlans from '@/views/front-pages/landing-page/pricing-plans.vue'
import ProductStats from '@/views/front-pages/landing-page/product-stats.vue'
definePage({ meta: { layout: 'blank' } })
const activeSectionId = ref()
const refHome = ref()
const refFeatures = ref()
const refTeam = ref()
const refContact = ref()
const refFaq = ref()
useIntersectionObserver([
refHome,
refFeatures,
refTeam,
refContact,
refFaq,
], ([{ isIntersecting, target }]) => {
if (isIntersecting)
activeSectionId.value = target.id
}, { threshold: 0.25 })
</script>
<template>
<div class="landing-page-wrapper">
<Navbar :active-id="activeSectionId" />
<!-- 👉 Hero Section -->
<HeroSection ref="refHome" />
<!-- 👉 Useful features -->
<div :style="{ 'background-color': 'rgb(var(--v-theme-surface))' }">
<Features ref="refFeatures" />
</div>
<!-- 👉 Customer Review -->
<CustomersReview />
<!-- 👉 Our Team -->
<div :style="{ 'background-color': 'rgb(var(--v-theme-surface))' }">
<OurTeam ref="refTeam" />
</div>
<!-- 👉 Pricing Plans -->
<PricingPlans />
<!-- 👉 Product stats -->
<ProductStats />
<!-- 👉 FAQ Section -->
<FaqSection ref="refFaq" />
<!-- 👉 Banner -->
<Banner />
<!-- 👉 Contact Us -->
<ContactUs ref="refContact" />
<!-- 👉 Footer -->
<Footer />
</div>
</template>
<style lang="scss">
@media (max-width: 960px) and (min-width: 600px) {
.landing-page-wrapper {
.v-container {
padding-inline: 2rem !important;
}
}
}
</style>

View File

@@ -0,0 +1,313 @@
<script setup>
import Footer from '@/views/front-pages/front-page-footer.vue'
import Navbar from '@/views/front-pages/front-page-navbar.vue'
import paypalDark from '@images/icons/payments/img/paypal-dark.png'
import paypalLight from '@images/icons/payments/img/paypal-light.png'
import visaDark from '@images/icons/payments/img/visa-dark.png'
import visaLight from '@images/icons/payments/img/visa-light.png'
const visa = useGenerateImageVariant(visaLight, visaDark)
const paypal = useGenerateImageVariant(paypalLight, paypalDark)
definePage({ meta: { layout: 'blank' } })
const radioContent = [
{
title: 'Credit Card',
value: 'credit card',
images: visa.value,
},
{
title: 'PayPal',
value: 'paypal',
images: paypal.value,
},
]
const selectedRadio = ref('credit card')
const selectedCountry = ref('USA')
const isPricingPlanDialogVisible = ref(false)
</script>
<template>
<!-- eslint-disable vue/attribute-hyphenation -->
<div class="payment-page">
<!-- 👉 Navbar -->
<Navbar />
<!-- 👉 Payment card -->
<VContainer>
<div class="d-flex justify-center align-center payment-card">
<VCard width="100%">
<VRow>
<VCol
cols="12"
md="8"
:class="$vuetify.display.mdAndUp ? 'border-e' : 'border-b'"
>
<VCardText
class="pa-8"
:class="$vuetify.display.smAndDown ? 'pb-5' : 'pe-5'"
>
<!-- Checkout header -->
<div>
<h4 class="text-h4 mb-2">
Checkout
</h4>
<div class="text-body-1">
All plans include 40+ advanced tools and features to boost your product. Choose the best plan to fit your needs.
</div>
</div>
<CustomRadios
v-model:selected-radio="selectedRadio"
:radio-content="radioContent"
:grid-column="{ cols: '12', sm: '6' }"
class="my-8"
>
<template #default="{ item }">
<div class="d-flex align-center gap-x-4">
<img
:src="item.images"
height="34"
width="58"
>
<div class="text-body-1 text-high-emphasis">
{{ item.title }}
</div>
</div>
</template>
</CustomRadios>
<!-- billing Details -->
<div class="mb-7">
<h4 class="text-h4 mb-6">
Billing Details
</h4>
<VRow class="my-0">
<VCol
cols="12"
md="6"
>
<VTextField
label="Email Address"
type="email"
placeholder="johndoe@email.com"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
label="Password"
autocomplete="on"
type="password"
placeholder="············"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VSelect
v-model="selectedCountry"
label="Billing Country"
:items="['USA', 'Canada', 'UK', 'AUS']"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
label="Biling Zip/Postal Code"
type="number"
placeholder="129211"
/>
</VCol>
</VRow>
</div>
<!-- Credit card info -->
<div :class="selectedRadio === 'paypal' ? 'd-none' : 'd-block'">
<h4 class="text-h4 mb-6">
Credit Card Info
</h4>
<VRow class="my-0">
<VCol cols="12">
<VTextField
label="Card Number"
placeholder="8787 2345 3458"
type="number"
/>
</VCol>
<VCol
cols="12"
md="4"
>
<VTextField
label="Card Holder"
placeholder="John Doe"
/>
</VCol>
<VCol
cols="12"
md="4"
>
<VTextField
label="Exp. date"
placeholder="05/2020"
/>
</VCol>
<VCol
cols="12"
md="4"
>
<VTextField
label="CVV"
type="number"
placeholder="784"
/>
</VCol>
</VRow>
</div>
</VCardText>
</VCol>
<VCol
cols="12"
md="4"
>
<VCardText
class="pa-8"
:class="$vuetify.display.smAndDown ? '' : 'ps-5'"
>
<!-- order summary -->
<div class="mb-8">
<h4 class="text-h4 mb-2">
Order Summary
</h4>
<div class="text-body-1">
It can help you manage and service orders before, during, and after fulfilment.
</div>
</div>
<VCard
flat
color="rgba(var(--v-theme-on-surface), var(--v-hover-opacity))"
>
<VCardText class="pa-6">
<div>
<div class="text-body-1">
A simple start for everyone
</div>
<div class="my-4">
<h1 class="text-h1 d-inline-block">
$59.99
</h1> <div class="text-body-1 font-weight-medium d-inline-block">
/ month
</div>
</div>
<VBtn
variant="outlined"
block
@click="isPricingPlanDialogVisible = !isPricingPlanDialogVisible"
>
Change Plan
</VBtn>
</div>
</VCardText>
</VCard>
<div class="my-5">
<div class="d-flex justify-space-between mb-2">
<div class="text-body-1">
Subscription
</div>
<div class="text-body-1 font-weight-medium text-high-emphasis">
$85.99
</div>
</div>
<div class="d-flex justify-space-between">
<div class="text-body-1">
Tax
</div>
<div class="text-body-1 font-weight-medium text-high-emphasis">
$4.99
</div>
</div>
<VDivider class="my-4" />
<div class="d-flex justify-space-between">
<div class="text-body-1">
Total
</div>
<div class="text-body-1 font-weight-medium text-high-emphasis">
$90.98
</div>
</div>
</div>
<VBtn
:append-icon="$vuetify.locale.isRtl ? 'ri-arrow-left-line' : 'ri-arrow-right-line'"
block
color="success"
class="mb-8"
>
Proceed With Payment
</VBtn>
<div class="text-body-1">
By continuing, you accept to our Terms of Services and Privacy Policy. Please note that payments are non-refundable.
</div>
</VCardText>
</VCol>
</VRow>
</VCard>
</div>
</VContainer>
<!-- 👉 Footer -->
<Footer />
<PricingPlanDialog v-model:is-dialog-visible="isPricingPlanDialogVisible" />
</div>
</template>
<style lang="scss" scoped>
.footer {
position: static !important;
inline-size: 100%;
inset-block-end: 0;
}
.payment-card {
margin-block: 9.25rem 5.25rem;
}
.payment-page {
@media (min-width: 600px) and (max-width: 960px) {
.v-container {
padding-inline: 2rem !important;
}
}
}
</style>
<style lang="scss">
.payment-card {
.custom-radio {
.v-radio {
margin-block-start: 0 !important;
}
}
}
</style>

View File

@@ -0,0 +1,409 @@
<script setup>
import Footer from '@/views/front-pages/front-page-footer.vue'
import Navbar from '@/views/front-pages/front-page-navbar.vue'
import poseFs9 from '@images/pages/pose-fs-9.png'
definePage({ meta: { layout: 'blank' } })
const features = [
{
feature: '14-days free trial',
starter: true,
pro: true,
enterprise: true,
addOnAvailable: {
starter: false,
pro: false,
enterprise: false,
},
},
{
feature: 'No user limit',
starter: false,
pro: false,
enterprise: true,
addOnAvailable: {
starter: false,
pro: false,
enterprise: false,
},
},
{
feature: 'Product Support',
starter: false,
pro: true,
enterprise: true,
addOnAvailable: {
starter: false,
pro: false,
enterprise: false,
},
},
{
feature: 'Email Support',
starter: false,
pro: false,
enterprise: true,
addOnAvailable: {
starter: false,
pro: true,
enterprise: false,
},
},
{
feature: 'Integrations',
starter: false,
pro: true,
enterprise: true,
addOnAvailable: {
starter: false,
pro: false,
enterprise: false,
},
},
{
feature: 'Removal of Front branding',
starter: false,
pro: false,
enterprise: true,
addOnAvailable: {
starter: false,
pro: true,
enterprise: false,
},
},
{
feature: 'Active maintenance & support',
starter: false,
pro: false,
enterprise: true,
addOnAvailable: {
starter: false,
pro: false,
enterprise: false,
},
},
{
feature: 'Data storage for 365 days',
starter: false,
pro: false,
enterprise: true,
addOnAvailable: {
starter: false,
pro: false,
enterprise: false,
},
},
]
const faqs = [
{
question: 'What counts towards the 100 responses limit?',
answer: 'Donec placerat, lectus sed mattis semper, neque lectus feugiat lectus, varius pulvinar diam eros in elit. Pellentesque convallis laoreet laoreet.Donec placerat, lectus sed mattis semper, neque lectus feugiat lectus, varius pulvinar diam eros in elit. Pellentesque convallis laoreet laoreet.',
},
{
question: 'How do you process payments?',
answer: 'We accept Visa®, MasterCard®, American Express®, and PayPal®. So you can be confident that your credit card information will be kept safe and secure.',
},
{
question: 'Do you have a money-back guarantee?',
answer: '2Checkout accepts all types of credit and debit cards.',
},
{
question: 'I have more questions. Where can I get help?',
answer: 'Yes. You may request a refund within 30 days of your purchase without any additional explanations.',
},
]
</script>
<template>
<div class="pricing-page">
<Navbar />
<VCard class="pricing-card">
<!-- 👉 App Pricing components -->
<VContainer>
<div class="pricing-section">
<AppPricing
md="4"
cols="12"
>
<template #heading>
<h2 class="text-h2 pb-2">
Pricing Plans
</h2>
</template>
</AppPricing>
</div>
</VContainer>
<!-- 👉 Free trial Banner -->
<div class="page-pricing-free-trial-banner-bg">
<VContainer>
<div class="d-flex align-center flex-md-row flex-column position-relative">
<div class="text-center text-md-start py-10 px-10 px-sm-0">
<h4 class="text-h4 text-primary mb-2">
Still not convinced? Start with a 14-day FREE trial!
</h4>
<p class="text-body-1">
You will get full access to all the features for 14 days.
</p>
<VBtn
class="mt-4"
:to="{ name: 'front-pages-payment' }"
>
Start-14-day FREE trial
</VBtn>
</div>
<div class="free-trial-illustrator">
<VImg
:src="poseFs9"
:width="250"
/>
</div>
</div>
</VContainer>
</div>
<!-- 👉 Plans -->
<VContainer>
<div class="pricing-section">
<div class="text-center pb-6">
<h4 class="text-h4 mb-2">
Pick a plan that works best for you
</h4>
<p class="text-body-1 mb-0">
Stay cool, we have a 48-hour money back guarantee!
</p>
</div>
<!-- 👉 Features & Tables -->
<VTable class="text-no-wrap border rounded pricing-table">
<!-- 👉 Table head -->
<thead>
<tr>
<th
scope="col"
class="py-4"
>
TIME
</th>
<th
v-for="{ plan, price } in [
{ plan: 'STARTER', price: 'Free' },
{ plan: 'PRO', price: '$7.5/Month' },
{ plan: 'ENTERPRISE', price: '$16/Month' },
]"
:key="plan"
scope="col"
class="text-center py-4"
>
<div class="position-relative">
{{ plan }}
<VAvatar
v-if="plan === 'PRO'"
rounded="lg"
color="primary"
size="18"
class="position-absolute ms-2"
style="inset-block-start: -0.25rem;"
>
<VIcon
icon="ri-star-s-fill"
size="14"
/>
</VAvatar>
</div>
<div class="text-body-2">
{{ price }}
</div>
</th>
</tr>
</thead>
<!-- 👉 Table Body -->
<tbody>
<tr
v-for="feature in features"
:key="feature.feature"
>
<td class="text-high-emphasis">
{{ feature.feature }}
</td>
<td class="text-center">
<VIcon
v-if="!feature.addOnAvailable.starter"
:color="feature.starter ? 'primary' : ''"
size="20"
:icon="feature.starter ? 'ri-checkbox-circle-line' : 'ri-close-circle-line'"
/>
<VChip
v-if="feature.addOnAvailable.starter"
color="primary"
size="small"
>
Add-On-Available
</VChip>
</td>
<td class="text-center">
<VIcon
v-if="!feature.addOnAvailable.pro"
:color="feature.pro ? 'primary' : ''"
size="20"
:icon="feature.pro ? 'ri-checkbox-circle-line' : 'ri-close-circle-line'"
/>
<VChip
v-if="feature.addOnAvailable.pro"
color="primary"
size="small"
>
Add-On-Available
</VChip>
</td>
<td class="text-center">
<VIcon
v-if="!feature.addOnAvailable.enterprise"
:color="feature.enterprise ? 'primary' : ''"
size="20"
:icon="feature.enterprise ? 'ri-checkbox-circle-line' : 'ri-close-circle-line'"
/>
<VChip
v-if="feature.addOnAvailable.enterprise"
color="primary"
size="small"
>
Add-On-Available
</VChip>
</td>
</tr>
</tbody>
<!-- 👉 Table footer -->
<tfoot>
<tr>
<td class="py-4" />
<td class="text-center py-4">
<VBtn
variant="outlined"
:to="{ name: 'front-pages-payment' }"
>
Choose Plan
</VBtn>
</td>
<td class="text-center py-4">
<VBtn :to="{ name: 'front-pages-payment' }">
Choose Plan
</VBtn>
</td>
<td class="text-center py-4">
<VBtn
variant="outlined"
:to="{ name: 'front-pages-payment' }"
>
Choose Plan
</VBtn>
</td>
</tr>
</tfoot>
</VTable>
</div>
</VContainer>
<!-- 👉 FAQ -->
<div class="bg-background">
<VContainer>
<div class="pricing-section">
<div class="text-center">
<h4 class="text-h4 mb-2">
FAQ's
</h4>
<p class="text-body-1 mb-0">
Let us help answer the most common questions.
</p>
</div>
<div class="pt-6">
<VExpansionPanels>
<VExpansionPanel
v-for="faq in faqs"
:key="faq.question"
:title="faq.question"
:text="faq.answer"
/>
</VExpansionPanels>
</div>
</div>
</VContainer>
</div>
<Footer />
</VCard>
</div>
</template>
<style lang="scss" scoped>
.pricing-section{
padding-block: 5.25rem !important;
padding-inline: 0 !important;
}
.page-pricing-free-trial-banner-bg {
/* stylelint-disable-next-line color-function-notation */
background-color: rgba(var(--v-theme-primary), 0.16);
}
.pricing-card {
padding-block-start: 4rem !important;
}
.pricing-table-title{
color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity));
font-size: 2rem;
font-weight: 500;
line-height: 2.625rem;
}
@media screen and (min-width: 960px) {
.free-trial-illustrator {
position: absolute;
inset-block-end: -1rem !important;
inset-inline-end: 5%;
}
}
@media screen and (max-width: 959px) {
.free-trial-illustrator {
position: relative;
inset-block-end: -1rem !important;
}
}
</style>
<style lang="scss">
.pricing-table{
--v-table-header-color: rgb(var(--v-theme-surface));
&.v-table{
.v-table__wrapper{
table{
thead {
tr{
th{
border-block-end: 1px solid rgba(var(--v-theme-on-surface), var(--v-border-opacity)) !important;
}
}
}
tbody{
tr:nth-child(even){
background: rgba(var(--v-theme-on-surface), var(--v-hover-opacity));
}
}
}
}
}
}
.pricing-page{
@media (min-width: 600px) and (max-width: 960px) {
.v-container {
padding-inline: 2rem !important;
}
}
}
</style>

View File

@@ -0,0 +1,242 @@
<script setup>
import tree1 from '@images/misc/tree1.png'
import authV2LoginIllustrationBorderedDark from '@images/pages/auth-v2-login-illustration-bordered-dark.png'
import authV2LoginIllustrationBorderedLight from '@images/pages/auth-v2-login-illustration-bordered-light.png'
import authV2LoginIllustrationDark from '@images/pages/auth-v2-login-illustration-dark.png'
import authV2LoginIllustrationLight from '@images/pages/auth-v2-login-illustration-light.png'
import authV2MaskDark from '@images/pages/mask-v2-dark.png'
import authV2MaskLight from '@images/pages/mask-v2-light.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
import { VForm } from 'vuetify/components/VForm'
const authThemeImg = useGenerateImageVariant(authV2LoginIllustrationLight, authV2LoginIllustrationDark, authV2LoginIllustrationBorderedLight, authV2LoginIllustrationBorderedDark, true)
const authThemeMask = useGenerateImageVariant(authV2MaskLight, authV2MaskDark)
definePage({
meta: {
layout: 'blank',
unauthenticatedOnly: true,
},
})
const isPasswordVisible = ref(false)
const route = useRoute()
const router = useRouter()
const ability = useAbility()
const errors = ref({
email: undefined,
password: undefined,
})
const refVForm = ref()
const credentials = ref({
email: 'admin@demo.com',
password: 'admin',
})
const rememberMe = ref(false)
const login = async () => {
try {
const res = await $api('/auth/login', {
method: 'POST',
body: {
email: credentials.value.email,
password: credentials.value.password,
},
onResponseError({ response }) {
errors.value = response._data.errors
},
})
const { accessToken, userData, userAbilityRules } = res
useCookie('userAbilityRules').value = userAbilityRules
ability.update(userAbilityRules)
useCookie('userData').value = userData
useCookie('accessToken').value = accessToken
await nextTick(() => {
router.replace(route.query.to ? String(route.query.to) : '/')
})
} catch (err) {
console.error(err)
}
}
const onSubmit = () => {
refVForm.value?.validate().then(({ valid: isValid }) => {
if (isValid)
login()
})
}
</script>
<template>
<RouterLink to="/">
<div class="auth-logo d-flex align-center gap-x-3">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
</RouterLink>
<VRow
no-gutters
class="auth-wrapper"
>
<VCol
md="8"
class="d-none d-md-flex position-relative"
>
<div class="d-flex align-center justify-end w-100 h-100 pa-10 pe-0">
<VImg
max-width="797"
:src="authThemeImg"
class="auth-illustration"
/>
</div>
<img
class="auth-footer-mask"
height="360"
:src="authThemeMask"
>
<VImg
:src="tree1"
alt="tree image"
height="190"
width="90"
class="auth-footer-tree"
/>
</VCol>
<VCol
cols="12"
md="4"
class="auth-card-v2 d-flex align-center justify-center"
style="background-color: rgb(var(--v-theme-surface));"
>
<VCard
flat
:max-width="500"
class="mt-12 mt-sm-0 pa-4"
>
<VCardText>
<h4 class="text-h4 mb-1">
Welcome to <span class="text-capitalize">{{ themeConfig.app.title }}!</span> 👋🏻
</h4>
<p class="mb-0">
Please sign-in to your account and start the adventure
</p>
</VCardText>
<VCardText>
<VAlert
color="primary"
variant="tonal"
>
<p class="text-caption mb-2 text-primary">
Admin Email: <strong>admin@demo.com</strong> / Pass: <strong>admin</strong>
</p>
<p class="text-caption mb-0 text-primary">
Client Email: <strong>client@demo.com</strong> / Pass: <strong>client</strong>
</p>
</VAlert>
</VCardText>
<VCardText>
<VForm
ref="refVForm"
@submit.prevent="onSubmit"
>
<VRow>
<!-- email -->
<VCol cols="12">
<VTextField
v-model="credentials.email"
label="Email"
placeholder="johndoe@email.com"
type="email"
autofocus
:rules="[requiredValidator, emailValidator]"
:error-messages="errors.email"
/>
</VCol>
<!-- password -->
<VCol cols="12">
<VTextField
v-model="credentials.password"
label="Password"
placeholder="············"
:rules="[requiredValidator]"
:type="isPasswordVisible ? 'text' : 'password'"
:error-messages="errors.password"
:append-inner-icon="isPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/>
<div class="d-flex align-center flex-wrap justify-space-between my-5 gap-2">
<VCheckbox
v-model="rememberMe"
label="Remember me"
/>
<RouterLink
class="text-primary"
:to="{ name: 'forgot-password' }"
>
Forgot Password?
</RouterLink>
</div>
<VBtn
block
type="submit"
>
Login
</VBtn>
</VCol>
<!-- create account -->
<VCol
cols="12"
class="text-center text-base"
>
<span>New on our platform?</span> <RouterLink
class="text-primary d-inline-block"
:to="{ name: 'register' }"
>
Create an account
</RouterLink>
</VCol>
<!-- <VCol
cols="12"
class="d-flex align-center"
>
<VDivider />
<span class="mx-4">or</span>
<VDivider />
</VCol> -->
<!-- auth providers -->
<!-- <VCol
cols="12"
class="text-center"
>
<AuthProvider />
</VCol> -->
</VRow>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,78 @@
<script setup>
import miscMaskDark from '@images/misc/misc-mask-dark.png'
import miscMaskLight from '@images/misc/misc-mask-light.png'
import tree1 from '@images/misc/tree1.png'
import tree3 from '@images/misc/tree3.png'
import pages401 from '@images/pages/401.png'
const authThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark)
definePage({
alias: '/pages/misc/not-authorized',
meta: {
layout: 'blank',
public: true,
},
})
</script>
<template>
<div class="misc-wrapper">
<ErrorHeader
status-code="401"
title="You are not authorized! 🔐"
description="You don't have permission to access this page. Go Home!"
class="mb-10"
/>
<!-- 👉 Image -->
<div class="misc-avatar w-100 text-center">
<VImg
:src="pages401"
alt="Coming Soon"
:max-width="785"
:height="500"
class="mx-auto"
/>
<VBtn
to="/"
class="mt-10"
style="z-index: 1;"
>
Back to Home
</VBtn>
<div class="d-md-flex gap-x-2 misc-footer-tree d-none">
<img
:src="tree3"
alt="tree"
height="120"
width="68"
>
<img
:src="tree3"
alt="tree"
height="70"
width="40"
class="align-self-end"
>
</div>
<img
height="210"
:src="tree1"
class="misc-footer-tree-1 d-none d-md-block"
>
<img
cover
:src="authThemeMask"
height="172"
class="misc-footer-img d-none d-md-block flip-in-rtl"
>
</div>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/pages/misc.scss";
</style>

View File

@@ -0,0 +1,98 @@
<script setup>
import AccountSettingsAccount from '@/views/pages/account-settings/AccountSettingsAccount.vue'
import AccountSettingsBillingAndPlans from '@/views/pages/account-settings/AccountSettingsBillingAndPlans.vue'
import AccountSettingsConnections from '@/views/pages/account-settings/AccountSettingsConnections.vue'
import AccountSettingsNotification from '@/views/pages/account-settings/AccountSettingsNotification.vue'
import AccountSettingsSecurity from '@/views/pages/account-settings/AccountSettingsSecurity.vue'
const route = useRoute('pages-account-settings-tab')
const activeTab = computed({
get: () => route.params.tab,
set: () => route.params.tab,
})
// tabs
const tabs = [
{
title: 'Account',
icon: 'ri-group-line',
tab: 'account',
},
{
title: 'Security',
icon: 'ri-lock-line',
tab: 'security',
},
{
title: 'Billing & Plans',
icon: 'ri-bookmark-line',
tab: 'billing-plans',
},
{
title: 'Notifications',
icon: 'ri-notification-3-line',
tab: 'notification',
},
{
title: 'Connections',
icon: 'ri-link',
tab: 'connection',
},
]
definePage({ meta: { navActiveLink: 'pages-account-settings-tab' } })
</script>
<template>
<div>
<VTabs
v-model="activeTab"
class="v-tabs-pill"
>
<VTab
v-for="item in tabs"
:key="item.icon"
:value="item.tab"
:to="{ name: 'pages-account-settings-tab', params: { tab: item.tab } }"
>
<VIcon
start
:icon="item.icon"
/>
{{ item.title }}
</VTab>
</VTabs>
<VWindow
v-model="activeTab"
class="mt-5 disable-tab-transition"
:touch="false"
>
<!-- Account -->
<VWindowItem value="account">
<AccountSettingsAccount />
</VWindowItem>
<!-- Security -->
<VWindowItem value="security">
<AccountSettingsSecurity />
</VWindowItem>
<!-- Billing -->
<VWindowItem value="billing-plans">
<AccountSettingsBillingAndPlans />
</VWindowItem>
<!-- Notification -->
<VWindowItem value="notification">
<AccountSettingsNotification />
</VWindowItem>
<!-- Connections -->
<VWindowItem value="connection">
<AccountSettingsConnections />
</VWindowItem>
</VWindow>
</div>
</template>

View File

@@ -0,0 +1,115 @@
<script setup>
import miscMaskDark from '@images/misc/misc-mask-dark.png'
import miscMaskLight from '@images/misc/misc-mask-light.png'
import tree1 from '@images/misc/tree1.png'
import tree3 from '@images/misc/tree3.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
const authThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark)
definePage({ meta: { layout: 'blank' } })
const form = ref({ email: '' })
</script>
<template>
<div class="auth-wrapper d-flex align-center justify-center pa-4">
<VCard
class="auth-card pa-sm-4 pa-md-7 pa-0"
max-width="460"
>
<VCardText>
<div class="d-flex align-center gap-x-3 justify-center mb-6">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
<h4 class="text-h4 mb-1">
Forgot Password? 🔒
</h4>
<p class="mb-0">
Enter your email and we'll send you instructions to reset your password
</p>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- email -->
<VCol cols="12">
<VTextField
v-model="form.email"
autofocus
label="Email"
type="email"
placeholder="johndoe@email.com"
/>
</VCol>
<!-- reset password -->
<VCol cols="12">
<VBtn
block
type="submit"
>
Send Reset Link
</VBtn>
</VCol>
<!-- back to login -->
<VCol cols="12">
<RouterLink
class="d-flex align-center justify-center"
:to="{ name: 'pages-authentication-login-v1' }"
>
<VIcon
icon="ri-arrow-left-s-line"
size="20"
class="flip-in-rtl me-2"
/>
<span>Back to login</span>
</RouterLink>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
<div class="d-flex gap-x-2 auth-footer-start-tree">
<img
class="d-none d-md-block"
:src="tree3"
:height="120"
:width="67"
>
<img
class="d-none d-md-block align-self-end"
:src="tree3"
:height="70"
:width="40"
>
</div>
<img
:src="tree1"
class="auth-footer-end-tree d-none d-md-block"
:width="97"
:height="210"
>
<!-- bg img -->
<img
class="auth-footer-mask d-none d-md-block"
:src="authThemeMask"
height="172"
>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,132 @@
<script setup>
import authV2ForgotPasswordIllustrationBorderedDark from '@images/pages/auth-v2-forgot-password-illustration-dark-border.png'
import authV2ForgotPasswordIllustrationDark from '@images/pages/auth-v2-forgot-password-illustration-dark.png'
import authV2ForgotPasswordIllustrationBorderedLight from '@images/pages/auth-v2-forgot-password-illustration-light-border.png'
import authV2ForgotPasswordIllustrationLight from '@images/pages/auth-v2-forgot-password-illustration-light.png'
import tree1 from '@images/misc/tree1.png'
import authV2MaskDark from '@images/pages/mask-v2-dark.png'
import authV2MaskLight from '@images/pages/mask-v2-light.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
const authThemeImg = useGenerateImageVariant(authV2ForgotPasswordIllustrationLight, authV2ForgotPasswordIllustrationDark, authV2ForgotPasswordIllustrationBorderedLight, authV2ForgotPasswordIllustrationBorderedDark, true)
const authThemeMask = useGenerateImageVariant(authV2MaskLight, authV2MaskDark)
definePage({ meta: { layout: 'blank' } })
const email = ref('')
</script>
<template>
<RouterLink to="/">
<div class="auth-logo d-flex align-center gap-x-3">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
</RouterLink>
<VRow
class="auth-wrapper"
no-gutters
>
<VCol
cols="12"
md="8"
class="d-none d-md-flex position-relative"
>
<div
class="d-flex align-center justify-end w-100 h-100 pa-10"
:class="$vuetify.locale.isRtl ? 'pe-10' : 'pe-0'"
>
<VImg
width="853"
height="684"
:src="authThemeImg"
class="auth-illustration"
/>
</div>
<img
class="auth-footer-mask"
height="360"
:src="authThemeMask"
>
<img
:src="tree1"
alt="tree image"
height="140"
class="auth-footer-tree"
>
</VCol>
<VCol
cols="12"
md="4"
class="auth-card-v2 d-flex align-center justify-center"
style="background-color: rgb(var(--v-theme-surface));"
>
<VCard
flat
:max-width="500"
class="mt-12 mt-sm-0 pa-4"
>
<VCardText>
<h4 class="text-h4 mb-1">
Forgot Password? 🔒
</h4>
<p class="mb-0">
Enter your email and we'll send you instructions to reset your password
</p>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- email -->
<VCol cols="12">
<VTextField
v-model="email"
autofocus
label="Email"
placeholder="johndoe@email.com"
type="email"
/>
</VCol>
<!-- Reset link -->
<VCol cols="12">
<VBtn
block
type="submit"
>
Send Reset Link
</VBtn>
</VCol>
<!-- back to login -->
<VCol cols="12">
<RouterLink
class="d-flex align-center justify-center"
:to="{ name: 'pages-authentication-login-v2' }"
>
<VIcon
icon="ri-arrow-left-s-line"
class="flip-in-rtl"
/>
<span>Back to login</span>
</RouterLink>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,158 @@
<script setup>
import AuthProvider from '@/views/pages/authentication/AuthProvider.vue'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
import miscMaskDark from '@images/misc/misc-mask-dark.png'
import miscMaskLight from '@images/misc/misc-mask-light.png'
import tree1 from '@images/misc/tree1.png'
import tree3 from '@images/misc/tree3.png'
const authThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark)
definePage({ meta: { layout: 'blank' } })
const form = ref({
email: '',
password: '',
remember: false,
})
const isPasswordVisible = ref(false)
</script>
<template>
<div class="auth-wrapper d-flex align-center justify-center pa-4">
<VCard
class="auth-card pa-sm-4 pa-md-7 pa-0"
max-width="448"
>
<VCardText>
<div class="d-flex align-center gap-x-3 justify-center mb-6">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
<h4 class="text-h4 mb-1">
Welcome to <span class="text-capitalize">{{ themeConfig.app.title }}!</span> 👋🏻
</h4>
<p class="mb-0">
Please sign-in to your account and start the adventure
</p>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- email -->
<VCol cols="12">
<VTextField
v-model="form.email"
autofocus
label="Email"
type="email"
placeholder="johndoe@email.com"
/>
</VCol>
<!-- password -->
<VCol cols="12">
<VTextField
v-model="form.password"
label="Password"
placeholder="············"
:type="isPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/>
<div class="d-flex align-center flex-wrap justify-space-between my-5 gap-4">
<VCheckbox
v-model="form.remember"
label="Remember me"
/>
<RouterLink
class="text-primary"
:to="{ name: 'pages-authentication-forgot-password-v2' }"
>
Forgot Password?
</RouterLink>
</div>
<VBtn
block
type="submit"
>
Login
</VBtn>
</VCol>
<!-- create account -->
<VCol
cols="12"
class="text-center text-base"
>
<span>New on our platform?</span> <RouterLink
class="text-primary d-inline-block"
:to="{ name: 'pages-authentication-register-v2' }"
>
Create an account
</RouterLink>
</VCol>
<VCol
cols="12"
class="d-flex align-center"
>
<VDivider />
<span class="mx-2 text-high-emphasis">or</span>
<VDivider />
</VCol>
<!-- auth providers -->
<VCol
cols="12"
class="text-center"
>
<AuthProvider />
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
<div class="d-flex gap-x-2 auth-footer-start-tree">
<img
class="d-none d-md-block"
:src="tree3"
:height="120"
:width="67"
>
<img
class="d-none d-md-block align-self-end"
:src="tree3"
:height="70"
:width="40"
>
</div>
<img
:src="tree1"
class="auth-footer-end-tree d-none d-md-block"
:width="97"
:height="210"
>
<!-- bg img -->
<img
class="auth-footer-mask d-none d-md-block"
:src="authThemeMask"
height="172"
>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,175 @@
<script setup>
import AuthProvider from '@/views/pages/authentication/AuthProvider.vue'
import { themeConfig } from '@themeConfig'
import tree1 from '@images/misc/tree1.png'
import authV2LoginIllustrationBorderedDark from '@images/pages/auth-v2-login-illustration-bordered-dark.png'
import authV2LoginIllustrationBorderedLight from '@images/pages/auth-v2-login-illustration-bordered-light.png'
import authV2LoginIllustrationDark from '@images/pages/auth-v2-login-illustration-dark.png'
import authV2LoginIllustrationLight from '@images/pages/auth-v2-login-illustration-light.png'
import authV2MaskDark from '@images/pages/mask-v2-dark.png'
import authV2MaskLight from '@images/pages/mask-v2-light.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
const authThemeImg = useGenerateImageVariant(authV2LoginIllustrationLight, authV2LoginIllustrationDark, authV2LoginIllustrationBorderedLight, authV2LoginIllustrationBorderedDark, true)
const authThemeMask = useGenerateImageVariant(authV2MaskLight, authV2MaskDark)
definePage({ meta: { layout: 'blank' } })
const form = ref({
email: '',
password: '',
remember: false,
})
const isPasswordVisible = ref(false)
</script>
<template>
<RouterLink to="/">
<div class="auth-logo d-flex align-center gap-x-3">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
</RouterLink>
<VRow
no-gutters
class="auth-wrapper"
>
<VCol
md="8"
class="d-none d-md-flex position-relative"
>
<div
class="d-flex align-center justify-end w-100 h-100 pa-10"
:class="$vuetify.locale.isRtl ? 'pe-10' : 'pe-0'"
>
<VImg
max-width="797"
:src="authThemeImg"
class="auth-illustration"
/>
</div>
<img
class="auth-footer-mask"
height="360"
:src="authThemeMask"
>
<VImg
:src="tree1"
alt="tree image"
height="190"
width="90"
class="auth-footer-tree"
/>
</VCol>
<VCol
cols="12"
md="4"
class="auth-card-v2 d-flex align-center justify-center"
style="background-color: rgb(var(--v-theme-surface));"
>
<VCard
flat
:max-width="500"
class="mt-12 mt-sm-0 pa-4"
>
<VCardText>
<h4 class="text-h4 mb-1">
Welcome to <span class="text-capitalize">{{ themeConfig.app.title }}!</span> 👋🏻
</h4>
<p class="mb-0">
Please sign-in to your account and start the adventure
</p>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- email -->
<VCol cols="12">
<VTextField
v-model="form.email"
autofocus
label="Email"
type="email"
placeholder="johndoe@email.com"
/>
</VCol>
<!-- password -->
<VCol cols="12">
<VTextField
v-model="form.password"
label="Password"
placeholder="············"
:type="isPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/>
<div class="d-flex align-center flex-wrap justify-space-between my-5 gap-2">
<VCheckbox
v-model="form.remember"
label="Remember me"
/>
<RouterLink
class="text-primary"
:to="{ name: 'pages-authentication-forgot-password-v2' }"
>
Forgot Password?
</RouterLink>
</div>
<VBtn
block
type="submit"
>
Login
</VBtn>
</VCol>
<!-- create account -->
<VCol
cols="12"
class="text-center text-base"
>
<span>New on our platform?</span> <RouterLink
class="text-primary d-inline-block"
:to="{ name: 'pages-authentication-register-v2' }"
>
Create an account
</RouterLink>
</VCol>
<VCol
cols="12"
class="d-flex align-center"
>
<VDivider />
<span class="mx-2 text-high-emphasis">or</span>
<VDivider />
</VCol>
<!-- auth providers -->
<VCol
cols="12"
class="text-center"
>
<AuthProvider />
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,430 @@
<script setup>
import registerMultiStepIllustration from '@images/pages/register-multi-step-illustration.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
definePage({ meta: { layout: 'blank' } })
const currentStep = ref(0)
const isPasswordVisible = ref(false)
const isConfirmPasswordVisible = ref(false)
const radioContent = [
{
title: 'Starter',
desc: 'A simple start for everyone.',
value: '0',
},
{
title: 'Standard',
desc: 'For small to medium businesses.',
value: '99',
},
{
title: 'Enterprise',
desc: 'Solution for big organizations.',
value: '499',
},
]
const items = [
{
title: 'Account',
subtitle: 'Account Details',
},
{
title: 'Personal',
subtitle: 'Enter Information',
},
{
title: 'Billing',
subtitle: 'Payment Details',
},
]
const form = ref({
username: '',
email: '',
password: '',
confirmPassword: '',
link: '',
firstName: '',
lastName: '',
mobile: '',
pincode: '',
address: '',
landmark: '',
city: '',
state: null,
selectedPlan: '0',
cardNumber: '',
cardName: '',
expiryDate: '',
cvv: '',
})
const onSubmit = () => {
// eslint-disable-next-line no-alert
alert('Submitted..!!')
}
</script>
<template>
<RouterLink to="/">
<div class="auth-logo d-flex align-center gap-x-3">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
</RouterLink>
<VRow
no-gutters
class="auth-wrapper"
>
<VCol
md="4"
class="d-none d-md-flex"
>
<!-- here your illustration -->
<div
class="d-flex align-center w-100 h-100 justify-end pa-10"
:class="$vuetify.locale.isRtl ? 'pe-10 ps-0' : 'pe-0'"
>
<VImg
max-width="359"
:src="registerMultiStepIllustration"
class="auth-illustration"
/>
</div>
</VCol>
<VCol
cols="12"
md="8"
class="auth-card-v2 d-flex align-center justify-center pa-10"
style="background-color: rgb(var(--v-theme-surface));"
>
<VCard
flat
class="mt-12 mt-sm-0"
>
<AppStepper
v-model:current-step="currentStep"
:items="items"
:direction="$vuetify.display.smAndUp ? 'horizontal' : 'vertical'"
class="mb-12"
/>
<VWindow
v-model="currentStep"
class="disable-tab-transition"
style="max-inline-size: 650px;"
>
<VForm>
<VWindowItem>
<h4 class="text-h4">
Account Information
</h4>
<p class="mb-5">
Enter Your Account Details
</p>
<VRow>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="form.username"
label="Username"
placeholder="Johndoe"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="form.email"
label="Email"
placeholder="johndoe@email.com"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="form.password"
label="Password"
placeholder="············"
:type="isPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="form.confirmPassword"
label="Confirm Password"
placeholder="············"
:type="isConfirmPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isConfirmPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
/>
</VCol>
<VCol cols="12">
<VTextField
v-model="form.link"
label="Profile Link"
placeholder="https://profile.com/johndoe"
type="url"
/>
</VCol>
</VRow>
</VWindowItem>
<VWindowItem>
<h4 class="text-h4">
Personal Information
</h4>
<p class="mb-5">
Enter Your Personal Information
</p>
<VRow>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="form.firstName"
label="First Name"
placeholder="John"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="form.lastName"
label="Last Name"
placeholder="Doe"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="form.mobile"
type="number"
label="Mobile"
placeholder="+1 123 456 7890"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="form.pincode"
type="number"
label="Pincode"
placeholder="123456"
/>
</VCol>
<VCol cols="12">
<VTextField
v-model="form.address"
label="Address"
placeholder="1234 Main St, New York, NY 10001, USA"
/>
</VCol>
<VCol cols="12">
<VTextField
v-model="form.landmark"
label="Landmark"
placeholder="Near Central Park"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="form.city"
label="City"
placeholder="New York"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VSelect
v-model="form.state"
label="State"
placeholder="Select State"
:items="['New York', 'California', 'Florida', 'Washington', 'Texas']"
/>
</VCol>
</VRow>
</VWindowItem>
<VWindowItem>
<h4 class="text-h4">
Select Plan
</h4>
<p class="mb-5">
Select plan as per your requirement
</p>
<CustomRadiosWithIcon
v-model:selected-radio="form.selectedPlan"
:radio-content="radioContent"
:grid-column="{ sm: '4', cols: '12' }"
>
<template #default="{ item }">
<div class="text-center">
<h6 class="text-h6 mb-2">
{{ item.title }}
</h6>
<p class="clamp-text text-body-2 mb-2">
{{ item.desc }}
</p>
<div class="d-flex align-center justify-center">
<div class="text-primary mb-2">
$
</div>
<h4 class="text-h4 text-primary">
{{ item.value }}
</h4>
<div class="text-body-2 text-disabled mt-2">
/month
</div>
</div>
</div>
</template>
</CustomRadiosWithIcon>
<h4 class="text-h4 mt-12">
Payment Information
</h4>
<p class="mb-5">
Enter your card information
</p>
<VRow>
<VCol cols="12">
<VTextField
v-model="form.cardNumber"
type="number"
label="Card Number"
placeholder="1234 1234 1234 1234"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="form.cardName"
label="Name on Card"
placeholder="John Doe"
/>
</VCol>
<VCol
cols="6"
md="3"
>
<VTextField
v-model="form.expiryDate"
label="Expiry"
placeholder="MM/YY"
/>
</VCol>
<VCol
cols="6"
md="3"
>
<VTextField
v-model="form.cvv"
type="number"
label="CVV"
placeholder="123"
/>
</VCol>
</VRow>
</VWindowItem>
</VForm>
</VWindow>
<div class="d-flex flex-wrap justify-space-between gap-x-4 gap-y-2 mt-5">
<VBtn
color="secondary"
variant="outlined"
:disabled="currentStep === 0"
@click="currentStep--"
>
<VIcon
icon="ri-arrow-left-line"
start
class="flip-in-rtl"
/>
Previous
</VBtn>
<VBtn
v-if="items.length - 1 === currentStep"
color="success"
append-icon="ri-check-line"
@click="onSubmit"
>
submit
</VBtn>
<VBtn
v-else
@click="currentStep++"
>
Next
<VIcon
icon="ri-arrow-right-line"
end
class="flip-in-rtl"
/>
</VBtn>
</div>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,165 @@
<script setup>
import AuthProvider from '@/views/pages/authentication/AuthProvider.vue'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
import miscMaskDark from '@images/misc/misc-mask-dark.png'
import miscMaskLight from '@images/misc/misc-mask-light.png'
import tree1 from '@images/misc/tree1.png'
import tree3 from '@images/misc/tree3.png'
const authThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark)
definePage({ meta: { layout: 'blank' } })
const form = ref({
username: '',
email: '',
password: '',
privacyPolicies: false,
})
const isPasswordVisible = ref(false)
</script>
<template>
<div class="auth-wrapper d-flex align-center justify-center pa-4">
<VCard
class="auth-card pa-sm-4 pa-md-7 pa-0"
style="inline-size: 460px;"
>
<VCardText>
<div class="d-flex align-center gap-x-3 justify-center mb-6">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
<h4 class="text-h4 mb-1">
Adventure starts here 🚀
</h4>
<p class="mb-0">
Make your app management easy and fun!
</p>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- Username -->
<VCol cols="12">
<VTextField
v-model="form.username"
autofocus
label="Username"
placeholder="Johndoe"
/>
</VCol>
<!-- email -->
<VCol cols="12">
<VTextField
v-model="form.email"
label="Email"
type="email"
placeholder="johndoe@email.com"
/>
</VCol>
<!-- password -->
<VCol cols="12">
<VTextField
v-model="form.password"
label="Password"
placeholder="············"
:type="isPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/>
<div class="d-flex align-center my-5">
<VCheckbox
id="privacy-policy"
v-model="form.privacyPolicies"
inline
/>
<VLabel
for="privacy-policy"
style="opacity: 1;"
>
<span class="me-1 text-high-emphasis">I agree to</span>
<a
href="javascript:void(0)"
class="text-primary"
>privacy policy & terms</a>
</VLabel>
</div>
<VBtn
block
type="submit"
>
Sign up
</VBtn>
</VCol>
</VRow>
<!-- create account -->
<div class="text-center text-base my-5">
<span class="d-inline-block">Already have an account?</span> <RouterLink
class="text-primary d-inline-block"
:to="{ name: 'pages-authentication-login-v2' }"
>
Sign in instead
</RouterLink>
</div>
<div class="d-flex align-center mb-5">
<VDivider />
<span class="mx-4 text-high-emphasis">or</span>
<VDivider />
</div>
<!-- auth providers -->
<div class="text-center">
<AuthProvider />
</div>
</VForm>
</VCardText>
</VCard>
<div class="d-flex gap-x-2 auth-footer-start-tree">
<img
class="d-none d-md-block"
:src="tree3"
:height="120"
:width="67"
>
<img
class="d-none d-md-block align-self-end"
:src="tree3"
:height="70"
:width="40"
>
</div>
<img
:src="tree1"
class="auth-footer-end-tree d-none d-md-block"
:width="97"
:height="210"
>
<!-- bg img -->
<img
class="auth-footer-mask d-none d-md-block"
:src="authThemeMask"
height="172"
>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,189 @@
<script setup>
import AuthProvider from '@/views/pages/authentication/AuthProvider.vue'
import authV2RegisterIllustrationDark from '@images/pages/auth-v2-register-illustration-dark.png'
import authV2RegisterIllustrationLight from '@images/pages/auth-v2-register-illustration-light.png'
import { themeConfig } from '@themeConfig'
import tree2 from '@images/misc/tree2.png'
import authV2RegisterIllustrationBorderedDark from '@images/pages/auth-v2-register-illustration-bordered-dark.png'
import authV2RegisterIllustrationBorderedLight from '@images/pages/auth-v2-register-illustration-bordered-light.png'
import authV2MaskDark from '@images/pages/mask-v2-dark.png'
import authV2MaskLight from '@images/pages/mask-v2-light.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
const authThemeMask = useGenerateImageVariant(authV2MaskLight, authV2MaskDark)
const authThemeImg = useGenerateImageVariant(authV2RegisterIllustrationLight, authV2RegisterIllustrationDark, authV2RegisterIllustrationBorderedLight, authV2RegisterIllustrationBorderedDark, true)
definePage({ meta: { layout: 'blank' } })
const form = ref({
username: '',
email: '',
password: '',
privacyPolicies: false,
})
const isPasswordVisible = ref(false)
</script>
<template>
<RouterLink to="/">
<div class="auth-logo d-flex align-center gap-x-3">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
</RouterLink>
<VRow
no-gutters
class="auth-wrapper"
>
<VCol
cols="12"
md="8"
class="d-none d-md-flex position-relative"
>
<div
class="d-flex align-center justify-end w-100 h-100 pa-10"
:class="$vuetify.locale.isRtl ? 'pe-10' : 'pe-0'"
>
<VImg
max-width="797"
:src="authThemeImg"
class="auth-illustration"
/>
</div>
<img
class="auth-footer-mask"
height="360"
:src="authThemeMask"
>
<div class="d-flex gap-x-2 auth-footer-tree">
<img
:src="tree2"
alt="tree image"
height="180"
>
<img
:src="tree2"
alt="tree image"
height="120"
class="align-self-end"
>
</div>
</VCol>
<VCol
cols="12"
md="4"
class="auth-card-v2 d-flex align-center justify-center"
style="background-color: rgb(var(--v-theme-surface));"
>
<VCard
flat
:max-width="500"
class="mt-12 mt-sm-0 pa-4"
>
<VCardText>
<h4 class="text-h4 mb-1">
Adventure starts here 🚀
</h4>
<p class="mb-0">
Make your app management easy and fun!
</p>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- Username -->
<VCol cols="12">
<VTextField
v-model="form.username"
autofocus
label="Username"
placeholder="Johndoe"
/>
</VCol>
<!-- email -->
<VCol cols="12">
<VTextField
v-model="form.email"
label="Email"
type="email"
placeholder="johndoe@email.com"
/>
</VCol>
<!-- password -->
<VCol cols="12">
<VTextField
v-model="form.password"
label="Password"
placeholder="············"
:type="isPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/>
<div class="d-flex align-center my-5">
<VCheckbox
id="privacy-policy"
v-model="form.privacyPolicies"
inline
/>
<VLabel
for="privacy-policy"
style="opacity: 1;"
>
<span class="me-1 text-high-emphasis">I agree to</span>
<a
href="javascript:void(0)"
class="text-primary"
>privacy policy & terms</a>
</VLabel>
</div>
<VBtn
block
type="submit"
>
Sign up
</VBtn>
</VCol>
</VRow>
<!-- create account -->
<div class="text-center text-base my-5">
<span class="d-inline-block">Already have an account?</span> <RouterLink
class="text-primary d-inline-block"
:to="{ name: 'pages-authentication-login-v2' }"
>
Sign in instead
</RouterLink>
</div>
<div class="d-flex align-center mb-5">
<VDivider />
<span class="mx-4 text-high-emphasis">or</span>
<VDivider />
</div>
<!-- auth providers -->
<div class="text-center">
<AuthProvider />
</div>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,134 @@
<script setup>
import miscMaskDark from '@images/misc/misc-mask-dark.png'
import miscMaskLight from '@images/misc/misc-mask-light.png'
import tree1 from '@images/misc/tree1.png'
import tree3 from '@images/misc/tree3.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
const authThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark)
definePage({ meta: { layout: 'blank' } })
const form = ref({
newPassword: '',
confirmPassword: '',
})
const isPasswordVisible = ref(false)
const isConfirmPasswordVisible = ref(false)
</script>
<template>
<div class="auth-wrapper d-flex align-center justify-center pa-4">
<VCard
class="auth-card pa-sm-4 pa-md-7 pa-0"
max-width="460"
>
<VCardText>
<div class="d-flex align-center gap-x-3 justify-center mb-6">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
<h4 class="text-h4 mb-1">
Reset Password 🔒
</h4>
<p class="mb-0">
Enter your email and we'll send you instructions to reset your password
</p>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- password -->
<VCol cols="12">
<VTextField
v-model="form.newPassword"
autofocus
label="New Password"
placeholder="············"
:type="isPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/>
</VCol>
<!-- Confirm Password -->
<VCol cols="12">
<VTextField
v-model="form.confirmPassword"
label="Confirm Password"
placeholder="············"
:type="isConfirmPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isConfirmPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
/>
</VCol>
<!-- reset password -->
<VCol cols="12">
<VBtn
block
type="submit"
>
Set New Password
</VBtn>
</VCol>
<!-- back to login -->
<VCol>
<div class="d-flex align-center justify-center">
<RouterLink :to="{ name: 'pages-authentication-login-v1' }">
<VIcon
icon="ri-arrow-left-s-line"
size="20"
class="flip-in-rtl me-2"
/>
<span>Back to login</span>
</RouterLink>
</div>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
<div class="d-flex gap-x-2 auth-footer-start-tree">
<img
class="d-none d-md-block"
:src="tree3"
:height="120"
:width="67"
>
<img
class="d-none d-md-block align-self-end"
:src="tree3"
:height="70"
:width="40"
>
</div>
<img
:src="tree1"
class="auth-footer-end-tree d-none d-md-block"
:width="97"
:height="210"
>
<!-- bg img -->
<img
class="auth-footer-mask d-none d-md-block"
:src="authThemeMask"
height="172"
>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,158 @@
<script setup>
import tree3 from '@images/misc/tree3.png'
import authV2ResetPasswordIllustrationBorderedDark from '@images/pages/auth-v2-reset-password-illustration-bordered-dark.png'
import authV2ResetPasswordIllustrationBorderedLight from '@images/pages/auth-v2-reset-password-illustration-bordered-light.png'
import authV2ResetPasswordIllustrationDark from '@images/pages/auth-v2-reset-password-illustration-dark.png'
import authV2ResetPasswordIllustrationLight from '@images/pages/auth-v2-reset-password-illustration-light.png'
import authV2MaskDark from '@images/pages/mask-v2-dark.png'
import authV2MaskLight from '@images/pages/mask-v2-light.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
const authThemeImg = useGenerateImageVariant(authV2ResetPasswordIllustrationLight, authV2ResetPasswordIllustrationDark, authV2ResetPasswordIllustrationBorderedLight, authV2ResetPasswordIllustrationBorderedDark, true)
const authThemeMask = useGenerateImageVariant(authV2MaskLight, authV2MaskDark)
definePage({ meta: { layout: 'blank' } })
const form = ref({
newPassword: '',
confirmPassword: '',
})
const isPasswordVisible = ref(false)
const isConfirmPasswordVisible = ref(false)
</script>
<template>
<RouterLink to="/">
<div class="auth-logo d-flex align-center gap-x-3">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
</RouterLink>
<VRow
no-gutters
class="auth-wrapper"
>
<VCol
md="8"
class="d-none d-md-flex position-relative"
>
<div
class="d-flex align-center justify-end w-100 h-100 pa-10"
:class="$vuetify.locale.isRtl ? 'pe-10' : 'pe-0'"
>
<VImg
max-width="857"
:src="authThemeImg"
class="auth-illustration"
/>
</div>
<img
class="auth-footer-mask"
height="360"
:src="authThemeMask"
>
<div class="d-flex gap-x-2 auth-footer-tree">
<img
:src="tree3"
alt="tree image"
height="140"
>
<img
:src="tree3"
alt="tree image"
height="90"
width="50"
class="align-self-end"
>
</div>
</VCol>
<VCol
cols="12"
md="4"
class="auth-card-v2 d-flex align-center justify-center"
style="background-color: rgb(var(--v-theme-surface));"
>
<VCard
flat
:max-width="500"
class="mt-12 mt-sm-0 pa-4"
>
<VCardText>
<h4 class="text-h4 mb-1">
Reset Password 🔒
</h4>
<p class="mb-0">
Enter your email and we'll send you instructions to reset your password
</p>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- password -->
<VCol cols="12">
<VTextField
v-model="form.newPassword"
autofocus
label="New Password"
placeholder="············"
:type="isPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/>
</VCol>
<!-- Confirm Password -->
<VCol cols="12">
<VTextField
v-model="form.confirmPassword"
label="Confirm Password"
placeholder="············"
:type="isConfirmPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isConfirmPasswordVisible ? 'ri-eye-off-line' : 'ri-eye-line'"
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
/>
</VCol>
<!-- Set password -->
<VCol cols="12">
<VBtn
block
type="submit"
>
Set New Password
</VBtn>
</VCol>
<!-- back to login -->
<VCol cols="12">
<RouterLink
class="d-flex align-center justify-center"
:to="{ name: 'pages-authentication-login-v2' }"
>
<VIcon
icon="ri-arrow-left-s-line"
class="flip-in-rtl"
/>
<span>Back to login</span>
</RouterLink>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,125 @@
<script setup>
import miscMaskDark from '@images/misc/misc-mask-dark.png'
import miscMaskLight from '@images/misc/misc-mask-light.png'
import tree1 from '@images/misc/tree1.png'
import tree3 from '@images/misc/tree3.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
const authThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark)
definePage({ meta: { layout: 'blank' } })
const router = useRouter()
const otp = ref('')
const isOtpInserted = ref(false)
const onFinish = () => {
isOtpInserted.value = true
setTimeout(() => {
isOtpInserted.value = false
router.push('/')
}, 2000)
}
</script>
<template>
<div class="auth-wrapper d-flex align-center justify-center pa-4">
<VCard
class="auth-card pa-sm-4 pa-md-7 pa-0"
max-width="460"
>
<VCardText>
<div class="d-flex align-center gap-x-3 justify-center mb-6">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
<h4 class="text-h4 mb-1">
Two Step Verification 💬
</h4>
<p class="mb-1">
We sent a verification code to your mobile. Enter the code from the mobile in the field below.
</p>
<h6 class="text-h6">
******1234
</h6>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- email -->
<VCol cols="12">
<div>Type your 6 digit security code</div>
<VOtpInput
v-model="otp"
:disabled="isOtpInserted"
type="number"
class="pa-0"
@finish="onFinish"
/>
</VCol>
<!-- reset password -->
<VCol cols="12">
<VBtn
block
:loading="isOtpInserted"
:disabled="isOtpInserted"
type="submit"
>
Verify my account
</VBtn>
</VCol>
<!-- back to login -->
<VCol cols="12">
<div class="d-flex justify-center align-center flex-wrap">
<span class="me-1">Didn't get the code?</span>
<a href="#">Resend</a>
</div>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
<div class="d-flex gap-x-2 auth-footer-start-tree">
<img
class="d-none d-md-block"
:src="tree3"
:height="120"
:width="67"
>
<img
class="d-none d-md-block align-self-end"
:src="tree3"
:height="70"
:width="40"
>
</div>
<img
:src="tree1"
class="auth-footer-end-tree d-none d-md-block"
:width="97"
:height="210"
>
<!-- bg img -->
<img
class="auth-footer-mask d-none d-md-block"
:src="authThemeMask"
height="172"
>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,154 @@
<script setup>
import tree2 from '@images/misc/tree2.png'
import authV2MaskDark from '@images/pages/mask-v2-dark.png'
import authV2MaskLight from '@images/pages/mask-v2-light.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
import authV2TwoStepsIllustrationBorderedDark from '@images/pages/auth-v2-two-steps-illustration-border-dark.png'
import authV2TwoStepsIllustrationBorderedLight from '@images/pages/auth-v2-two-steps-illustration-border-light.png'
import authV2TwoStepsIllustrationDark from '@images/pages/auth-v2-two-steps-illustration-dark.png'
import authV2TwoStepsIllustrationLight from '@images/pages/auth-v2-two-steps-illustration-light.png'
const authThemeImg = useGenerateImageVariant(authV2TwoStepsIllustrationLight, authV2TwoStepsIllustrationDark, authV2TwoStepsIllustrationBorderedLight, authV2TwoStepsIllustrationBorderedDark, true)
const authThemeMask = useGenerateImageVariant(authV2MaskLight, authV2MaskDark)
definePage({ meta: { layout: 'blank' } })
const router = useRouter()
const otp = ref('')
const isOtpInserted = ref(false)
const onFinish = () => {
isOtpInserted.value = true
setTimeout(() => {
isOtpInserted.value = false
router.push('/')
}, 2000)
}
</script>
<template>
<RouterLink to="/">
<div class="auth-logo d-flex align-center gap-x-3">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
</RouterLink>
<div>
<VRow
class="auth-wrapper"
no-gutters
>
<VCol
md="8"
class="d-none d-md-flex position-relative"
>
<!-- Here your illustrator -->
<div class="d-flex align-center justify-end w-100 h-100 pa-10 pe-0">
<VImg
max-width="753"
:src="authThemeImg"
class="auth-illustration"
/>
</div>
<img
class="auth-footer-mask"
height="360"
:src="authThemeMask"
>
<div class="d-flex gap-x-2 auth-footer-tree">
<img
:src="tree2"
alt="tree image"
height="180"
width="48"
>
<img
:src="tree2"
alt="tree image"
height="120"
width="32"
class="align-self-end"
>
</div>
</VCol>
<VCol
cols="12"
md="4"
class="auth-card-v2 d-flex align-center justify-center"
style="background-color: rgb(var(--v-theme-surface));"
>
<VCard
flat
:max-width="500"
class="mt-12 mt-sm-0 pa-4"
>
<VCardText>
<h4 class="text-h4 mb-1">
Two Step Verification 💬
</h4>
<p class="mb-1">
We sent a verification code to your mobile. Enter the code from the mobile in the field below.
</p>
<h6 class="text-h6">
******1234
</h6>
</VCardText>
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<!-- email -->
<VCol cols="12">
<div>Type your 6 digit security code</div>
<VOtpInput
v-model="otp"
:disabled="isOtpInserted"
type="number"
class="pa-0"
@finish="onFinish"
/>
</VCol>
<!-- reset password -->
<VCol cols="12">
<VBtn
block
:loading="isOtpInserted"
:disabled="isOtpInserted"
type="submit"
>
Verify my account
</VBtn>
</VCol>
<!-- back to login -->
<VCol cols="12">
<div class="d-flex justify-center align-center flex-wrap">
<span class="me-1">Didn't get the code?</span>
<a href="#">Resend</a>
</div>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
// .auth-illustration{
// inset-block-end: 11.5rem;
// inset-inline-end: 1.25rem;
// }
</style>

View File

@@ -0,0 +1,82 @@
<script setup>
import miscMaskDark from '@images/misc/misc-mask-dark.png'
import miscMaskLight from '@images/misc/misc-mask-light.png'
import tree1 from '@images/misc/tree1.png'
import tree3 from '@images/misc/tree3.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
const authThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark)
definePage({ meta: { layout: 'blank' } })
</script>
<template>
<div class="auth-wrapper d-flex align-center justify-center pa-4">
<VCard
class="auth-card pa-sm-4 pa-md-7 pa-0"
max-width="460"
>
<VCardText>
<div class="d-flex align-center gap-x-3 justify-center mb-6">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
<h4 class="text-h4 mb-1">
Verify your email
</h4>
<p class="mb-5">
Account activation link sent to your email address: hello@example.com Please follow the link inside to continue.
</p>
<VBtn
block
to="/"
class="mb-5"
>
Skip for now
</VBtn>
<div class="d-flex align-center justify-center">
<span class="me-1">Didn't get the mail? </span><a href="#">Resend</a>
</div>
</VCardText>
</VCard>
<div class="d-flex gap-x-2 auth-footer-start-tree">
<img
class="d-none d-md-block"
:src="tree3"
:height="120"
:width="67"
>
<img
class="d-none d-md-block align-self-end"
:src="tree3"
:height="70"
:width="40"
>
</div>
<img
:src="tree1"
class="auth-footer-end-tree d-none d-md-block"
:width="97"
:height="210"
>
<!-- bg img -->
<img
class="auth-footer-mask d-none d-md-block"
:src="authThemeMask"
height="172"
>
</div>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,109 @@
<script setup>
import tree2 from '@images/misc/tree2.png'
import authV2MaskDark from '@images/pages/mask-v2-dark.png'
import authV2MaskLight from '@images/pages/mask-v2-light.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
import authV2VerifyEmailIllustrationBorderedDark from '@images/pages/auth-v2-verify-email-illustration-border-dark.png'
import authV2VerifyEmailIllustrationBorderedLight from '@images/pages/auth-v2-verify-email-illustration-border-light.png'
import authV2VerifyEmailIllustrationDark from '@images/pages/auth-v2-verify-email-illustration-dark.png'
import authV2VerifyEmailIllustrationLight from '@images/pages/auth-v2-verify-email-illustration-light.png'
const authThemeImg = useGenerateImageVariant(authV2VerifyEmailIllustrationLight, authV2VerifyEmailIllustrationDark, authV2VerifyEmailIllustrationBorderedLight, authV2VerifyEmailIllustrationBorderedDark, true)
const authThemeMask = useGenerateImageVariant(authV2MaskLight, authV2MaskDark)
definePage({ meta: { layout: 'blank' } })
</script>
<template>
<RouterLink to="/">
<div class="auth-logo d-flex align-center gap-x-3">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
</RouterLink>
<VRow
class="auth-wrapper"
no-gutters
>
<VCol
md="8"
class="d-none d-md-flex position-relative"
>
<!-- here your illustration -->
<div
class="d-flex align-center justify-end w-100 h-100 pa-10 "
:class="$vuetify.locale.isRtl ? 'pe-10' : 'pe-0'"
>
<VImg
max-width="808"
:src="authThemeImg"
class="auth-illustration"
/>
</div>
<img
class="auth-footer-mask"
height="360"
:src="authThemeMask"
>
<div class="d-flex gap-x-2 auth-footer-tree">
<img
:src="tree2"
alt="tree image"
height="180"
width="48"
>
<img
:src="tree2"
alt="tree image"
height="120"
width="32"
class="align-self-end"
>
</div>
</VCol>
<VCol
cols="12"
md="4"
class="auth-card-v2 d-flex align-center justify-center"
style="background-color: rgb(var(--v-theme-surface));"
>
<VCard
flat
:max-width="500"
class="mt-12 mt-sm-0 pa-4"
>
<VCardText>
<h4 class="text-h4 mb-1">
Verify your email
</h4>
<p class="mb-5">
Account activation link sent to your email address: hello@example.com Please follow the link inside to continue.
</p>
<VBtn
block
to="/"
class="mb-5"
>
Skip for now
</VBtn>
<div class="d-flex align-center justify-center">
<span class="me-1">Didn't get the mail? </span><a href="#">Resend</a>
</div>
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/pages/page-auth.scss";
</style>

View File

@@ -0,0 +1,179 @@
<script setup>
const tableData = [
{
action: 'Collapse',
icon: 'ri-arrow-up-s-line',
details: 'Collapse card content using collapse action.',
},
{
action: 'Refresh Content',
icon: 'ri-refresh-line',
details: 'Refresh your card content using refresh action.',
},
{
action: 'Remove Card',
icon: 'ri-close-line',
details: 'Remove card from page using remove card action',
},
]
const isLoading = ref(true)
setTimeout(() => {
isLoading.value = false
}, 3000)
const refetchData = hideOverlay => {
setTimeout(hideOverlay, 3000)
}
</script>
<template>
<VRow>
<VCol cols="12">
<!-- 👉 Main Card -->
<AppCardActions
title="Cards Actions"
@refresh="refetchData"
>
<VCardText>
<VTable>
<thead>
<tr>
<th scope="col">
ACTION
</th>
<th scope="col">
ICON
</th>
<th scope="col">
DETAILS
</th>
</tr>
</thead>
<tbody>
<tr
v-for="data in tableData"
:key="data.icon"
>
<td>
{{ data.action }}
</td>
<td>
<VIcon
size="20"
:icon="data.icon"
/>
</td>
<td>
{{ data.details }}
</td>
</tr>
</tbody>
</VTable>
</VCardText>
</AppCardActions>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Initial Load -->
<AppCardActions
v-model:loading="isLoading"
title="Initial Load"
no-actions
>
<VCardText>
<p>You can specifically add Initial Load action using <code>loading</code> prop</p>
<span>Refresh the page to see it again in action.</span>
</VCardText>
</AppCardActions>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Collapsible -->
<AppCardActions
action-collapsed
title="Collapsible"
>
<VCardText>
<p>You can specifically add collapsible action using <code>actionCollapse</code> prop</p>
<span>Click on <VIcon
size="20"
icon="ri-arrow-up-s-line"
/> icon to see it in action</span>
</VCardText>
</AppCardActions>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Refresh Content -->
<AppCardActions
title="Refresh Content"
action-refresh
@refresh="refetchData"
>
<VCardText>
<p>You can specifically add refresh action using <code>actionRefresh</code> prop</p>
<span>Click on <VIcon
size="20"
icon="ri-refresh-line"
/> icon to see it in action</span>
</VCardText>
</AppCardActions>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Remove Card -->
<AppCardActions
title="Remove Card"
action-remove
>
<VCardText>
<p>You can specifically add remove action using <code>actionRemove</code> prop</p>
<span>Click on <VIcon
size="20"
icon="ri-close-line"
/> icon to see it in action</span>
</VCardText>
</AppCardActions>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 Slots -->
<AppCardActions
title="Slots"
@refresh="refetchData"
>
<template #before-actions>
<VChip
class="me-3"
color="primary"
size="small"
>
3 Updates
</VChip>
</template>
<VCardText>
<p><code>app-card-actions</code> also provides <code>before-actions</code> and <code>after-actions</code> slot</p>
<span>You can find more details in our documentation</span>
</VCardText>
</AppCardActions>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,175 @@
<script setup>
import CardAdvanceActivityTimeline from '@/views/pages/cards/card-advance/CardActivityTimeline.vue'
import CardAdvanceAnalytics from '@/views/pages/cards/card-advance/CardAdvanceAnalytics.vue'
import CardAdvanceCafeBadilico from '@/views/pages/cards/card-advance/CardAdvanceCafeBadilico.vue'
import CardAdvanceDepositWithdraw from '@/views/pages/cards/card-advance/CardAdvanceDepositWithdraw.vue'
import CardAdvanceDeveloperMeetup from '@/views/pages/cards/card-advance/CardAdvanceDeveloperMeetup.vue'
import CardAdvanceFinanceSummary from '@/views/pages/cards/card-advance/CardAdvanceFinanceSummary.vue'
import CardAdvanceMeetingSchedule from '@/views/pages/cards/card-advance/CardAdvanceMeetingSchedule.vue'
import CardAdvanceSalesByCountries from '@/views/pages/cards/card-advance/CardAdvanceSalesByCountries.vue'
import CardAdvanceTeamMembers from '@/views/pages/cards/card-advance/CardAdvanceTeamMembers.vue'
import CardAdvanceTotalEarning from '@/views/pages/cards/card-advance/CardAdvanceTotalEarning.vue'
import CardAdvanceTransactions from '@/views/pages/cards/card-advance/CardAdvanceTransactions.vue'
import CardAdvanceUpgradeYourPlan from '@/views/pages/cards/card-advance/CardAdvanceUpgradeYourPlan.vue'
import CardAdvanceWebsiteStatistics from '@/views/pages/cards/card-advance/CardAdvanceWebsiteStatistics.vue'
import CardAdvancedAssignmentProgress from '@/views/pages/cards/card-advance/CardAdvancedAssignmentProgress.vue'
import CardAdvancedDeliveryPerformance from '@/views/pages/cards/card-advance/CardAdvancedDeliveryPerformance.vue'
import CardAdvancedOrderByCountries from '@/views/pages/cards/card-advance/CardAdvancedOrderByCountries.vue'
import CardAdvancedPopularInstructors from '@/views/pages/cards/card-advance/CardAdvancedPopularInstructors.vue'
import CardAdvancedTopCourses from '@/views/pages/cards/card-advance/CardAdvancedTopCourses.vue'
import CardAdvancedUpcomingWebinar from '@/views/pages/cards/card-advance/CardAdvancedUpcomingWebinar.vue'
</script>
<template>
<VRow class="match-height">
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvanceTransactions />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvanceUpgradeYourPlan />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvanceMeetingSchedule />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvancedTopCourses />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvancedUpcomingWebinar />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvancedAssignmentProgress />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvancedPopularInstructors />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvancedOrderByCountries />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvancedDeliveryPerformance />
</VCol>
<VCol
cols="12"
md="6"
lg="5"
>
<CardAdvanceTeamMembers />
</VCol>
<VCol
cols="12"
lg="7"
>
<CardAdvanceDepositWithdraw />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvanceTotalEarning />
</VCol>
<VCol
cols="12"
md="6"
lg="5"
>
<CardAdvanceFinanceSummary />
</VCol>
<VCol
cols="12"
md="6"
lg="3"
>
<CardAdvanceAnalytics />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvanceWebsiteStatistics />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvanceDeveloperMeetup />
</VCol>
<VCol
cols="12"
md="6"
lg="4"
>
<CardAdvanceSalesByCountries />
</VCol>
</VRow>
<VRow>
<VCol
cols="12"
md="8"
>
<CardAdvanceActivityTimeline />
</VCol>
<VCol
cols="12"
md="4"
>
<CardAdvanceCafeBadilico />
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,27 @@
<script setup>
import CardBasic from '@/views/pages/cards/card-basic/CardBasic.vue'
import CardNavigation from '@/views/pages/cards/card-basic/CardNavigation.vue'
import CardSolid from '@/views/pages/cards/card-basic/CardSolid.vue'
</script>
<template>
<div>
<p class="text-2xl mb-6">
Basic Cards
</p>
<CardBasic />
<p class="text-2xl mb-6 mt-14">
Navigation Cards
</p>
<CardNavigation />
<p class="text-2xl mt-14 mb-6 ">
Solid Cards
</p>
<CardSolid />
</div>
</template>

View File

@@ -0,0 +1,40 @@
<script setup>
import CardAward from '@/views/pages/cards/gamification/CardAward.vue'
import CardCongratulations from '@/views/pages/cards/gamification/CardCongratulations.vue'
import CardUpgrade from '@/views/pages/cards/gamification/CardUpgrade.vue'
import CardWelcome from '@/views/pages/cards/gamification/CardWelcome.vue'
</script>
<template>
<VRow>
<VCol
cols="12"
md="4"
>
<CardAward />
</VCol>
<VCol
cols="12"
md="8"
class="align-self-end mt-sm-10 mt-md-0"
>
<CardCongratulations />
</VCol>
<VCol
cols="12"
md="8"
class="align-self-end mt-sm-10 mt-md-0"
>
<CardWelcome />
</VCol>
<VCol
cols="12"
md="4"
>
<CardUpgrade />
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,302 @@
<script setup>
import CardStatisticsLogistics from '@/views/pages/cards/card-statistics/CardStatisticsLogistics.vue'
import CardStatisticsSessionsBarCharts from '@/views/pages/cards/card-statistics/CardStatisticsSessionsBarCharts.vue'
import CardStatisticsSessionsBarWithGapCharts from '@/views/pages/cards/card-statistics/CardStatisticsSessionsBarWithGapCharts.vue'
import CardStatisticsTotalGrowthAreaCharts from '@/views/pages/cards/card-statistics/CardStatisticsTotalGrowthAreaCharts.vue'
import CardStatisticsTotalProfitLineCharts from '@/views/pages/cards/card-statistics/CardStatisticsTotalProfitLineCharts.vue'
import CardStatisticsTotalRevenueLineCharts from '@/views/pages/cards/card-statistics/CardStatisticsTotalRevenueLineCharts.vue'
import CardStatisticsTotalRevenueRadialBarCharts from '@/views/pages/cards/card-statistics/CardStatisticsTotalRevenueRadialBarCharts.vue'
import CardStatisticsTotalSalesCharts from '@/views/pages/cards/card-statistics/CardStatisticsTotalSalesCharts.vue'
import CardStatisticsTransactions from '@/views/pages/cards/card-statistics/CardStatisticsTransactions.vue'
import illustration1 from '@images/cards/illustration-1.png'
import illustration2 from '@images/cards/illustration-2.png'
import illustration3 from '@images/cards/illustration-3.png'
import illustration4 from '@images/cards/illustration-4.png'
const statisticsHorizontal = [
{
title: 'New Customers',
color: 'primary',
icon: 'ri-group-line',
stats: 2856,
change: -11.9,
},
{
title: 'Total Revenue',
color: 'success',
icon: 'ri-money-dollar-circle-line',
stats: 28600,
change: 25.8,
},
{
title: 'New Transactions',
color: 'info',
icon: 'ri-pie-chart-2-line',
stats: 13600,
change: -42.4,
},
{
title: 'Total Profit',
color: 'warning',
icon: 'ri-bar-chart-line',
stats: 2856,
change: 18.2,
},
]
const statisticsWithIcon = [
{
title: 'Session',
value: '21,459',
change: 29,
desc: 'Total Users',
icon: 'ri-group-line',
iconColor: 'primary',
},
{
title: 'Paid Users',
value: '4,567',
change: 18,
desc: 'Last Week Analytics',
icon: 'ri-user-add-line',
iconColor: 'error',
},
{
title: 'Active Users',
value: '19,860',
change: -14,
desc: 'Last Week Analytics',
icon: 'ri-user-follow-line',
iconColor: 'success',
},
{
title: 'Pending Users',
value: '237',
change: 42,
desc: 'Last Week Analytics',
icon: 'ri-user-search-line',
iconColor: 'warning',
},
]
const statisticsVertical = [
{
title: 'New Project',
color: 'primary',
icon: 'ri-file-word-2-line',
stats: '862',
change: -18,
subtitle: 'Yearly Project',
},
{
title: 'Total Profit',
color: 'secondary',
icon: 'ri-pie-chart-2-line',
stats: '$25.6k',
change: 42,
subtitle: 'Weekly Project',
},
{
title: 'Revenue',
color: 'success',
icon: 'ri-money-dollar-circle-line',
stats: '$95.2k',
change: 12,
subtitle: 'Revenue Increase',
},
{
title: 'Logistics',
color: 'error',
icon: 'ri-car-line',
stats: '44.10k',
change: 12,
subtitle: 'Revenue Increase',
},
{
title: 'Reports',
color: 'warning',
icon: 'ri-file-chart-line',
stats: '268',
change: -8,
subtitle: 'System Bugs',
},
{
title: 'Transactions',
color: 'info',
icon: 'ri-bank-card-line',
stats: '12k',
change: 32,
subtitle: 'Daily Transactions',
},
]
const statisticsWithImages = [
{
title: 'Ratings',
subtitle: 'Year of 2021',
stats: '13k',
change: 38,
image: illustration1,
color: 'primary',
},
{
title: 'Sessions',
subtitle: 'Last Week',
stats: '24.5k',
change: -22,
image: illustration2,
color: 'secondary',
},
{
title: 'Customers',
subtitle: 'Daily Customers',
stats: '2,856',
change: 59,
image: illustration3,
color: 'info',
},
{
title: 'Total Orders',
subtitle: 'Last Month',
stats: '42.5k',
change: 26,
image: illustration4,
color: 'warning',
},
]
</script>
<template>
<VRow class="match-height">
<!-- 👉 Horizontal Cards -->
<VCol
v-for="statistics in statisticsHorizontal"
:key="statistics.title"
cols="12"
sm="6"
md="3"
>
<CardStatisticsHorizontal v-bind="statistics" />
</VCol>
<VCol cols="12">
<CardStatisticsLogistics />
</VCol>
<VCol
v-for="statistics in statisticsWithIcon"
:key="statistics.title"
cols="12"
sm="6"
md="3"
>
<CardStatisticsWithIcon v-bind="statistics" />
</VCol>
<!-- 👉 Transition Card -->
<VCol
cols="12"
md="8"
>
<CardStatisticsTransactions />
</VCol>
<!-- 👉 Total sales -->
<VCol
cols="12"
md="4"
>
<CardStatisticsTotalSalesCharts />
</VCol>
<!-- 👉 Vertical Cards -->
<VCol
v-for="statistics in statisticsVertical"
:key="statistics.title"
cols="12"
sm="4"
lg="2"
>
<CardStatisticsVertical v-bind="statistics" />
</VCol>
<!-- 👉 Blank Column for make space above the Images cards -->
<!-- 👉 Images Cards -->
<VCol
v-for="statistics in statisticsWithImages"
:key="statistics.title"
class="pt-8"
cols="12"
lg="3"
sm="6"
>
<CardStatisticsWithImages v-bind="statistics" />
</VCol>
<!-- 👉 Total Revenue Line Chart -->
<VCol
cols="12"
sm="6"
md="4"
lg="2"
>
<CardStatisticsTotalRevenueLineCharts />
</VCol>
<!-- 👉 Sessions Bar Chart with Gaps -->
<VCol
cols="12"
sm="6"
md="4"
lg="2"
>
<CardStatisticsSessionsBarWithGapCharts />
</VCol>
<!-- 👉 Total Growth Area Chart -->
<VCol
cols="12"
sm="6"
md="4"
lg="2"
>
<CardStatisticsTotalGrowthAreaCharts />
</VCol>
<!-- 👉 Total Revenue Radial Bar Chart -->
<VCol
cols="12"
sm="6"
md="4"
lg="2"
>
<CardStatisticsTotalRevenueRadialBarCharts />
</VCol>
<!-- 👉 Session Bar Chart -->
<VCol
cols="12"
sm="6"
md="4"
lg="2"
>
<CardStatisticsSessionsBarCharts />
</VCol>
<!-- 👉 Total Profit Line Chart -->
<VCol
cols="12"
sm="6"
md="4"
lg="2"
>
<CardStatisticsTotalProfitLineCharts />
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/libs/apex-chart.scss";
</style>

View File

@@ -0,0 +1,149 @@
<script setup>
import CardWidgetsActivityTimeline from '@/views/pages/cards/card-widgets/CardWidgetsActivityTimeline.vue'
import CardWidgetsAnalytics from '@/views/pages/cards/card-widgets/CardWidgetsAnalytics.vue'
import CardWidgetsDeliveryExceptions from '@/views/pages/cards/card-widgets/CardWidgetsDeliveryExceptions.vue'
import CardWidgetsPerformance from '@/views/pages/cards/card-widgets/CardWidgetsPerformance.vue'
import CardWidgetsRevenueReport from '@/views/pages/cards/card-widgets/CardWidgetsRevenueReport.vue'
import CardWidgetsSalesOverview from '@/views/pages/cards/card-widgets/CardWidgetsSalesOverview.vue'
import CardWidgetsSalesState from '@/views/pages/cards/card-widgets/CardWidgetsSalesState.vue'
import CardWidgetsTotalProfit from '@/views/pages/cards/card-widgets/CardWidgetsTotalProfit.vue'
import CardWidgetsTotalProfitRadialBar from '@/views/pages/cards/card-widgets/CardWidgetsTotalProfitRadialBar.vue'
import CardWidgetsTotalRevenue from '@/views/pages/cards/card-widgets/CardWidgetsTotalRevenue.vue'
import CardWidgetsTotalSales from '@/views/pages/cards/card-widgets/CardWidgetsTotalSales.vue'
import CardWidgetsTotalVisitors from '@/views/pages/cards/card-widgets/CardWidgetsTotalVisitors.vue'
import CardWidgetsTotalVisits from '@/views/pages/cards/card-widgets/CardWidgetsTotalVisits.vue'
import CardWidgetsWeeklyOverview from '@/views/pages/cards/card-widgets/CardWidgetsWeeklyOverview.vue'
import CardWidgetsWeeklySales from '@/views/pages/cards/card-widgets/CardWidgetsWeeklySales.vue'
import CardWidgetsInterestedTopics from '@/views/pages/cards/card-widgets/cardWidgetsInterestedTopics.vue'
</script>
<template>
<VRow>
<VCol
cols="12"
md="8"
>
<CardWidgetsTotalProfit />
</VCol>
<VCol
cols="12"
sm="6"
md="4"
>
<CardWidgetsTotalVisitors />
</VCol>
<VCol
cols="12"
sm="6"
md="4"
>
<CardWidgetsWeeklySales />
</VCol>
<VCol
cols="12"
sm="6"
md="4"
>
<CardWidgetsTotalRevenue />
</VCol>
<VCol
cols="12"
sm="6"
md="4"
>
<CardWidgetsWeeklyOverview />
</VCol>
<VCol
cols="12"
sm="6"
md="4"
>
<CardWidgetsPerformance />
</VCol>
<VCol
cols="12"
sm="6"
md="4"
>
<CardWidgetsAnalytics />
</VCol>
<VCol
cols="12"
sm="6"
md="4"
>
<CardWidgetsSalesState />
</VCol>
<VCol
cols="12"
sm="6"
md="3"
>
<CardWidgetsTotalProfitRadialBar />
</VCol>
<VCol
cols="12"
sm="4"
md="3"
>
<CardWidgetsTotalSales />
</VCol>
<VCol
cols="12"
sm="4"
md="3"
>
<CardWidgetsTotalVisits />
</VCol>
<VCol
cols="12"
sm="4"
md="3"
>
<CardWidgetsRevenueReport />
</VCol>
<VCol
cols="12"
md="6"
>
<CardWidgetsActivityTimeline />
</VCol>
<VCol
cols="12"
md="6"
>
<CardWidgetsSalesOverview />
</VCol>
<VCol
cols="12"
md="8"
>
<CardWidgetsInterestedTopics />
</VCol>
<VCol
cols="12"
md="4"
>
<CardWidgetsDeliveryExceptions />
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core-scss/template/libs/apex-chart.scss";
</style>

View File

@@ -0,0 +1,362 @@
<script setup>
const isCardAddDialogVisible = ref(false)
const isPricingPlanDialogVisible = ref(false)
const isEditAddressDialogVisible = ref(false)
const isTwoFactorDialogOpen = ref(false)
const isUserInfoEditDialogVisible = ref(false)
const isShareProjectDialogVisible = ref(false)
const isReferAndEarnDialogVisible = ref(false)
const isPaymentProvidersDialogVisible = ref(false)
const isAddPaymentMethodsDialogVisible = ref(false)
const isCreateAppVisible = ref(false)
const userData = {
id: 110,
fullName: 'Salena Kyle',
company: 'Wayne Enterprises',
firstName: 'Selena',
lastName: 'Kyle',
username: 'charwomen1940',
role: 'Admin',
currentPlan: 'Pro',
country: 'USA',
contact: '(829) 537-0057',
email: 'salenakyle@gmail.com',
isBillingAddress: true,
status: 'active',
taxId: 'Tax-8894',
language: ['English'],
}
</script>
<template>
<VRow>
<!-- 👉 Share Project Dialog -->
<VCol
cols="12"
sm="6"
md="4"
>
<VCard class="text-high-emphasis text-center">
<VCardText class="d-flex flex-column align-center gap-2">
<VIcon
size="28"
icon="ri-file-pdf-line"
/>
<h5 class="text-h5">
Share Project
</h5>
</VCardText>
<VCardText>
<p class="text-base clamp-text">
Quickly collect the credit card details, built in input mask and form validation support.
</p>
<VBtn @click="isShareProjectDialogVisible = !isShareProjectDialogVisible">
Show
</VBtn>
</VCardText>
<ShareProjectDialog v-model:isDialogVisible="isShareProjectDialogVisible" />
</VCard>
</VCol>
<!-- 👉 Add New Card Dialog -->
<VCol
cols="12"
sm="6"
md="4"
>
<VCard class="text-high-emphasis text-center">
<VCardText class="d-flex flex-column align-center gap-2">
<VIcon
size="28"
icon="ri-bank-card-line"
/>
<h5 class="text-h5">
Add New Card
</h5>
</VCardText>
<VCardText>
<p class="text-base clamp-text">
Quickly collect the credit card details, built in input mask and form validation support.
</p>
<VBtn @click="isCardAddDialogVisible = !isCardAddDialogVisible">
Show
</VBtn>
</VCardText>
<CardAddEditDialog v-model:isDialogVisible="isCardAddDialogVisible" />
</VCard>
</VCol>
<!-- 👉 Pricing Dialog -->
<VCol
cols="12"
sm="6"
md="4"
>
<VCard class="text-high-emphasis text-center">
<VCardText class="d-flex flex-column align-center gap-2">
<VIcon
size="28"
icon="ri-money-dollar-circle-line"
/>
<h5 class="text-h5">
Pricing
</h5>
</VCardText>
<VCardText>
<p class="text-base clamp-text">
Elegant pricing options dialog popup example, easy to use in any page.
</p>
<VBtn @click="isPricingPlanDialogVisible = !isPricingPlanDialogVisible">
Show
</VBtn>
</VCardText>
<!-- 👉 plan and pricing dialog -->
<PricingPlanDialog v-model:is-dialog-visible="isPricingPlanDialogVisible" />
</VCard>
</VCol>
<!-- 👉 Refer And Earn Dialog -->
<VCol
cols="12"
sm="6"
md="4"
>
<VCard class="text-high-emphasis text-center">
<VCardText class="d-flex flex-column align-center gap-2">
<VIcon
size="28"
icon="ri-gift-line"
/>
<h5 class="text-h5">
Refer & Earn
</h5>
</VCardText>
<VCardText>
<p class="text-base clamp-text">
Use Refer & Earn modal to encourage your exiting customers refer their friends & colleague.
</p>
<VBtn @click="isReferAndEarnDialogVisible = !isReferAndEarnDialogVisible">
Show
</VBtn>
</VCardText>
<!-- 👉 plan and pricing dialog -->
<ReferAndEarnDialog v-model:is-dialog-visible="isReferAndEarnDialogVisible" />
</VCard>
</VCol>
<!-- 👉 Add New Address Dialog -->
<VCol
cols="12"
sm="6"
md="4"
>
<VCard class="text-high-emphasis text-center">
<VCardText class="d-flex flex-column align-center gap-2">
<VIcon
size="28"
icon="ri-home-3-line"
/>
<h5 class="text-h5">
Add New Address
</h5>
</VCardText>
<VCardText>
<p class="text-base clamp-text">
Ready to use form to collect user address data with validation and custom input support.
</p>
<VBtn @click="isEditAddressDialogVisible = !isEditAddressDialogVisible">
Show
</VBtn>
</VCardText>
<!-- 👉 Edit Address dialog -->
<AddEditAddressDialog v-model:isDialogVisible="isEditAddressDialogVisible" />
</VCard>
</VCol>
<!-- 👉 Create app dialog -->
<VCol
cols="12"
sm="6"
md="4"
>
<VCard class="text-high-emphasis text-center">
<VCardText class="d-flex flex-column align-center gap-2">
<VIcon
size="28"
icon="ri-box-3-line"
/>
<h5 class="text-h5">
Create App
</h5>
</VCardText>
<VCardText>
<p class="text-base clamp-text">
Provide application data with this form to create the app dialog popup example, easy to use in any page.
</p>
<VBtn @click="isCreateAppVisible = !isCreateAppVisible">
Show
</VBtn>
</VCardText>
</VCard>
<CreateAppDialog v-model:is-dialog-visible="isCreateAppVisible" />
</VCol>
<!-- 👉 Two Factor Auth Dialog -->
<VCol
cols="12"
sm="6"
md="4"
>
<VCard class="text-high-emphasis text-center">
<VCardText class="d-flex flex-column align-center gap-2">
<VIcon
size="28"
icon="ri-lock-line"
/>
<h5 class="text-h5">
Two Factor Auth
</h5>
</VCardText>
<VCardText>
<p class="text-base clamp-text">
Enhance your application security by enabling two factor authentication.
</p>
<VBtn @click="isTwoFactorDialogOpen = !isTwoFactorDialogOpen">
Show
</VBtn>
</VCardText>
<!-- 👉 Enable One Time Password Dialog -->
<TwoFactorAuthDialog v-model:isDialogVisible="isTwoFactorDialogOpen" />
</VCard>
</VCol>
<!-- 👉 Edit User Info Dialog -->
<VCol
cols="12"
sm="6"
md="4"
>
<VCard class="text-high-emphasis text-center">
<VCardText class="d-flex flex-column align-center gap-2">
<VIcon
size="28"
icon="ri-user-line"
/>
<h5 class="text-h5">
Edit User Info
</h5>
</VCardText>
<VCardText>
<p class="text-base clamp-text">
Enhance your application security by enabling two factor authentication.
</p>
<VBtn @click="isUserInfoEditDialogVisible = !isUserInfoEditDialogVisible">
Show
</VBtn>
</VCardText>
<!-- 👉 Edit user info dialog -->
<UserInfoEditDialog
v-model:isDialogVisible="isUserInfoEditDialogVisible"
:user-data="userData"
/>
</VCard>
</VCol>
<!-- 👉 Payment Providers Dialog -->
<VCol
cols="12"
sm="6"
md="4"
>
<VCard class="text-high-emphasis text-center">
<VCardText class="d-flex flex-column align-center gap-2">
<VIcon
size="28"
icon="ri-bank-card-line"
/>
<h5 class="text-h5">
Payment Providers
</h5>
</VCardText>
<VCardText>
<p class="text-base clamp-text">
Elegant payment options modal popup example, easy to use in any page.
</p>
<VBtn @click="isPaymentProvidersDialogVisible = !isPaymentProvidersDialogVisible">
Show
</VBtn>
</VCardText>
<!-- 👉 plan and pricing dialog -->
<PaymentProvidersDialog v-model:is-dialog-visible="isPaymentProvidersDialogVisible" />
</VCard>
</VCol>
<!-- 👉 Add Payment Methods -->
<VCol
cols="12"
sm="6"
md="4"
>
<VCard class="text-high-emphasis text-center">
<VCardText class="d-flex flex-column align-center gap-2">
<VIcon
size="28"
icon="ri-bank-card-2-line"
/>
<h5 class="text-h5">
Add Payment Methods
</h5>
</VCardText>
<VCardText>
<p class="text-base clamp-text">
Elegant payment methods modal popup example, easy to use in any page.
</p>
<VBtn @click="isAddPaymentMethodsDialogVisible = !isAddPaymentMethodsDialogVisible">
Show
</VBtn>
</VCardText>
<!-- 👉 plan and pricing dialog -->
<AddPaymentMethodDialog v-model:is-dialog-visible="isAddPaymentMethodsDialogVisible" />
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
.pricing-dialog {
.pricing-title {
font-size: 1.5rem !important;
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More