initial commit

This commit is contained in:
2026-06-23 13:28:38 +07:00
commit 966ed115b2
590 changed files with 111412 additions and 0 deletions
+259
View File
@@ -0,0 +1,259 @@
<script setup>
import safeBoxWithGoldenCoin from '@images/misc/3d-safe-box-with-golden-dollar-coins.png'
import spaceRocket from '@images/misc/3d-space-rocket-with-smoke.png'
import dollarCoinPiggyBank from '@images/misc/dollar-coins-flying-pink-piggy-bank.png'
const props = defineProps({
title: {
type: String,
required: false,
},
xs: {
type: [
Number,
String,
],
required: false,
},
sm: {
type: [
Number,
String,
],
required: false,
},
md: {
type: [
String,
Number,
],
required: false,
},
lg: {
type: [
String,
Number,
],
required: false,
},
xl: {
type: [
String,
Number,
],
required: false,
},
})
const annualMonthlyPlanPriceToggler = ref(true)
const pricingPlans = [
{
name: 'Basic',
tagLine: 'A simple start for everyone',
logo: dollarCoinPiggyBank,
monthlyPrice: 0,
yearlyPrice: 0,
isPopular: false,
current: true,
features: [
'100 responses a month',
'Unlimited forms and surveys',
'Unlimited fields',
'Basic form creation tools',
'Up to 2 subdomains',
],
},
{
name: 'Standard',
tagLine: 'For small to medium businesses',
logo: safeBoxWithGoldenCoin,
monthlyPrice: 42,
yearlyPrice: 460,
isPopular: true,
current: false,
features: [
'Unlimited responses',
'Unlimited forms and surveys',
'Instagram profile page',
'Google Docs integration',
'Custom “Thank you” page',
],
},
{
name: 'Enterprise',
tagLine: 'Solution for big organizations',
logo: spaceRocket,
monthlyPrice: 84,
yearlyPrice: 690,
isPopular: false,
current: false,
features: [
'PayPal payments',
'Logic Jumps',
'File upload with 5GB storage',
'Custom domain support',
'Stripe integration',
],
},
]
</script>
<template>
<!-- 👉 Title and subtitle -->
<div class="text-center">
<h4 class="text-h2 pricing-title mb-4">
{{ props.title ? props.title : 'Pricing Plans' }}
</h4>
<p class="mb-0">
All plans include 40+ advanced tools and features to boost your product.
</p>
<p>Choose the best plan to fit your needs.</p>
</div>
<!-- 👉 Annual and monthly price toggler -->
<div class="d-flex align-center justify-center mx-auto my-10">
<VLabel
for="pricing-plan-toggle"
class="me-2"
>
Monthly
</VLabel>
<div class="position-relative">
<VSwitch
id="pricing-plan-toggle"
v-model="annualMonthlyPlanPriceToggler"
label="Annual"
/>
<div class="save-upto-chip position-absolute align-center d-none d-md-flex gap-1">
<VIcon
icon="tabler-corner-left-down"
class="flip-in-rtl"
/>
<VChip
label
color="primary"
>
Save up to 10%
</VChip>
</div>
</div>
</div>
<!-- SECTION pricing plans -->
<VRow>
<VCol
v-for="plan in pricingPlans"
:key="plan.logo"
v-bind="props"
cols="12"
>
<!-- 👉 Card -->
<VCard
flat
border
:class="plan.isPopular ? 'border-primary border-opacity-100' : ''"
>
<VCardText
style="block-size: 4.125rem;"
class="text-end"
>
<!-- 👉 Popular -->
<VChip
v-show="plan.isPopular"
label
color="primary"
size="small"
>
Popular
</VChip>
</VCardText>
<!-- 👉 Plan logo -->
<VCardText class="text-center">
<VImg
:height="140"
:src="plan.logo"
class="mx-auto mb-5"
/>
<!-- 👉 Plan name -->
<h5 class="text-h5 mb-2">
{{ plan.name }}
</h5>
<p class="mb-0">
{{ plan.tagLine }}
</p>
</VCardText>
<!-- 👉 Plan price -->
<VCardText class="position-relative text-center">
<div class="d-flex justify-center align-center">
<sup class="text-sm font-weight-medium me-1">$</sup>
<h1 class="text-5xl font-weight-medium text-primary">
{{ annualMonthlyPlanPriceToggler ? Math.floor(Number(plan.yearlyPrice) / 12) : plan.monthlyPrice }}
</h1>
<sub class="text-sm font-weight-medium ms-1 mt-4">/month</sub>
</div>
<!-- 👉 Annual Price -->
<span
v-show="annualMonthlyPlanPriceToggler"
class="position-absolute text-caption font-weight-medium mt-1"
style="inset-inline: 0;"
>
{{ plan.yearlyPrice === 0 ? 'free' : `USD ${plan.yearlyPrice}/Year` }}
</span>
</VCardText>
<!-- 👉 Plan features -->
<VCardText class="mt-5">
<VList class="card-list">
<VListItem
v-for="feature in plan.features"
:key="feature"
>
<template #prepend>
<VIcon
:size="14"
icon="tabler-circle"
class="me-3"
/>
</template>
<VListItemTitle>
{{ feature }}
</VListItemTitle>
</VListItem>
</VList>
</VCardText>
<!-- 👉 Plan actions -->
<VCardActions>
<VBtn
block
:color="plan.current ? 'success' : 'primary'"
:variant="plan.isPopular ? 'elevated' : 'tonal'"
>
{{ plan.yearlyPrice === 0 ? 'Your Current Plan' : 'Upgrade' }}
</VBtn>
</VCardActions>
</VCard>
</VCol>
</VRow>
<!-- !SECTION -->
</template>
<style lang="scss" scoped>
.card-list {
--v-card-list-gap: 0.75rem;
}
.save-upto-chip {
inset-block-start: -1.5rem;
inset-inline-end: -7rem;
}
</style>
@@ -0,0 +1,82 @@
<script setup>
import AppSearchHeaderBg from '@images/pages/app-search-header-bg.png'
const props = defineProps({
title: {
type: String,
required: false,
},
subtitle: {
type: String,
required: false,
},
customClass: {
type: String,
required: false,
},
})
defineOptions({ inheritAttrs: false })
</script>
<template>
<!-- 👉 Search Banner -->
<VCard
flat
class="text-center search-header"
:class="props.customClass"
:style="`background: url(${AppSearchHeaderBg});`"
>
<VCardText>
<h5 class="text-h3 font-weight-medium">
{{ props.title }}
</h5>
<!-- 👉 Search Input -->
<AppTextField
v-bind="$attrs"
placeholder="Search a question..."
class="search-header-input mx-auto my-3"
density="comfortable"
>
<template #prepend-inner>
<VIcon
icon="tabler-search"
size="23"
/>
</template>
</AppTextField>
<p class="mb-0">
{{ props.subtitle }}
</p>
</VCardText>
</VCard>
</template>
<style lang="scss">
.search-header {
padding: 4rem !important;
background-size: cover !important;
}
// search input
.search-header-input {
border-radius: 0.25rem !important;
border-radius: 0.375rem !important;
background-color: rgb(var(--v-theme-surface));
max-inline-size: 40.125rem !important;
.v-field__prepend-inner {
i {
inset-block-start: 3px !important;
}
}
}
@media (max-width: 37.5rem) {
.search-header {
padding: 1.5rem !important;
}
}
</style>
+37
View File
@@ -0,0 +1,37 @@
<script setup>
const props = defineProps({
errorCode: {
type: String,
required: false,
},
errorTitle: {
type: String,
required: false,
},
errorDescription: {
type: String,
required: false,
},
})
</script>
<template>
<div class="text-center">
<!-- 👉 Title and subtitle -->
<h1
v-if="props.errorCode"
class="text-h1 font-weight-medium"
>
{{ props.errorCode }}
</h1>
<h4
v-if="props.errorTitle"
class="text-h4 font-weight-medium mb-3"
>
{{ props.errorTitle }}
</h4>
<p v-if="props.errorDescription">
{{ props.errorDescription }}
</p>
</div>
</template>
+84
View File
@@ -0,0 +1,84 @@
<script setup>
import { onBeforeUnmount, onMounted, watchEffect } from 'vue'
import WaveSurfer from 'wavesurfer.js'
// eslint-disable-next-line import/extensions
import TimelinePlugin from 'wavesurfer.js/dist/plugins/timeline.js'
const props = defineProps({
url: {
type: String,
required: true,
},
})
const zoom = ref(50)
const audioEl = ref()
const waveEl = ref()
onMounted(()=>{
const audio = new Audio()
audio.controls = true
audio.src = props.url
const topTimeline = TimelinePlugin.create({
height: 20,
insertPosition: 'beforebegin',
timeInterval: 0.1,
primaryLabelInterval: 5,
secondaryLabelInterval: 1,
style: {
fontSize: '8px',
color: '#2D5B88',
},
})
const ws = WaveSurfer.create({
container: waveEl.value,
waveColor: '#4F4A85',
progressColor: '#383351',
url: props.url,
media: audio,
plugins: [topTimeline],
minPxPerSec: 50,
normalize: true,
})
ws.once('decode', () => {
watchEffect(() => {
ws.zoom(zoom.value)
})
})
audio.style.width = '100%'
audioEl.value.appendChild(audio)
onBeforeUnmount(() => {
ws.destroy()
audioEl.value.removeChild(audio)
})
})
</script>
<template>
<div>
<VSlider
v-model="zoom"
label="Zoom"
min="50"
max="200"
step="1"
class="mx-0"
prepend-icon="tabler-circle-minus"
append-icon="tabler-circle-plus"
@click:prepend="zoom = Math.max(zoom - 10, 50)"
@click:append="zoom = Math.min(zoom + 10, 200)"
/>
<div ref="waveEl" />
<div
ref="audioEl"
style="width: 100%;"
/>
</div>
</template>
@@ -0,0 +1,110 @@
<script setup>
import themeselectionQr from '@images/pages/themeselection-qr.png'
const props = defineProps({
authCode: {
type: String,
required: false,
},
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits([
'update:isDialogVisible',
'submit',
])
const authCode = ref(structuredClone(toRaw(props.authCode)))
const formSubmit = () => {
if (authCode.value) {
emit('submit', authCode.value)
emit('update:isDialogVisible', false)
}
}
const resetAuthCode = () => {
authCode.value = structuredClone(toRaw(props.authCode))
emit('update:isDialogVisible', false)
}
</script>
<template>
<VDialog
max-width="787"
:model-value="props.isDialogVisible"
@update:model-value="(val) => $emit('update:isDialogVisible', val)"
>
<!-- Dialog close btn -->
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
<VCard class="pa-5 pa-sm-8">
<VCardItem>
<VCardTitle class="text-h5 font-weight-medium text-center">
Add Authenticator App
</VCardTitle>
</VCardItem>
<VCardText class="pt-3">
<h6 class="text-lg font-weight-medium mb-2">
Authenticator Apps
</h6>
<p class="mb-6">
Using an authenticator app like Google Authenticator, Microsoft Authenticator, Authy, or 1Password, scan the QR code. It will generate a 6 digit code for you to enter below.
</p>
<div class="mb-4">
<VImg
width="122"
:src="themeselectionQr"
class="mx-auto"
/>
</div>
<VAlert
color="light-warning"
class="text-warning"
>
<span class="text-lg font-weight-medium">ASDLKNASDA9AHS678dGhASD78AB</span>
<p class="mb-0">
If you are unable to scan the QR code, you can manually enter the secret key below.
</p>
</VAlert>
<VForm @submit.prevent="() => {}">
<AppTextField
v-model="authCode"
name="auth-code"
label="Enter Authentication Code"
class="my-4"
/>
<div class="d-flex justify-end flex-wrap gap-3">
<VBtn
color="secondary"
variant="tonal"
@click="resetAuthCode"
>
Cancel
</VBtn>
<VBtn
type="submit"
@click="formSubmit"
>
Continue
<VIcon
end
icon="tabler-arrow-right"
class="flip-in-rtl"
/>
</VBtn>
</div>
</VForm>
</VCardText>
</VCard>
</VDialog>
</template>
@@ -0,0 +1,231 @@
<script setup>
const props = defineProps({
billingAddress: {
type: Object,
required: false,
default: () => ({
companyName: '',
billingEmail: '',
taxID: '',
vatNumber: '',
address: '',
contact: '',
country: null,
state: '',
zipCode: null,
}),
},
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits([
'update:isDialogVisible',
'submit',
])
const billingAddress = ref(structuredClone(toRaw(props.billingAddress)))
const resetForm = () => {
emit('update:isDialogVisible', false)
billingAddress.value = structuredClone(toRaw(props.billingAddress))
}
const onFormSubmit = () => {
emit('update:isDialogVisible', false)
emit('submit', billingAddress.value)
}
const selectedAddress = ref('Home')
const addressTypes = [
{
icon: {
icon: 'custom-home',
size: '40',
},
title: 'Home',
desc: 'Delivery Time (7am - 9pm)',
value: 'Home',
},
{
icon: {
icon: 'custom-office',
size: '40',
},
title: 'Office',
desc: 'Delivery Time (10am - 6pm)',
value: 'Office',
},
]
</script>
<template>
<VDialog
:width="$vuetify.display.smAndDown ? 'auto' : 610 "
:model-value="props.isDialogVisible"
@update:model-value="val => $emit('update:isDialogVisible', val)"
>
<!-- 👉 Dialog close btn -->
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
<VCard
v-if="props.billingAddress"
class="pa-sm-8 pa-5"
>
<!-- 👉 Title -->
<VCardItem>
<VCardTitle class="text-h4 text-center">
{{ props.billingAddress.address ? 'Edit' : 'Add New' }} Address
</VCardTitle>
</VCardItem>
<VCardText>
<!-- 👉 Subtitle -->
<VCardSubtitle class="text-center mb-6">
<span class="text-base">
Add new address for express delivery
</span>
</VCardSubtitle>
<div class="d-flex">
<CustomRadiosWithIcon
v-model:selected-radio="selectedAddress"
:radio-content="addressTypes"
:grid-column="{ sm: '6', cols: '12' }"
/>
</div>
<!-- 👉 Form -->
<VForm
class="mt-4"
@submit.prevent="onFormSubmit"
>
<VRow>
<!-- 👉 Company Name -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="billingAddress.companyName"
label="Company Name"
/>
</VCol>
<!-- 👉 Email -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="billingAddress.billingEmail"
label="Email"
/>
</VCol>
<!-- 👉 Tax ID -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="billingAddress.taxID"
label="Tax ID"
/>
</VCol>
<!-- 👉 VAT Number -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="billingAddress.vatNumber"
label="VAT Number"
/>
</VCol>
<!-- 👉 Billing Address -->
<VCol cols="12">
<AppTextarea
v-model="billingAddress.address"
rows="2"
label="Billing Address"
/>
</VCol>
<!-- 👉 Contact -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="billingAddress.contact"
label="Contact"
/>
</VCol>
<!-- 👉 Country -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="billingAddress.country"
label="Country"
/>
</VCol>
<!-- 👉 State -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="billingAddress.state"
label="State"
/>
</VCol>
<!-- 👉 Zip Code -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="billingAddress.zipCode"
label="Zip Code"
type="number"
/>
</VCol>
<!-- 👉 Submit and Cancel button -->
<VCol
cols="12"
class="text-center"
>
<VBtn
type="submit"
class="me-3"
>
submit
</VBtn>
<VBtn
variant="tonal"
color="secondary"
@click="resetForm"
>
Cancel
</VBtn>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VDialog>
</template>
@@ -0,0 +1,97 @@
<script setup>
const props = defineProps({
isDialogVisible: {
type: Boolean,
required: true,
},
permissionName: {
type: String,
required: false,
default: '',
},
})
const emit = defineEmits([
'update:isDialogVisible',
'update:permissionName',
])
const permissionName = ref('')
const onReset = () => {
emit('update:isDialogVisible', false)
permissionName.value = ''
}
const onSubmit = () => {
emit('update:isDialogVisible', false)
emit('update:permissionName', permissionName.value)
}
watch(props, () => {
permissionName.value = props.permissionName
})
</script>
<template>
<VDialog
:width="$vuetify.display.smAndDown ? 'auto' : 600"
:model-value="props.isDialogVisible"
@update:model-value="onReset"
>
<!-- 👉 dialog close btn -->
<DialogCloseBtn @click="onReset" />
<VCard class="pa-sm-8 pa-5">
<!-- 👉 Title -->
<VCardItem class="text-center">
<VCardTitle class="text-h5">
{{ props.permissionName ? 'Edit' : 'Add' }} Permission
</VCardTitle>
<VCardSubtitle>
{{ props.permissionName ? 'Edit' : 'Add' }} permission as per your requirements.
</VCardSubtitle>
</VCardItem>
<VCardText class="mt-1">
<!-- 👉 Form -->
<VForm>
<VAlert
type="warning"
title="Warning!"
variant="tonal"
class="mb-6"
>
By editing the permission name, you might break the system permissions functionality. Please ensure you're absolutely certain before proceeding.
</VAlert>
<!-- 👉 Role name -->
<div class="d-flex align-end gap-3 mb-3">
<AppTextField
v-model="permissionName"
density="compact"
label="Permission Name"
placeholder="Enter Permission Name"
/>
<VBtn @click="onSubmit">
Update
</VBtn>
</div>
<VCheckbox label="Set as core permission" />
</VForm>
</VCardText>
</VCard>
</VDialog>
</template>
<style lang="scss">
.permission-table {
td {
border-block-end: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
padding-block: 0.5rem;
padding-inline: 0;
}
}
</style>
@@ -0,0 +1,287 @@
<script setup>
import { VForm } from 'vuetify/components/VForm'
const props = defineProps({
rolePermissions: {
type: Object,
required: false,
default: () => ({
name: '',
permissions: [],
}),
},
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits([
'update:isDialogVisible',
'update:rolePermissions',
])
// 👉 Permission List
const permissions = ref([
{
name: 'User Management',
read: false,
write: false,
create: false,
},
{
name: 'Content Management',
read: false,
write: false,
create: false,
},
{
name: 'Disputes Management',
read: false,
write: false,
create: false,
},
{
name: 'Database Management',
read: false,
write: false,
create: false,
},
{
name: 'Financial Management',
read: false,
write: false,
create: false,
},
{
name: 'Reporting',
read: false,
write: false,
create: false,
},
{
name: 'API Control',
read: false,
write: false,
create: false,
},
{
name: 'Repository Management',
read: false,
write: false,
create: false,
},
{
name: 'Payroll',
read: false,
write: false,
create: false,
},
])
const isSelectAll = ref(false)
const role = ref('')
const refPermissionForm = ref()
const checkedCount = computed(() => {
let counter = 0
permissions.value.forEach(permission => {
Object.entries(permission).forEach(([key, value]) => {
if (key !== 'name' && value)
counter++
})
})
return counter
})
const isIndeterminate = computed(() => checkedCount.value > 0 && checkedCount.value < permissions.value.length * 3)
// select all
watch(isSelectAll, val => {
permissions.value = permissions.value.map(permission => ({
...permission,
read: val,
write: val,
create: val,
}))
})
// if Indeterminate is false, then set isSelectAll to false
watch(isIndeterminate, () => {
if (!isIndeterminate.value)
isSelectAll.value = false
})
// if all permissions are checked, then set isSelectAll to true
watch(permissions, () => {
if (checkedCount.value === permissions.value.length * 3)
isSelectAll.value = true
}, { deep: true })
// if rolePermissions is not empty, then set permissions
watch(props, () => {
if (props.rolePermissions && props.rolePermissions.permissions.length) {
role.value = props.rolePermissions.name
permissions.value = permissions.value.map(permission => {
const rolePermission = props.rolePermissions?.permissions.find(item => item.name === permission.name)
if (rolePermission) {
return {
...permission,
...rolePermission,
}
}
return permission
})
}
})
const onSubmit = () => {
const rolePermissions = {
name: role.value,
permissions: permissions.value,
}
emit('update:rolePermissions', rolePermissions)
emit('update:isDialogVisible', false)
isSelectAll.value = false
refPermissionForm.value?.reset()
}
const onReset = () => {
emit('update:isDialogVisible', false)
isSelectAll.value = false
refPermissionForm.value?.reset()
}
</script>
<template>
<VDialog
:width="$vuetify.display.smAndDown ? 'auto' : 900"
:model-value="props.isDialogVisible"
@update:model-value="onReset"
>
<!-- 👉 Dialog close btn -->
<DialogCloseBtn @click="onReset" />
<VCard class="pa-sm-8 pa-5">
<!-- 👉 Title -->
<VCardItem class="text-center">
<VCardTitle class="text-h3 mb-3">
{{ props.rolePermissions.name ? 'Edit' : 'Add New' }} Role
</VCardTitle>
<p class="text-base mb-0">
Set Role Permissions
</p>
</VCardItem>
<VCardText class="mt-6">
<!-- 👉 Form -->
<VForm ref="refPermissionForm">
<!-- 👉 Role name -->
<AppTextField
v-model="role"
label="Role Name"
placeholder="Enter Role Name"
/>
<h6 class="text-h4 mt-8 mb-3">
Role Permissions
</h6>
<!-- 👉 Role Permissions -->
<VTable class="permission-table text-no-wrap">
<!-- 👉 Admin -->
<tr>
<td>
Administrator Access
</td>
<td colspan="3">
<div class="d-flex justify-end">
<VCheckbox
v-model="isSelectAll"
v-model:indeterminate="isIndeterminate"
label="Select All"
/>
</div>
</td>
</tr>
<!-- 👉 Other permission loop -->
<template
v-for="permission in permissions"
:key="permission.name"
>
<tr>
<td>{{ permission.name }}</td>
<td>
<div class="d-flex justify-end">
<VCheckbox
v-model="permission.read"
label="Read"
/>
</div>
</td>
<td>
<div class="d-flex justify-end">
<VCheckbox
v-model="permission.write"
label="Write"
/>
</div>
</td>
<td>
<div class="d-flex justify-end">
<VCheckbox
v-model="permission.create"
label="Create"
/>
</div>
</td>
</tr>
</template>
</VTable>
<!-- 👉 Actions button -->
<div class="d-flex align-center justify-center gap-3 mt-6">
<VBtn @click="onSubmit">
Submit
</VBtn>
<VBtn
color="secondary"
variant="tonal"
@click="onReset"
>
Cancel
</VBtn>
</div>
</VForm>
</VCardText>
</VCard>
</VDialog>
</template>
<style lang="scss">
.permission-table {
td {
border-block-end: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
padding-block: 0.5rem;
.v-checkbox {
min-inline-size: 4.75rem;
}
&:not(:first-child) {
padding-inline: 0.5rem;
}
.v-label {
white-space: nowrap;
}
}
}
</style>
@@ -0,0 +1,140 @@
<script setup>
const props = defineProps({
cardDetails: {
type: Object,
required: false,
default: () => ({
number: '',
name: '',
expiry: '',
cvv: '',
isPrimary: false,
type: '',
}),
},
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits([
'submit',
'update:isDialogVisible',
])
const cardDetails = ref(structuredClone(toRaw(props.cardDetails)))
watch(props, () => {
cardDetails.value = structuredClone(toRaw(props.cardDetails))
})
const formSubmit = () => {
emit('submit', cardDetails.value)
}
const dialogModelValueUpdate = val => {
emit('update:isDialogVisible', val)
}
</script>
<template>
<VDialog
:width="$vuetify.display.smAndDown ? 'auto' : 580"
:model-value="props.isDialogVisible"
@update:model-value="dialogModelValueUpdate"
>
<!-- Dialog close btn -->
<DialogCloseBtn @click="dialogModelValueUpdate(false)" />
<VCard class="pa-5 pa-sm-8">
<!-- 👉 Title -->
<VCardItem class="text-center">
<VCardTitle class="text-h5 font-weight-medium mb-3">
{{ props.cardDetails.name ? 'Edit Card' : 'Add New Card' }}
</VCardTitle>
<p class="mb-0">
{{ props.cardDetails.name ? 'Edit your saved card details' : 'Add your saved card details' }}
</p>
</VCardItem>
<VCardText class="pt-6">
<VForm @submit.prevent="() => {}">
<VRow>
<!-- 👉 Card Number -->
<VCol cols="12">
<AppTextField
v-model="cardDetails.number"
label="Card Number"
type="number"
/>
</VCol>
<!-- 👉 Card Name -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="cardDetails.name"
label="Name"
/>
</VCol>
<!-- 👉 Card Expiry -->
<VCol
cols="6"
md="3"
>
<AppTextField
v-model="cardDetails.expiry"
label="Expiry"
/>
</VCol>
<!-- 👉 Card CVV -->
<VCol
cols="6"
md="3"
>
<AppTextField
v-model="cardDetails.cvv"
type="number"
label="CVV"
/>
</VCol>
<!-- 👉 Card Primary Set -->
<VCol cols="12">
<VSwitch
v-model="cardDetails.isPrimary"
label="Set as primary card"
/>
</VCol>
<!-- 👉 Card actions -->
<VCol
cols="12"
class="text-center"
>
<VBtn
class="me-3"
type="submit"
@click="formSubmit"
>
Submit
</VBtn>
<VBtn
color="secondary"
variant="tonal"
@click="$emit('update:isDialogVisible', false)"
>
Cancel
</VBtn>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VDialog>
</template>
@@ -0,0 +1,164 @@
<script setup>
const props = defineProps({
confirmationQuestion: {
type: String,
required: true,
},
isDialogVisible: {
type: Boolean,
required: true,
},
confirmTitle: {
type: String,
required: true,
},
confirmMsg: {
type: String,
required: true,
},
cancelTitle: {
type: String,
required: true,
},
cancelMsg: {
type: String,
required: true,
},
})
const emit = defineEmits([
'update:isDialogVisible',
'confirm',
])
const unsubscribed = ref(false)
const cancelled = ref(false)
const updateModelValue = val => {
emit('update:isDialogVisible', val)
}
const onConfirmation = () => {
emit('confirm', true)
updateModelValue(false)
unsubscribed.value = true
}
const onCancel = () => {
emit('confirm', false)
emit('update:isDialogVisible', false)
cancelled.value = true
}
</script>
<template>
<!-- 👉 Confirm Dialog -->
<VDialog
max-width="500"
:model-value="props.isDialogVisible"
@update:model-value="updateModelValue"
>
<VCard class="text-center px-10 py-6">
<VCardText>
<VBtn
icon
variant="outlined"
color="warning"
class="my-4"
style=" block-size: 88px;inline-size: 88px; pointer-events: none;"
>
<span class="text-5xl">!</span>
</VBtn>
<h6 class="text-lg font-weight-medium">
{{ props.confirmationQuestion }}
</h6>
</VCardText>
<VCardText class="d-flex align-center justify-center gap-2">
<VBtn
variant="elevated"
@click="onConfirmation"
>
Confirm
</VBtn>
<VBtn
color="secondary"
variant="tonal"
@click="onCancel"
>
Cancel
</VBtn>
</VCardText>
</VCard>
</VDialog>
<!-- Unsubscribed -->
<VDialog
v-model="unsubscribed"
max-width="500"
>
<VCard>
<VCardText class="text-center px-10 py-6">
<VBtn
icon
variant="outlined"
color="success"
class="my-4"
style=" block-size: 88px;inline-size: 88px; pointer-events: none;"
>
<span class="text-3xl">
<VIcon icon="tabler-check" />
</span>
</VBtn>
<h1 class="text-h4 mb-4">
{{ props.confirmTitle }}
</h1>
<p>{{ props.confirmMsg }}</p>
<VBtn
color="success"
@click="unsubscribed = false"
>
Ok
</VBtn>
</VCardText>
</VCard>
</VDialog>
<!-- Cancelled -->
<VDialog
v-model="cancelled"
max-width="500"
>
<VCard>
<VCardText class="text-center px-10 py-6">
<VBtn
icon
variant="outlined"
color="error"
class="my-4"
style=" block-size: 88px;inline-size: 88px; pointer-events: none;"
>
<span class="text-5xl font-weight-light">X</span>
</VBtn>
<h1 class="text-h4 mb-4">
{{ props.cancelTitle }}
</h1>
<p>{{ props.cancelMsg }}</p>
<VBtn
color="success"
@click="cancelled = false"
>
Ok
</VBtn>
</VCardText>
</VCard>
</VDialog>
</template>
@@ -0,0 +1,437 @@
<script setup>
import laptopGirl from '@images/illustrations/laptop-girl.png'
const props = defineProps({
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits([
'update:isDialogVisible',
'updatedData',
])
const currentStep = ref(0)
const createApp = [
{
icon: 'tabler-clipboard',
title: 'Details',
subtitle: 'Enter Details',
},
{
icon: 'tabler-box',
title: 'Frameworks',
subtitle: 'Select Framework',
},
{
icon: 'tabler-database',
title: 'Database',
subtitle: 'Select Database',
},
{
icon: 'tabler-credit-card',
title: 'Billing',
subtitle: 'Payment Details',
},
{
icon: 'tabler-check',
title: 'Submit',
subtitle: 'submit',
},
]
const categories = [
{
icon: 'tabler-briefcase',
color: 'info',
title: 'CRM Application',
subtitle: 'Scales with any business',
slug: 'crm-application',
},
{
icon: 'tabler-shopping-cart',
color: 'success',
title: 'Ecommerce Platforms',
subtitle: 'Grow Your Business With App',
slug: 'ecommerce-application',
},
{
icon: 'tabler-device-laptop',
color: 'error',
title: 'Online Learning platform',
subtitle: 'Start learning today',
slug: 'online-learning-application',
},
]
const frameworks = [
{
icon: 'tabler-brand-react-native',
color: 'info',
title: 'React Native',
subtitle: 'Create truly native apps',
slug: 'react-framework',
},
{
icon: 'tabler-brand-angular',
color: 'error',
title: 'Angular',
subtitle: 'Most suited for your application',
slug: 'angular-framework',
},
{
icon: 'tabler-brand-html5',
color: 'warning',
title: 'HTML',
subtitle: 'Progressive Framework',
slug: 'html-framework',
},
{
icon: 'tabler-brand-python',
color: 'primary',
title: 'Python',
subtitle: 'js web frameworks',
slug: 'js-framework',
},
]
const databases = [
{
icon: 'tabler-brand-firebase',
color: 'error',
title: 'Firebase',
subtitle: 'Cloud Firestore',
slug: 'firebase-database',
},
{
icon: 'tabler-brand-amazon',
color: 'warning',
title: 'AWS',
subtitle: 'Amazon Fast NoSQL Database',
slug: 'aws-database',
},
{
icon: 'tabler-database',
color: 'info',
title: 'MySQL',
subtitle: 'Basic MySQL database',
slug: 'mysql-database',
},
]
const createAppData = ref({
category: 'crm-application',
framework: 'vue-framework',
database: 'firebase-database',
cardNumber: null,
cardName: '',
cardExpiry: '',
cardCvv: '',
isSave: false,
})
const dialogVisibleUpdate = val => {
emit('update:isDialogVisible', val)
currentStep.value = 0
}
watch(props, () => {
if (!props.isDialogVisible)
currentStep.value = 0
})
const onSubmit = () => {
// eslint-disable-next-line no-alert
alert('submitted...!!')
emit('updatedData', createAppData.value)
}
</script>
<template>
<VDialog
:model-value="props.isDialogVisible"
max-width="900"
@update:model-value="dialogVisibleUpdate"
>
<!-- 👉 dialog close btn -->
<DialogCloseBtn
size="small"
@click="emit('update:isDialogVisible', false)"
/>
<VCard class="create-app-dialog">
<VCardText class="pa-5 pa-sm-10">
<h5 class="text-h5 text-center mb-2">
Create App
</h5>
<p class="text-sm text-center mb-8">
Provide data with this form to create your app.
</p>
<VRow>
<VCol
cols="12"
sm="5"
md="4"
lg="3"
>
<AppStepper
v-model:current-step="currentStep"
direction="vertical"
:items="createApp"
icon-size="24"
class="stepper-icon-step-bg"
/>
</VCol>
<VCol
cols="12"
sm="7"
md="8"
lg="9"
>
<VWindow
v-model="currentStep"
class="disable-tab-transition stepper-content"
>
<!-- 👉 category -->
<VWindowItem>
<AppTextField label="Application Name" />
<h6 class="text-h6 my-4">
Category
</h6>
<VRadioGroup v-model="createAppData.category">
<VList class="card-list">
<VListItem
v-for="category in categories"
:key="category.title"
@click="createAppData.category = category.slug"
>
<template #prepend>
<VAvatar
size="48"
rounded
variant="tonal"
:color="category.color"
:icon="category.icon"
/>
</template>
<VListItemTitle class="mb-1">
{{ category.title }}
</VListItemTitle>
<VListItemSubtitle>
{{ category.subtitle }}
</VListItemSubtitle>
<template #append>
<VRadio :value="category.slug" />
</template>
</VListItem>
</VList>
</VRadioGroup>
</VWindowItem>
<!-- 👉 Frameworks -->
<VWindowItem>
<h6 class="text-h6 mb-4">
Select Framework
</h6>
<VRadioGroup v-model="createAppData.framework">
<VList class="card-list">
<VListItem
v-for="framework in frameworks"
:key="framework.title"
@click="createAppData.framework = framework.slug"
>
<template #prepend>
<VAvatar
size="48"
rounded
variant="tonal"
:color="framework.color"
>
<VIcon :icon="framework.icon" />
</VAvatar>
</template>
<VListItemTitle class="mb-1">
{{ framework.title }}
</VListItemTitle>
<VListItemSubtitle>
{{ framework.subtitle }}
</VListItemSubtitle>
<template #append>
<VRadio :value="framework.slug" />
</template>
</VListItem>
</VList>
</VRadioGroup>
</VWindowItem>
<!-- 👉 Database Engine -->
<VWindowItem>
<AppTextField label="Database Name" />
<h6 class="text-h6 my-4">
Select Database Engine
</h6>
<VRadioGroup v-model="createAppData.database">
<VList class="card-list">
<VListItem
v-for="database in databases"
:key="database.title"
@click="createAppData.database = database.slug"
>
<template #prepend>
<VAvatar
size="48"
rounded
variant="tonal"
:color="database.color"
>
<VIcon :icon="database.icon" />
</VAvatar>
</template>
<VListItemTitle class="mb-1">
{{ database.title }}
</VListItemTitle>
<VListItemSubtitle>
{{ database.subtitle }}
</VListItemSubtitle>
<template #append>
<VRadio :value="database.slug" />
</template>
</VListItem>
</VList>
</VRadioGroup>
</VWindowItem>
<!-- 👉 Billing form -->
<VWindowItem>
<h6 class="text-h6 mb-4">
Payment Details
</h6>
<VForm>
<VRow>
<VCol cols="12">
<AppTextField
v-model="createAppData.cardNumber"
label="Card Number"
type="number"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="createAppData.cardName"
label="Name on Card"
/>
</VCol>
<VCol
cols="6"
md="3"
>
<AppTextField
v-model="createAppData.cardExpiry"
label="Expiry"
/>
</VCol>
<VCol
cols="6"
md="3"
>
<AppTextField
v-model="createAppData.cardCvv"
label="CVV"
/>
</VCol>
<VCol cols="12">
<VSwitch
v-model="createAppData.isSave"
label="Save Card for future billing?"
/>
</VCol>
</VRow>
</VForm>
</VWindowItem>
<VWindowItem class="text-center">
<h6 class="text-h6 mb-2">
Submit 🥳
</h6>
<p class="text-sm mb-6">
Submit to kickstart your project.
</p>
<VImg
:src="laptopGirl"
width="176"
class="mx-auto"
/>
</VWindowItem>
</VWindow>
<div class="d-flex justify-space-between mt-8">
<VBtn
variant="tonal"
color="secondary"
:disabled="currentStep === 0"
@click="currentStep--"
>
<VIcon
icon="tabler-arrow-left"
start
class="flip-in-rtl"
/>
Previous
</VBtn>
<VBtn
v-if="createApp.length - 1 === currentStep"
color="success"
@click="onSubmit"
>
submit
<VIcon
icon="tabler-check"
end
class="flip-in-rtl"
/>
</VBtn>
<VBtn
v-else
@click="currentStep++"
>
Next
<VIcon
icon="tabler-arrow-right"
end
class="flip-in-rtl"
/>
</VBtn>
</div>
</VCol>
</VRow>
</VCardText>
</VCard>
</VDialog>
</template>
<style lang="scss">
.stepper-content .card-list {
--v-card-list-gap: 24px;
}
</style>
@@ -0,0 +1,93 @@
<script setup>
const props = defineProps({
mobileNumber: {
type: String,
required: false,
},
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits([
'update:isDialogVisible',
'submit',
])
const phoneNumber = ref(structuredClone(toRaw(props.mobileNumber)))
const formSubmit = () => {
if (phoneNumber.value) {
emit('submit', phoneNumber.value)
emit('update:isDialogVisible', false)
}
}
const resetPhoneNumber = () => {
phoneNumber.value = structuredClone(toRaw(props.mobileNumber))
emit('update:isDialogVisible', false)
}
const dialogModelValueUpdate = val => {
emit('update:isDialogVisible', val)
}
</script>
<template>
<VDialog
max-width="787"
:model-value="props.isDialogVisible"
@update:model-value="dialogModelValueUpdate"
>
<!-- Dialog close btn -->
<DialogCloseBtn @click="dialogModelValueUpdate(false)" />
<VCard class="pa-5 pa-sm-8">
<VCardItem class="text-start">
<VCardTitle class="text-h6 font-weight-medium mb-2">
Verify Your Mobile Number for SMS
</VCardTitle>
<VCardSubtitle>
<span class="text-base">
Enter your mobile phone number with country code and we will send you a verification code.
</span>
</VCardSubtitle>
</VCardItem>
<VCardText class="pt-6">
<VForm @submit.prevent="() => {}">
<AppTextField
v-model="phoneNumber"
name="mobile"
label="Phone Number"
type="number"
placeholder="202 555 0111"
class="mb-5"
/>
<div class="d-flex flex-wrap justify-end gap-4">
<VBtn
color="secondary"
variant="tonal"
@click="resetPhoneNumber"
>
Cancel
</VBtn>
<VBtn
type="submit"
@click="formSubmit"
>
continue
<VIcon
end
icon="tabler-arrow-right"
class="flip-in-rtl"
/>
</VBtn>
</div>
</VForm>
</VCardText>
</VCard>
</VDialog>
</template>
@@ -0,0 +1,34 @@
<script setup>
const props = defineProps({
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits(['update:isDialogVisible'])
const dialogVisibleUpdate = val => {
emit('update:isDialogVisible', val)
}
</script>
<template>
<VDialog
:model-value="props.isDialogVisible"
class="v-dialog-xl"
@update:model-value="dialogVisibleUpdate"
>
<!-- 👉 Dialog close btn -->
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
<VCard class="pricing-dialog pa-5 pa-sm-8">
<VCardText>
<AppPricing
title="Subscription Plan"
md="4"
/>
</VCardText>
</VCard>
</VDialog>
</template>
@@ -0,0 +1,182 @@
<script setup>
const props = defineProps({
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits(['update:isDialogVisible'])
const dialogVisibleUpdate = val => {
emit('update:isDialogVisible', val)
}
const referAndEarnSteps = [
{
icon: 'tabler-send',
title: 'Send Invitation 👍🏻',
subtitle: 'Send your referral link to your friend',
},
{
icon: 'tabler-rocket',
title: 'Registration 😎',
subtitle: 'Let them register to our services',
},
{
icon: 'tabler-keyboard',
title: 'Free Trial 🎉',
subtitle: 'Your friend will get 30 days free trial',
},
]
</script>
<template>
<VDialog
:model-value="props.isDialogVisible"
max-width="740"
@update:model-value="dialogVisibleUpdate"
>
<!-- 👉 Dialog close btn -->
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
<VCard class="refer-and-earn-dialog">
<VCardText class="px-5 px-sm-16 pt-16 pb-10">
<h5 class="text-h5 text-center mb-3">
Refer & Earn
</h5>
<p class="text-sm-body-1 text-center">
Invite your friend to vuexy, if they sign up, you and your friend will get 30 days free trial
</p>
<VRow class="text-center mt-8">
<VCol
v-for="step in referAndEarnSteps"
:key="step.title"
cols="12"
sm="4"
>
<VAvatar
variant="tonal"
size="82"
color="primary"
rounded
>
<VIcon
size="40"
:icon="step.icon"
/>
</VAvatar>
<h6 class="text-lg mt-4 mb-2">
{{ step.title }}
</h6>
<span class="text-base">{{ step.subtitle }}</span>
</VCol>
</VRow>
</VCardText>
<VDivider />
<VCardText class="px-5 px-sm-16 pt-10 pb-16">
<h6 class="text-lg mb-4">
Invite your friends
</h6>
<p class="mb-1 text-sm">
Enter your friend's email address and invite them to join Vuexy 😍
</p>
<VForm
class="d-flex align-center gap-4"
@submit.prevent="() => {}"
>
<AppTextField
density="compact"
placeholder="johnDoe@gmail.com"
/>
<VBtn type="submit">
Submit
</VBtn>
</VForm>
<h6 class="text-h6 mb-4 mt-7">
Share the referral link
</h6>
<p class="mb-1 text-sm">
You can also copy and send it or share it on your social media. 🚀
</p>
<VForm
class="d-flex align-center flex-wrap gap-3"
@submit.prevent="() => {}"
>
<AppTextField
density="compact"
placeholder="http://referral.link"
class="refer-link-input me-1"
>
<template #append-inner>
<VBtn variant="text">
COPY LINK
</VBtn>
</template>
</AppTextField>
<div class="d-flex gap-3">
<VBtn
icon
class="rounded"
color="#3B5998"
size="38"
>
<VIcon
color="white"
icon="tabler-brand-facebook"
size="22"
/>
</VBtn>
<VBtn
icon
class="rounded"
color="#55ACEE"
size="38"
>
<VIcon
color="white"
icon="tabler-brand-twitter"
size="22"
/>
</VBtn>
<VBtn
icon
class="rounded"
color="#007BB6"
size="38"
>
<VIcon
color="white"
icon="tabler-brand-linkedin"
size="22"
/>
</VBtn>
</div>
</VForm>
</VCardText>
</VCard>
</VDialog>
</template>
<style lang="scss">
.refer-link-input {
.v-field--appended {
padding-inline-end: 0;
}
.v-field__append-inner {
padding-block-start: 0.125rem;
}
}
</style>
@@ -0,0 +1,185 @@
<script setup>
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'
const props = defineProps({
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits(['update:isDialogVisible'])
const dialogVisibleUpdate = val => {
emit('update:isDialogVisible', val)
}
const membersList = [
{
avatar: avatar1,
name: 'Lester Palmer',
email: 'jerrod98@gmail.com',
permission: 'Can Edit',
},
{
avatar: avatar2,
name: 'Mattie Blair',
email: 'prudence.boehm@yahoo.com',
permission: 'Owner',
},
{
avatar: avatar3,
name: 'Marvin Wheeler',
email: 'rumet@jujpejah.net',
permission: 'Can Comment',
},
{
avatar: avatar4,
name: 'Nannie Ford',
email: 'negza@nuv.io',
permission: 'Can View',
},
{
avatar: avatar5,
name: 'Julian Murphy',
email: 'lunebame@umdomgu.net',
permission: 'Can Edit',
},
{
avatar: avatar6,
name: 'Sophie Gilbert',
email: 'ha@sugit.gov',
permission: 'Can View',
},
{
avatar: avatar7,
name: 'Chris Watkins',
email: 'zokap@mak.org',
permission: 'Can Comment',
},
{
avatar: avatar8,
name: 'Adelaide Nichols',
email: 'ujinomu@jigo.com',
permission: 'Can Edit',
},
]
</script>
<template>
<VDialog
:model-value="props.isDialogVisible"
max-width="800"
@update:model-value="dialogVisibleUpdate"
>
<!-- 👉 Dialog close btn -->
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
<VCard class="share-project-dialog pa-5 pa-sm-8">
<VCardText>
<h5 class="text-h5 text-center mb-3">
Share Project
</h5>
<p class="text-sm-body-1 text-center mb-6">
Share project with a team members
</p>
<p class="font-weight-medium mb-1">
Add Members
</p>
<AppAutocomplete
:items="membersList"
item-title="name"
item-value="name"
placeholder="Add project members..."
density="compact"
>
<template #item="{ props: listItemProp, item }">
<VListItem v-bind="listItemProp">
<template #prepend>
<VAvatar
:image="item.raw.avatar"
size="30"
/>
</template>
</VListItem>
</template>
</AppAutocomplete>
<h6 class="text-h6 mb-4 mt-8">
8 Members
</h6>
<VList class="card-list">
<VListItem
v-for="member in membersList"
:key="member.name"
>
<template #prepend>
<VAvatar :image="member.avatar" />
</template>
<VListItemTitle class="text-sm">
{{ member.name }}
</VListItemTitle>
<VListItemSubtitle>
{{ member.email }}
</VListItemSubtitle>
<template #append>
<VBtn
variant="plain"
color="default"
:icon="$vuetify.display.xs"
>
<span class="d-none d-sm-block">{{ member.permission }}</span>
<VIcon icon="tabler-chevron-down" />
<VMenu activator="parent">
<VList :selected="[member.permission]">
<VListItem
v-for="(item, index) in ['Owner', 'Can Edit', 'Can Comment', 'Can View']"
:key="index"
:value="item"
>
<VListItemTitle>{{ item }}</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</VBtn>
</template>
</VListItem>
</VList>
<div class="d-flex align-center justify-space-between flex-wrap gap-3 mt-6">
<h6 class="text-sm font-weight-medium d-flex align-start">
<VIcon
icon="tabler-users"
class="me-2"
/>
<span>Public to Master - ThemeSelection</span>
</h6>
<VBtn class="text-capitalize">
Copy Project Link
</VBtn>
</div>
</VCardText>
</VCard>
</VDialog>
</template>
<style lang="scss">
.share-project-dialog {
.card-list {
--v-card-list-gap: 1rem;
}
}
</style>
@@ -0,0 +1,166 @@
<script setup>
const props = defineProps({
isDialogVisible: {
type: Boolean,
required: true,
default: false,
},
smsCode: {
type: String,
required: false,
default: '',
},
authAppCode: {
type: String,
required: false,
default: '',
},
})
const emit = defineEmits(['update:isDialogVisible'])
const authMethods = [
{
icon: 'tabler-settings',
title: 'Authenticator Apps',
subtitle: 'Get code from an app like Google Authenticator or Microsoft Authenticator.',
method: 'authApp',
},
{
icon: 'tabler-message',
title: 'SMS',
subtitle: 'We will send a code via SMS if you need to use your backup login method.',
method: 'sms',
},
]
const selectedMethod = ref(['authApp'])
const isAuthAppDialogVisible = ref(false)
const isSmsDialogVisible = ref(false)
const openSelectedMethodDialog = () => {
if (selectedMethod.value[0] === 'authApp') {
isAuthAppDialogVisible.value = true
isSmsDialogVisible.value = false
emit('update:isDialogVisible', false)
}
if (selectedMethod.value[0] === 'sms') {
isAuthAppDialogVisible.value = false
isSmsDialogVisible.value = true
emit('update:isDialogVisible', false)
}
}
</script>
<template>
<VDialog
max-width="787"
:model-value="props.isDialogVisible"
@update:model-value="(val) => $emit('update:isDialogVisible', val)"
>
<!-- Dialog close btn -->
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
<VCard class="pa-5 pa-sm-8">
<VCardItem class="text-center">
<VCardTitle class="text-h5 mb-3">
Select Authentication Method
</VCardTitle>
<VCardSubtitle>
<span class="text-base">
You also need to select a method by which the proxy authenticates to the directory serve.
</span>
</VCardSubtitle>
</VCardItem>
<VCardText>
<VList
v-model:selected="selectedMethod"
mandatory
variant="outlined"
class="card-list auth-method-card"
:class="$vuetify.display.xs ? 'responsive-card' : ''"
>
<VListItem
v-for="item of authMethods"
:key="item.title"
rounded
:value="item.method"
class="py-5 px-6 my-6"
:class="selectedMethod[0] === item.method && 'bg-light-primary text-primary'"
>
<template #prepend>
<VIcon
:icon="item.icon"
size="38"
/>
</template>
<VListItemTitle class="mb-2">
<span class="text-xl font-weight-medium">
{{ item.title }}
</span>
</VListItemTitle>
<p class="text-base mb-0">
{{ item.subtitle }}
</p>
</VListItem>
</VList>
<div class="d-flex gap-4 justify-center">
<VBtn @click="openSelectedMethodDialog">
submit
</VBtn>
<VBtn
color="secondary"
variant="tonal"
@click="$emit('update:isDialogVisible', false)"
>
Close
</VBtn>
</div>
</VCardText>
</VCard>
</VDialog>
<AddAuthenticatorAppDialog
v-model:isDialogVisible="isAuthAppDialogVisible"
:auth-code="props.authAppCode"
/>
<EnableOneTimePasswordDialog
v-model:isDialogVisible="isSmsDialogVisible"
:mobile-number="props.smsCode"
/>
</template>
<style lang="scss">
.auth-method-card {
&.card-list .v-list-item {
padding-block: 20px !important;
padding-inline: 30px !important;
}
.v-list-item--active {
background-color: transparent !important;
.v-list-item__overlay {
opacity: 0;
}
}
&.responsive-card {
.v-list-item {
display: flex;
flex-direction: column;
gap: 0.5rem;
text-align: center;
.v-list-item__prepend {
svg {
margin: 0;
}
}
}
}
}
</style>
@@ -0,0 +1,204 @@
<script setup>
const props = defineProps({
userData: {
type: Object,
required: false,
default: () => ({
id: 0,
fullName: '',
company: '',
role: '',
username: '',
country: '',
contact: '',
email: '',
currentPlan: '',
status: '',
avatar: '',
taskDone: null,
projectDone: null,
taxId: '',
language: '',
}),
},
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits([
'submit',
'update:isDialogVisible',
])
const userData = ref(structuredClone(toRaw(props.userData)))
const isUseAsBillingAddress = ref(false)
watch(props, () => {
userData.value = structuredClone(toRaw(props.userData))
})
const onFormSubmit = () => {
emit('update:isDialogVisible', false)
emit('submit', userData.value)
}
const onFormReset = () => {
userData.value = structuredClone(toRaw(props.userData))
emit('update:isDialogVisible', false)
}
const dialogModelValueUpdate = val => {
emit('update:isDialogVisible', val)
}
</script>
<template>
<VDialog
:width="$vuetify.display.smAndDown ? 'auto' : 677"
:model-value="props.isDialogVisible"
@update:model-value="dialogModelValueUpdate"
>
<!-- Dialog close btn -->
<DialogCloseBtn @click="dialogModelValueUpdate(false)" />
<VCard class="pa-sm-8 pa-5">
<VCardItem class="text-center">
<VCardTitle class="text-h5 mb-3">
Edit User Information
</VCardTitle>
<p class="mb-0">
Updating user details will receive a privacy audit.
</p>
</VCardItem>
<VCardText>
<!-- 👉 Form -->
<VForm
class="mt-6"
@submit.prevent="onFormSubmit"
>
<VRow>
<!-- 👉 First Name -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="userData.fullName.split(' ')[0]"
label="first Name"
/>
</VCol>
<!-- 👉 Last Name -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="userData.fullName.split(' ')[1]"
label="Last Name"
/>
</VCol>
<!-- 👉 Billing Email -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="userData.email"
label="Billing Email"
/>
</VCol>
<!-- 👉 Status -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="userData.status"
label="Status"
/>
</VCol>
<!-- 👉 Tax Id -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="userData.taxId"
label="Tax Id"
/>
</VCol>
<!-- 👉 Contact -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="userData.contact"
label="Contact"
/>
</VCol>
<!-- 👉 Language -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="userData.language"
chips
multiple
label="Language"
/>
</VCol>
<!-- 👉 Country -->
<VCol
cols="12"
md="6"
>
<AppTextField
v-model="userData.country"
label="Country"
/>
</VCol>
<!-- 👉 Switch -->
<VCol cols="12">
<VSwitch
v-model="isUseAsBillingAddress"
density="compact"
label="Use as a billing address?"
/>
</VCol>
<!-- 👉 Submit and Cancel -->
<VCol
cols="12"
class="d-flex flex-wrap justify-center gap-4"
>
<VBtn type="submit">
Submit
</VBtn>
<VBtn
color="secondary"
variant="tonal"
@click="onFormReset"
>
Cancel
</VBtn>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VDialog>
</template>
@@ -0,0 +1,118 @@
<script setup>
const props = defineProps({
isDialogVisible: {
type: Boolean,
required: true,
},
})
const emit = defineEmits(['update:isDialogVisible'])
const selectedPlan = ref('standard')
const plansList = [
{
desc: 'Standard - $99/month',
title: 'Standard',
value: 'standard',
},
{
desc: 'Basic - $0/month',
title: 'Basic',
value: 'basic',
},
{
desc: 'Enterprise - $499/month',
title: 'Enterprise',
value: 'enterprice',
},
{
desc: 'Company - $999/month',
title: 'Company',
value: 'company',
},
]
const isConfirmDialogVisible = ref(false)
const dialogModelValueUpdate = val => {
emit('update:isDialogVisible', val)
}
</script>
<template>
<!-- 👉 upgrade plan -->
<VDialog
:width="$vuetify.display.smAndDown ? 'auto' : 560"
:model-value="props.isDialogVisible"
@update:model-value="dialogModelValueUpdate"
>
<!-- Dialog close btn -->
<DialogCloseBtn @click="dialogModelValueUpdate(false)" />
<VCard class="py-8">
<!-- 👉 dialog close btn -->
<DialogCloseBtn
variant="text"
size="small"
@click="$emit('update:isDialogVisible', false)"
/>
<VCardItem class="text-center">
<VCardTitle class="text-h5 mb-5">
Upgrade Plan
</VCardTitle>
<VCardSubtitle>
Choose the best plan for user.
</VCardSubtitle>
</VCardItem>
<VCardText class="d-flex align-center flex-column flex-sm-nowrap px-15">
<CustomRadios
:radio-content="plansList"
:selected-radio="selectedPlan"
:grid-column="{ cols: '12', sm: '6' }"
/>
<VBtn class="mt-5">
Upgrade
</VBtn>
</VCardText>
<VDivider class="my-3" />
<VCardText class="px-15">
<p class="font-weight-medium mb-2">
User current plan is standard plan
</p>
<div class="d-flex justify-space-between flex-wrap">
<div class="d-flex align-center me-3">
<sup class="text-primary">$</sup>
<h3 class="text-h3 text-primary">
99
</h3>
<sub class="text-body-1 mt-3">/ month</sub>
</div>
<VBtn
color="error"
variant="tonal"
class="mt-3"
@click="isConfirmDialogVisible = true"
>
Cancel Subscription
</VBtn>
</div>
</VCardText>
<!-- 👉 Confirm Dialog -->
<ConfirmDialog
v-model:isDialogVisible="isConfirmDialogVisible"
cancel-title="Cancelled"
confirm-title="Unsubscribed!"
confirm-msg="Your subscription cancelled successfully."
confirmation-question="Are you sure to cancel your subscription?"
cancel-msg="Unsubscription Cancelled!!"
/>
</VCard>
</VDialog>
</template>