Initial commit

This commit is contained in:
2025-08-04 16:33:07 +03:30
commit f798e8e35c
9595 changed files with 1208683 additions and 0 deletions

View File

@@ -0,0 +1,367 @@
<script setup>
const props = defineProps({
userData: {
type: Object,
required: true,
},
})
const standardPlan = {
plan: 'Standard',
price: 99,
benefits: [
'10 Users',
'Up to 10GB storage',
'Basic Support',
],
}
const isUserInfoEditDialogVisible = ref(false)
const isUpgradePlanDialogVisible = ref(false)
const resolveUserRoleVariant = role => {
if (role === 'subscriber')
return {
color: 'warning',
icon: 'tabler-user',
}
if (role === 'author')
return {
color: 'success',
icon: 'tabler-circle-check',
}
if (role === 'maintainer')
return {
color: 'primary',
icon: 'tabler-chart-pie-2',
}
if (role === 'editor')
return {
color: 'info',
icon: 'tabler-pencil',
}
if (role === 'admin')
return {
color: 'secondary',
icon: 'tabler-server-2',
}
return {
color: 'primary',
icon: 'tabler-user',
}
}
</script>
<template>
<VRow>
<!-- SECTION User Details -->
<VCol cols="12">
<VCard v-if="props.userData">
<VCardText class="text-center pt-12">
<!-- 👉 Avatar -->
<VAvatar
rounded
:size="100"
:color="!props.userData.avatar ? 'primary' : undefined"
:variant="!props.userData.avatar ? 'tonal' : undefined"
>
<VImg
v-if="props.userData.avatar"
:src="props.userData.avatar"
/>
<span
v-else
class="text-5xl font-weight-medium"
>
{{ avatarText(props.userData.fullName) }}
</span>
</VAvatar>
<!-- 👉 User fullName -->
<h5 class="text-h5 mt-4">
{{ props.userData.fullName }}
</h5>
<!-- 👉 Role chip -->
<VChip
label
:color="resolveUserRoleVariant(props.userData.role).color"
size="small"
class="text-capitalize mt-4"
>
{{ props.userData.role }}
</VChip>
</VCardText>
<VCardText>
<div class="d-flex justify-space-around gap-x-6 gap-y-2 flex-wrap mb-6">
<!-- 👉 Done task -->
<div class="d-flex align-center me-8">
<VAvatar
:size="40"
rounded
color="primary"
variant="tonal"
class="me-4"
>
<VIcon
icon="tabler-checkbox"
size="24"
/>
</VAvatar>
<div>
<h5 class="text-h5">
{{ `${(props.userData.taskDone / 1000).toFixed(2)}k` }}
</h5>
<span class="text-sm">Task Done</span>
</div>
</div>
<!-- 👉 Done Project -->
<div class="d-flex align-center me-4">
<VAvatar
:size="38"
rounded
color="primary"
variant="tonal"
class="me-4"
>
<VIcon
icon="tabler-briefcase"
size="24"
/>
</VAvatar>
<div>
<h5 class="text-h5">
{{ kFormatter(props.userData.projectDone) }}
</h5>
<span class="text-sm">Project Done</span>
</div>
</div>
</div>
<!-- 👉 Details -->
<h5 class="text-h5">
Details
</h5>
<VDivider class="my-4" />
<!-- 👉 User Details list -->
<VList class="card-list mt-2">
<VListItem>
<VListItemTitle>
<h6 class="text-h6">
Username:
<div class="d-inline-block text-body-1">
{{ props.userData.fullName }}
</div>
</h6>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle>
<span class="text-h6">
Billing Email:
</span>
<span class="text-body-1">
{{ props.userData.email }}
</span>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle>
<h6 class="text-h6">
Status:
<div class="d-inline-block text-body-1 text-capitalize">
{{ props.userData.status }}
</div>
</h6>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle>
<h6 class="text-h6">
Role:
<div class="d-inline-block text-capitalize text-body-1">
{{ props.userData.role }}
</div>
</h6>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle>
<h6 class="text-h6">
Tax ID:
<div class="d-inline-block text-body-1">
{{ props.userData.taxId }}
</div>
</h6>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle>
<h6 class="text-h6">
Contact:
<div class="d-inline-block text-body-1">
{{ props.userData.contact }}
</div>
</h6>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle>
<h6 class="text-h6">
Language:
<div class="d-inline-block text-body-1">
{{ props.userData.language }}
</div>
</h6>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle>
<h6 class="text-h6">
Country:
<div class="d-inline-block text-body-1">
{{ props.userData.country }}
</div>
</h6>
</VListItemTitle>
</VListItem>
</VList>
</VCardText>
<!-- 👉 Edit and Suspend button -->
<VCardText class="d-flex justify-center gap-x-4">
<VBtn
variant="elevated"
@click="isUserInfoEditDialogVisible = true"
>
Edit
</VBtn>
<VBtn
variant="tonal"
color="error"
>
Suspend
</VBtn>
</VCardText>
</VCard>
</VCol>
<!-- !SECTION -->
<!-- SECTION Current Plan -->
<VCol cols="12">
<VCard>
<VCardText class="d-flex">
<!-- 👉 Standard Chip -->
<VChip
label
color="primary"
size="small"
class="font-weight-medium"
>
Popular
</VChip>
<VSpacer />
<!-- 👉 Current Price -->
<div class="d-flex align-center">
<sup class="text-h5 text-primary mt-1">$</sup>
<h1 class="text-h1 text-primary">
99
</h1>
<sub class="mt-3"><h6 class="text-h6 font-weight-regular mb-n1">/ month</h6></sub>
</div>
</VCardText>
<VCardText>
<!-- 👉 Price Benefits -->
<VList class="card-list">
<VListItem
v-for="benefit in standardPlan.benefits"
:key="benefit"
>
<div class="d-flex align-center gap-x-2">
<VIcon
size="10"
color="secondary"
icon="tabler-circle-filled"
/>
<div class="text-medium-emphasis">
{{ benefit }}
</div>
</div>
</VListItem>
</VList>
<!-- 👉 Days -->
<div class="my-6">
<div class="d-flex justify-space-between mb-1">
<h6 class="text-h6">
Days
</h6>
<h6 class="text-h6">
26 of 30 Days
</h6>
</div>
<!-- 👉 Progress -->
<VProgressLinear
rounded
rounded-bar
:model-value="65"
color="primary"
/>
<p class="mt-1">
4 days remaining
</p>
</div>
<!-- 👉 Upgrade Plan -->
<div class="d-flex gap-4">
<VBtn
block
@click="isUpgradePlanDialogVisible = true"
>
Upgrade Plan
</VBtn>
</div>
</VCardText>
</VCard>
</VCol>
<!-- !SECTION -->
</VRow>
<!-- 👉 Edit user info dialog -->
<UserInfoEditDialog
v-model:is-dialog-visible="isUserInfoEditDialogVisible"
:user-data="props.userData"
/>
<!-- 👉 Upgrade plan dialog -->
<UserUpgradePlanDialog v-model:is-dialog-visible="isUpgradePlanDialogVisible" />
</template>
<style lang="scss" scoped>
.card-list {
--v-card-list-gap: 0.5rem;
}
.text-capitalize {
text-transform: capitalize !important;
}
</style>

View File

@@ -0,0 +1,261 @@
<script setup>
const searchQuery = ref('')
const selectedStatus = ref()
// Data table options
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
const updateOptions = options => {
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const isLoading = ref(false)
// 👉 headers
const headers = [
{
title: '#',
key: 'id',
},
{
title: 'Status',
key: 'trending',
sortable: false,
},
{
title: 'Total',
key: 'total',
},
{
title: 'Issued Date',
key: 'date',
},
{
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)
const resolveInvoiceStatusVariantAndIcon = status => {
if (status === 'Partial Payment')
return {
variant: 'success',
icon: 'tabler-check',
}
if (status === 'Paid')
return {
variant: 'warning',
icon: 'tabler-chart-pie',
}
if (status === 'Downloaded')
return {
variant: 'info',
icon: 'tabler-arrow-down',
}
if (status === 'Draft')
return {
variant: 'primary',
icon: 'tabler-folder',
}
if (status === 'Sent')
return {
variant: 'secondary',
icon: 'tabler-mail',
}
if (status === 'Past Due')
return {
variant: 'error',
icon: 'tabler-info-circle',
}
return {
variant: 'secondary',
icon: 'tabler-x',
}
}
const computedMoreList = computed(() => {
return paramId => [
{
title: 'Download',
value: 'download',
prependIcon: 'tabler-download',
},
{
title: 'Edit',
value: 'edit',
prependIcon: 'tabler-pencil',
to: {
name: 'apps-invoice-edit-id',
params: { id: paramId },
},
},
{
title: 'Duplicate',
value: 'duplicate',
prependIcon: 'tabler-layers-intersect',
},
]
})
const deleteInvoice = async id => {
await $api(`/apps/invoice/${ id }`, { method: 'DELETE' })
fetchInvoices()
}
</script>
<template>
<section v-if="invoices">
<VCard id="invoice-list">
<VCardText>
<div class="d-flex align-center justify-space-between flex-wrap gap-4">
<div class="text-h5">
Invoice List
</div>
<div class="d-flex align-center gap-x-4">
<AppSelect
:model-value="itemsPerPage"
:items="[
{ value: 10, title: '10' },
{ value: 25, title: '25' },
{ value: 50, title: '50' },
{ value: 100, title: '100' },
{ value: -1, title: 'All' },
]"
style="inline-size: 6.25rem;"
@update:model-value="itemsPerPage = parseInt($event, 10)"
/>
<!-- 👉 Export invoice -->
<VBtn
append-icon="tabler-upload"
variant="tonal"
color="secondary"
>
Export
</VBtn>
</div>
</div>
</VCardText>
<VDivider />
<!-- SECTION Datatable -->
<VDataTableServer
v-model:items-per-page="itemsPerPage"
v-model:page="page"
:loading="isLoading"
:items-length="totalInvoices"
:headers="headers"
:items="invoices"
item-value="total"
class="text-no-wrap text-sm 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>
<!-- Total -->
<template #item.total="{ item }">
${{ item.total }}
</template>
<!-- issued Date -->
<template #item.date="{ item }">
{{ item.issuedDate }}
</template>
<!-- Actions -->
<template #item.actions="{ item }">
<IconBtn @click="deleteInvoice(item.id)">
<VIcon icon="tabler-trash" />
</IconBtn>
<IconBtn :to="{ name: 'apps-invoice-preview-id', params: { id: item.id } }">
<VIcon icon="tabler-eye" />
</IconBtn>
<MoreBtn
:menu-list="computedMoreList(item.id)"
color="undefined"
item-props
/>
</template>
<template #bottom>
<TablePagination
v-model:page="page"
:items-per-page="itemsPerPage"
:total-items="totalInvoices"
/>
</template>
</VDataTableServer>
<!-- !SECTION -->
</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,424 @@
<script setup>
import UserInvoiceTable from './UserInvoiceTable.vue'
import avatar1 from '@images/avatars/avatar-1.png'
import avatar2 from '@images/avatars/avatar-2.png'
import avatar3 from '@images/avatars/avatar-3.png'
import avatar4 from '@images/avatars/avatar-4.png'
import avatar5 from '@images/avatars/avatar-5.png'
import avatar6 from '@images/avatars/avatar-6.png'
import avatar7 from '@images/avatars/avatar-7.png'
import avatar8 from '@images/avatars/avatar-8.png'
import figma from '@images/icons/project-icons/figma.png'
import html5 from '@images/icons/project-icons/html5.png'
import pdf from '@images/icons/project-icons/pdf.png'
import python from '@images/icons/project-icons/python.png'
import react from '@images/icons/project-icons/react.png'
import sketch from '@images/icons/project-icons/sketch.png'
import vue from '@images/icons/project-icons/vue.png'
import xamarin from '@images/icons/project-icons/xamarin.png'
const projectTableHeaders = [
{
title: 'PROJECT',
key: 'project',
},
{
title: 'LEADER',
key: 'leader',
},
{
title: 'Team',
key: 'team',
},
{
title: 'PROGRESS',
key: 'progress',
},
{
title: 'Action',
key: 'Action',
sortable: false,
},
]
const search = ref('')
const options = ref({
itemsPerPage: 5,
page: 1,
})
const projects = [
{
logo: react,
name: 'BGC eCommerce App',
project: 'React Project',
leader: 'Eileen',
progress: 78,
hours: '18:42',
team: [
avatar1,
avatar8,
avatar6,
],
extraMembers: 3,
},
{
logo: figma,
name: 'Falcon Logo Design',
project: 'Figma Project',
leader: 'Owen',
progress: 25,
hours: '20:42',
team: [
avatar5,
avatar2,
],
},
{
logo: vue,
name: 'Dashboard Design',
project: 'Vuejs Project',
leader: 'Keith',
progress: 62,
hours: '120:87',
team: [
avatar8,
avatar2,
avatar1,
],
},
{
logo: xamarin,
name: 'Foodista mobile app',
project: 'Xamarin Project',
leader: 'Merline',
progress: 8,
hours: '120:87',
team: [
avatar3,
avatar4,
avatar7,
],
extraMembers: 8,
},
{
logo: python,
name: 'Dojo Email App',
project: 'Python Project',
leader: 'Harmonia',
progress: 51,
hours: '230:10',
team: [
avatar4,
avatar3,
avatar1,
],
extraMembers: 5,
},
{
logo: sketch,
name: 'Blockchain Website',
project: 'Sketch Project',
leader: 'Allyson',
progress: 92,
hours: '342:41',
team: [
avatar1,
avatar8,
],
},
{
logo: html5,
name: 'Hoffman Website',
project: 'HTML Project',
leader: 'Georgie',
progress: 80,
hours: '12:45',
team: [
avatar1,
avatar8,
avatar6,
],
},
]
const moreList = [
{
title: 'Download',
value: 'Download',
},
{
title: 'Delete',
value: 'Delete',
},
{
title: 'View',
value: 'View',
},
]
</script>
<template>
<VRow>
<VCol cols="12">
<VCard>
<VCardText class="d-flex justify-space-between align-center flex-wrap gap-4">
<h5 class="text-h5">
User's Projects List
</h5>
<div style="inline-size: 250px;">
<AppTextField
v-model="search"
placeholder="Search Project"
/>
</div>
</VCardText>
<VDivider />
<!-- 👉 User Project List Table -->
<!-- SECTION Datatable -->
<VDataTable
v-model:page="options.page"
:headers="projectTableHeaders"
:items-per-page="options.itemsPerPage"
:items="projects"
item-value="name"
hide-default-footer
:search="search"
show-select
class="text-no-wrap"
>
<!-- projects -->
<template #item.project="{ item }">
<div class="d-flex align-center gap-x-3">
<VAvatar
:size="34"
:image="item.logo"
/>
<div>
<h6 class="text-h6 text-no-wrap">
{{ item.name }}
</h6>
<div class="text-body-2">
{{ item.project }}
</div>
</div>
</div>
</template>
<template #item.leader="{ item }">
<div class="text-base text-high-emphasis">
{{ item.leader }}
</div>
</template>
<!-- Team -->
<template #item.team="{ item }">
<div class="d-flex">
<div class="v-avatar-group">
<VAvatar
v-for="(data, index) in item.team"
:key="index"
size="26"
>
<VImg :src="data" />
</VAvatar>
<VAvatar
v-if="item.extraMembers"
:color="$vuetify.theme.current.dark ? '#373b50' : '#eeedf0'"
:size="26"
>
<div class="text-caption text-high-emphasis">
+{{ item.extraMembers }}
</div>
</VAvatar>
</div>
</div>
</template>
<!-- Progress -->
<template #item.progress="{ item }">
<div class="d-flex align-center gap-3">
<div class="flex-grow-1">
<VProgressLinear
:height="6"
:model-value="item.progress"
color="primary"
rounded
/>
</div>
<div class="text-body-1 text-high-emphasis">
{{ item.progress }}%
</div>
</div>
</template>
<!-- Action -->
<template #item.Action>
<MoreBtn :menu-list="moreList" />
</template>
<!-- TODO Refactor this after vuetify provides proper solution for removing default footer -->
<template #bottom>
<TablePagination
v-model:page="options.page"
:items-per-page="options.itemsPerPage"
:total-items="projects.length"
/>
</template>
</VDataTable>
<!-- !SECTION -->
</VCard>
</VCol>
<VCol cols="12">
<!-- 👉 User Activity timeline -->
<VCard title="User Activity Timeline">
<VCardText>
<VTimeline
side="end"
align="start"
line-inset="8"
truncate-line="start"
density="compact"
>
<!-- SECTION Timeline Item: Flight -->
<VTimelineItem
dot-color="primary"
size="x-small"
>
<!-- 👉 Header -->
<div class="d-flex justify-space-between align-center gap-2 flex-wrap mb-2">
<span class="app-timeline-title">
12 Invoices have been paid
</span>
<span class="app-timeline-meta">12 min ago</span>
</div>
<!-- 👉 Content -->
<div class="app-timeline-text mt-1">
Invoices have been paid to the company
</div>
<div class="d-inline-flex align-center timeline-chip mt-2">
<img
:src="pdf"
height="20"
class="me-2"
alt="img"
>
<span class="app-timeline-text font-weight-medium">
invoice.pdf
</span>
</div>
</VTimelineItem>
<!-- !SECTION -->
<!-- SECTION Timeline Item: Interview Schedule -->
<VTimelineItem
size="x-small"
dot-color="success"
>
<!-- 👉 Header -->
<div class="d-flex justify-space-between align-center flex-wrap mb-2">
<div class="app-timeline-title">
Client Meeting
</div>
<span class="app-timeline-meta">45 min ago</span>
</div>
<div class="app-timeline-text mt-1">
Project meeting with john @10:15am
</div>
<!-- 👉 Person -->
<div class="d-flex justify-space-between align-center flex-wrap">
<!-- 👉 Avatar & Personal Info -->
<div class="d-flex align-center mt-2">
<VAvatar
size="32"
class="me-2"
:image="avatar1"
/>
<div class="d-flex flex-column">
<p class="text-sm font-weight-medium text-medium-emphasis mb-0">
Lester McCarthy (Client)
</p>
<span class="text-sm">CEO of Pixinvent</span>
</div>
</div>
</div>
</VTimelineItem>
<!-- !SECTION -->
<!-- SECTION Design Review -->
<VTimelineItem
size="x-small"
dot-color="info"
>
<!-- 👉 Header -->
<div class="d-flex justify-space-between align-center flex-wrap mb-2">
<span class="app-timeline-title">
Create a new project for client
</span>
<span class="app-timeline-meta">2 Day Ago</span>
</div>
<!-- 👉 Content -->
<p class="app-timeline-text mt-1 mb-2">
6 team members in a project
</p>
<div class="v-avatar-group demo-avatar-group">
<VAvatar :size="40">
<VImg :src="avatar1" />
<VTooltip
activator="parent"
location="top"
>
John Doe
</VTooltip>
</VAvatar>
<VAvatar :size="40">
<VImg :src="avatar2" />
<VTooltip
activator="parent"
location="top"
>
Jennie Obrien
</VTooltip>
</VAvatar>
<VAvatar :size="40">
<VImg :src="avatar3" />
<VTooltip
activator="parent"
location="top"
>
Peter Harper
</VTooltip>
</VAvatar>
<VAvatar
:size="40"
:color="$vuetify.theme.current.dark ? '#373b50' : '#eeedf0'"
>
+3
</VAvatar>
</div>
</VTimelineItem>
<!-- !SECTION -->
</VTimeline>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<UserInvoiceTable />
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,425 @@
<script setup>
import americanExpress from '@images/icons/payments/american-express.png'
import mastercard from '@images/icons/payments/mastercard.png'
import visa from '@images/icons/payments/visa.png'
const isUpgradePlanDialogVisible = ref(false)
const currentCardDetails = ref()
const isCardEditDialogVisible = ref(false)
const isCardAddDialogVisible = ref(false)
const isEditAddressDialogVisible = ref(false)
const openEditCardDialog = cardDetails => {
currentCardDetails.value = cardDetails
isCardEditDialogVisible.value = true
}
const creditCards = [
{
name: 'Tom McBride',
number: '4851234567899865',
expiry: '12/24',
isPrimary: true,
isExpired: false,
type: 'mastercard',
cvv: '123',
image: mastercard,
},
{
name: 'Mildred Wagner',
number: '5531234567895678',
expiry: '02/24',
isPrimary: false,
isExpired: false,
type: 'visa',
cvv: '456',
image: visa,
},
{
name: 'Lester Jennings',
number: '5531234567890002',
expiry: '08/20',
isPrimary: false,
isExpired: true,
type: 'visa',
cvv: '456',
image: americanExpress,
},
]
const currentAddress = {
companyName: 'Pixinvent',
billingEmail: 'gertrude@gmail.com',
taxID: 'TAX-875623',
vatNumber: 'SDF754K77',
address: '100 Water Plant Avenue, Building 1303 Wake Island',
contact: '+1(609) 933-44-22',
country: 'USA',
state: 'Queensland',
zipCode: 403114,
}
const currentBillingAddress = {
firstName: 'Shamus',
lastName: 'Tuttle',
selectedCountry: 'USA',
addressLine1: '45 Rocker Terrace',
addressLine2: 'Latheronwheel',
landmark: 'KW5 8NW, London',
contact: '+1 (609) 972-22-22',
country: 'USA',
city: 'London',
state: 'London',
zipCode: 110001,
}
</script>
<template>
<VRow>
<!-- 👉 Current Plan -->
<VCol cols="12">
<VCard title="Current Plan">
<VCardText>
<VRow>
<VCol
cols="12"
md="6"
order-md="1"
order="2"
>
<h6 class="text-h6 mb-1">
Your Current Plan is Basic
</h6>
<p>
A simple start for everyone
</p>
<h6 class="text-h6 mb-1">
Active until Dec 09, 2021
</h6>
<p>
We will send you a notification upon Subscription expiration
</p>
<h6 class="text-h6 mb-1">
<span class="d-inline-block me-2">$99 Per Month</span>
<VChip
color="primary"
size="small"
label
>
Popular
</VChip>
</h6>
<p class="mb-0">
Standard plan for small to medium businesses
</p>
</VCol>
<VCol
cols="12"
md="6"
order-md="2"
order="1"
>
<!-- 👉 Alert -->
<VAlert
color="warning"
variant="tonal"
>
<VAlertTitle class="mb-1">
We need your attention!
</VAlertTitle>
<div class="text-base">
Your plan requires update
</div>
</VAlert>
<!-- 👉 Progress -->
<div class="d-flex justify-space-between font-weight-bold mt-4 mb-2">
<h6 class="text-h6">
Days
</h6>
<h6 class="text-h6">
26 of 30 Days
</h6>
</div>
<VProgressLinear
rounded
color="primary"
:height="10"
:model-value="75"
/>
<p class="text-sm mt-1">
Your plan requires update
</p>
</VCol>
<VCol
cols="12"
order="3"
>
<div class="d-flex flex-wrap gap-4">
<VBtn @click="isUpgradePlanDialogVisible = true">
upgrade plan
</VBtn>
<VBtn
color="error"
variant="tonal"
>
Cancel Subscription
</VBtn>
</div>
</VCol>
</VRow>
</VCardText>
</VCard>
</VCol>
<!-- 👉 Payment Methods -->
<VCol cols="12">
<VCard title="Payment Methods">
<template #append>
<VBtn
prepend-icon="tabler-plus"
size="small"
@click="isCardAddDialogVisible = !isCardAddDialogVisible"
>
Add Card
</VBtn>
</template>
<VCardText class="d-flex flex-column gap-y-4">
<VCard
v-for="card in creditCards"
:key="card.name"
border
flat
>
<VCardText class="d-flex flex-sm-row flex-column gap-6 justify-space-between">
<div class="text-no-wrap">
<img
:src="card.image"
:height="25"
>
<div class="my-2 d-flex gap-x-2 align-center">
<h6 class="text-h6">
{{ card.name }}
</h6>
<VChip
v-if="card.isPrimary || card.isExpired"
label
:color="card.isPrimary ? 'primary' : card.isExpired ? 'error' : 'secondary'"
size="small"
>
{{ card.isPrimary ? 'Popular' : card.isExpired ? 'Expired' : '' }}
</VChip>
</div>
<div class="text-body-1">
**** **** **** {{ card.number.substring(card.number.length - 4) }}
</div>
</div>
<div class="d-flex flex-column text-sm-end gap-y-4">
<div class="order-sm-0 order-1">
<VBtn
variant="tonal"
size="small"
class="me-4"
@click="openEditCardDialog(card)"
>
Edit
</VBtn>
<VBtn
color="error"
variant="tonal"
size="small"
>
Delete
</VBtn>
</div>
<div class="order-sm-1 order-0 text-sm">
Card expires at {{ card.expiry }}
</div>
</div>
</VCardText>
</VCard>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<!-- 👉 Billing Address -->
<VCard title="Billing Address">
<template #append>
<VBtn
size="small"
prepend-icon="tabler-plus"
@click="isEditAddressDialogVisible = !isEditAddressDialogVisible"
>
Edit Address
</VBtn>
</template>
<VCardText>
<VRow>
<VCol
cols="12"
lg="6"
>
<VTable class="billing-address-table">
<tr>
<td>
<h6 class="text-h6 text-no-wrap mb-2">
Company Name:
</h6>
</td>
<td>
<p class="text-body-1 mb-2">
{{ currentAddress.companyName }}
</p>
</td>
</tr>
<tr>
<td>
<h6 class="text-h6 text-no-wrap mb-2">
Billing Email:
</h6>
</td>
<td>
<p class="text-body-1 mb-2">
{{ currentAddress.billingEmail }}
</p>
</td>
</tr>
<tr>
<td>
<h6 class="text-h6 text-no-wrap mb-2">
Tax ID:
</h6>
</td>
<td>
<p class="text-body-1 mb-2">
{{ currentAddress.taxID }}
</p>
</td>
</tr>
<tr>
<td>
<h6 class="text-h6 text-no-wrap mb-2">
VAT Number:
</h6>
</td>
<td>
<p class="text-body-1 mb-2">
{{ currentAddress.vatNumber }}
</p>
</td>
</tr>
<tr>
<td class="d-flex align-baseline">
<h6 class="text-h6 text-no-wrap">
Billing Address:
</h6>
</td>
<td>
<p class="text-body-1 mb-2">
{{ currentAddress.address }}
</p>
</td>
</tr>
</VTable>
</VCol>
<VCol
cols="12"
lg="6"
>
<VTable class="billing-address-table">
<tr>
<td>
<h6 class="text-h6 text-no-wrap mb-2">
Contact:
</h6>
</td>
<td>
<p class="text-body-1 mb-2">
{{ currentAddress.contact }}
</p>
</td>
</tr>
<tr>
<td>
<h6 class="text-h6 text-no-wrap mb-2">
Country:
</h6>
</td>
<td>
<p class="text-body-1 mb-2">
{{ currentAddress.country }}
</p>
</td>
</tr>
<tr>
<td>
<h6 class="text-h6 text-no-wrap mb-2">
State:
</h6>
</td>
<td>
<p class="text-body-1 mb-2">
{{ currentAddress.state }}
</p>
</td>
</tr>
<tr>
<td>
<h6 class="text-h6 text-no-wrap mb-2">
Zip Code:
</h6>
</td>
<td>
<p class="text-body-1 mb-2">
{{ currentAddress.zipCode }}
</p>
</td>
</tr>
</VTable>
</VCol>
</VRow>
</VCardText>
</VCard>
</VCol>
</VRow>
<!-- 👉 Edit Card Dialog -->
<CardAddEditDialog
v-model:is-dialog-visible="isCardEditDialogVisible"
:card-details="currentCardDetails"
/>
<!-- 👉 Add Card Dialog -->
<CardAddEditDialog v-model:is-dialog-visible="isCardAddDialogVisible" />
<!-- 👉 Edit Address dialog -->
<AddEditAddressDialog
v-model:is-dialog-visible="isEditAddressDialogVisible"
:billing-address="currentBillingAddress"
/>
<!-- 👉 Upgrade plan dialog -->
<UserUpgradePlanDialog v-model:is-dialog-visible="isUpgradePlanDialogVisible" />
</template>
<style lang="scss">
.billing-address-table {
tr {
td:first-child {
inline-size: 148px;
}
}
}
</style>

View File

@@ -0,0 +1,181 @@
<script setup>
import asana from '@images/icons/brands/asana.png'
import behance from '@images/icons/brands/behance.png'
import dribbble from '@images/icons/brands/dribbble.png'
import facebook from '@images/icons/brands/facebook.png'
import github from '@images/icons/brands/github.png'
import google from '@images/icons/brands/google.png'
import linkedin from '@images/icons/brands/linkedin.png'
import mailchimp from '@images/icons/brands/mailchimp.png'
import slack from '@images/icons/brands/slack.png'
import twitter from '@images/icons/brands/twitter.png'
const connectedAccounts = ref([
{
img: google,
title: 'Google',
text: 'Calendar and contacts',
connected: true,
},
{
img: slack,
title: 'Slack',
text: 'Communication',
connected: false,
},
{
img: github,
title: 'GitHub',
text: 'Manage your Git repositories',
connected: true,
},
{
img: mailchimp,
title: 'Mailchimp',
text: 'Email marketing service',
connected: false,
},
{
img: asana,
title: 'Asana',
text: 'Communication',
connected: false,
},
])
const socialAccounts = ref([
{
img: facebook,
title: 'Facebook',
connected: false,
},
{
img: twitter,
title: 'Twitter',
link: 'https://twitter.com/pixinvents',
username: '@Pixinvent',
connected: true,
},
{
img: linkedin,
title: 'LinkedIn',
link: 'https://www.linkedin.com/company/pixinvent',
username: '@Pixinvent',
connected: true,
},
{
img: dribbble,
title: 'Dribbble',
connected: false,
},
{
img: behance,
title: 'Behance',
connected: false,
},
])
</script>
<template>
<VRow>
<!-- 👉 connected accounts -->
<VCol cols="12">
<VCard
title="Connected Accounts"
subtitle="Display content from your connected accounts on your site"
>
<VCardText>
<VList class="card-list">
<VListItem
v-for="account in connectedAccounts"
:key="account.title"
:subtitle="account.text"
>
<template #title>
<h6 class="text-h6">
{{ account.title }}
</h6>
</template>
<template #prepend>
<VAvatar
start
:size="36"
:image="account.img"
class="me-1"
/>
</template>
<template #append>
<VSwitch
v-model="account.connected"
density="compact"
class="me-1"
/>
</template>
</VListItem>
</VList>
</VCardText>
</VCard>
</VCol>
<!-- 👉 social accounts -->
<VCol cols="12">
<VCard
title="Social Accounts"
subtitle="Display content from social accounts on your site"
>
<VCardText>
<VList class="card-list">
<VListItem
v-for="(account) in socialAccounts"
:key="account.title"
>
<h6 class="text-h6">
{{ account.title }}
</h6>
<template #prepend>
<VAvatar
start
size="36"
rounded="0"
class="me-1"
:image="account.img"
/>
</template>
<VListItemSubtitle v-if="account.connected">
<a
:href="account.link"
target="_blank"
rel="noopener noreferrer"
>
{{ account.username }}
</a>
</VListItemSubtitle>
<VListItemSubtitle v-else>
Not connected
</VListItemSubtitle>
<template #append>
<IconBtn
variant="tonal"
:color="account.connected ? 'error' : 'secondary'"
class="rounded"
>
<VIcon :icon="account.connected ? 'tabler-trash' : 'tabler-link'" />
</IconBtn>
</template>
</VListItem>
</VList>
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss" scoped>
.card-list {
--v-card-list-gap: 16px;
}
</style>

View File

@@ -0,0 +1,89 @@
<script setup>
const notifications = ref([
{
type: 'New for you',
email: true,
browser: false,
app: false,
},
{
type: 'Account activity',
email: false,
browser: true,
app: true,
},
{
type: 'A new browser used to sign in',
email: true,
browser: true,
app: true,
},
{
type: 'A new device is linked',
email: false,
browser: true,
app: false,
},
])
</script>
<template>
<VCard
class="user-tab-notification"
title="Notifications"
subtitle="You will receive notification for the below selected items."
>
<VCardText class="px-0">
<VDivider />
<VTable class="text-no-wrap">
<thead>
<tr>
<th scope="col">
TYPE
</th>
<th scope="col">
EMAIL
</th>
<th scope="col">
BROWSER
</th>
<th scope="col">
APP
</th>
</tr>
</thead>
<tbody>
<tr
v-for="notification in notifications"
:key="notification.type"
>
<td class="text-high-emphasis">
{{ notification.type }}
</td>
<td>
<VCheckbox v-model="notification.email" />
</td>
<td>
<VCheckbox v-model="notification.browser" />
</td>
<td>
<VCheckbox v-model="notification.app" />
</td>
</tr>
</tbody>
</VTable>
<VDivider />
</VCardText>
<VCardText class="d-flex flex-wrap gap-4">
<VBtn>Save changes</VBtn>
<VBtn
color="secondary"
variant="tonal"
>
Discard
</VBtn>
</VCardText>
</VCard>
</template>

View File

@@ -0,0 +1,188 @@
<script setup>
const isNewPasswordVisible = ref(false)
const isConfirmPasswordVisible = ref(false)
const smsVerificationNumber = ref('+1(968) 819-2547')
const isTwoFactorDialogOpen = ref(false)
const recentDeviceHeader = [
{
title: 'BROWSER',
key: 'browser',
},
{
title: 'DEVICE',
key: 'device',
},
{
title: 'LOCATION',
key: 'location',
},
{
title: 'RECENT ACTIVITY',
key: 'activity',
},
]
const recentDevices = [
{
browser: ' Chrome on Windows',
icon: 'tabler-brand-windows',
color: 'info',
device: 'HP Spectre 360',
location: 'Switzerland',
activity: '10, July 2021 20:07',
},
{
browser: 'Chrome on Android',
icon: 'tabler-brand-android',
color: 'success',
device: 'Oneplus 9 Pro',
location: 'Dubai',
activity: '14, July 2021 15:15',
},
{
browser: 'Chrome on macOS',
icon: 'tabler-brand-apple',
color: 'secondary',
device: 'Apple iMac',
location: 'India',
activity: '16, July 2021 16:17',
},
{
browser: 'Chrome on iPhone',
icon: 'tabler-device-mobile',
color: 'error',
device: 'iPhone 12x',
location: 'Australia',
activity: '13, July 2021 10:10',
},
]
</script>
<template>
<VRow>
<VCol cols="12">
<!-- 👉 Change password -->
<VCard title="Change Password">
<VCardText>
<VAlert
closable
variant="tonal"
color="warning"
class="mb-4"
title="Ensure that these requirements are met"
text="Minimum 8 characters long, uppercase & symbol"
/>
<VForm @submit.prevent="() => { }">
<VRow>
<VCol
cols="12"
md="6"
>
<AppTextField
label="New Password"
placeholder="············"
:type="isNewPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isNewPasswordVisible ? 'tabler-eye-off' : 'tabler-eye'"
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<AppTextField
label="Confirm Password"
autocomplete="confirm-password"
placeholder="············"
:type="isConfirmPasswordVisible ? 'text' : 'password'"
:append-inner-icon="isConfirmPasswordVisible ? 'tabler-eye-off' : 'tabler-eye'"
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
/>
</VCol>
<VCol cols="12">
<VBtn type="submit">
Change Password
</VBtn>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<!-- 👉 Two step verification -->
<VCard
title="Two-steps verification"
subtitle="Keep your account secure with authentication step."
>
<VCardText>
<div class="text-h6 mb-1">
SMS
</div>
<AppTextField placeholder="+1(968) 819-2547">
<template #append>
<IconBtn color="secondary">
<VIcon
icon="tabler-edit"
size="22"
/>
</IconBtn>
<IconBtn color="secondary">
<VIcon
icon="tabler-user-plus"
size="22"
/>
</IconBtn>
</template>
</AppTextField>
<p class="mb-0 mt-4">
Two-factor authentication adds an additional layer of security to your account by requiring more than just a password to log in. <a
href="javascript:void(0)"
class="text-decoration-none"
>Learn more</a>.
</p>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<!-- 👉 Recent devices -->
<VCard title="Recent devices">
<VDivider />
<VDataTable
:items="recentDevices"
:headers="recentDeviceHeader"
hide-default-footer
class="text-no-wrap"
>
<template #item.browser="{ item }">
<div class="d-flex align-center gap-x-4">
<VIcon
:icon="item.icon"
:color="item.color"
:size="22"
/>
<div class="text-body-1 text-high-emphasis">
{{ item.browser }}
</div>
</div>
</template>
<!-- TODO Refactor this after vuetify provides proper solution for removing default footer -->
<template #bottom />
</VDataTable>
</VCard>
</VCol>
</VRow>
<!-- 👉 Enable One Time Password Dialog -->
<TwoFactorAuthDialog
v-model:is-dialog-visible="isTwoFactorDialogOpen"
:sms-code="smsVerificationNumber"
/>
</template>