init: чистый старт Laravel + Vuexy
This commit is contained in:
63
resources/ts/components/AppLoadingIndicator.vue
Normal file
63
resources/ts/components/AppLoadingIndicator.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<script setup lang="ts">
|
||||
const bufferValue = ref(20)
|
||||
const progressValue = ref(10)
|
||||
const isFallbackState = ref(false)
|
||||
const interval = ref<ReturnType<typeof setInterval>>()
|
||||
const showProgress = ref(false)
|
||||
|
||||
watch([progressValue, isFallbackState], () => {
|
||||
if (progressValue.value > 80 && isFallbackState.value)
|
||||
progressValue.value = 82
|
||||
|
||||
startBuffer()
|
||||
})
|
||||
|
||||
function startBuffer() {
|
||||
clearInterval(interval.value)
|
||||
interval.value = setInterval(() => {
|
||||
progressValue.value += Math.random() * (15 - 5) + 5
|
||||
bufferValue.value += Math.random() * (15 - 5) + 6
|
||||
}, 800)
|
||||
}
|
||||
|
||||
const fallbackHandle = () => {
|
||||
showProgress.value = true
|
||||
progressValue.value = 10
|
||||
isFallbackState.value = true
|
||||
startBuffer()
|
||||
}
|
||||
|
||||
const resolveHandle = () => {
|
||||
isFallbackState.value = false
|
||||
progressValue.value = 100
|
||||
|
||||
setTimeout(() => {
|
||||
clearInterval(interval.value)
|
||||
progressValue.value = 0
|
||||
bufferValue.value = 20
|
||||
showProgress.value = false
|
||||
}, 300)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
fallbackHandle,
|
||||
resolveHandle,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- loading state via #fallback slot -->
|
||||
<div
|
||||
v-if="showProgress"
|
||||
class="position-fixed"
|
||||
style="z-index: 9999; inset-block-start: 0; inset-inline: 0 0;"
|
||||
>
|
||||
<VProgressLinear
|
||||
v-model="progressValue"
|
||||
:buffer-value="bufferValue"
|
||||
color="primary"
|
||||
height="2"
|
||||
bg-color="background"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
247
resources/ts/components/AppPricing.vue
Normal file
247
resources/ts/components/AppPricing.vue
Normal file
@@ -0,0 +1,247 @@
|
||||
<script setup lang="ts">
|
||||
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'
|
||||
|
||||
interface Pricing {
|
||||
title?: string
|
||||
xs?: number | string
|
||||
sm?: number | string
|
||||
md?: string | number
|
||||
lg?: string | number
|
||||
xl?: string | number
|
||||
}
|
||||
|
||||
const props = defineProps<Pricing>()
|
||||
|
||||
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: 49,
|
||||
yearlyPrice: 499,
|
||||
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: 99,
|
||||
yearlyPrice: 999,
|
||||
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">
|
||||
<h3 class="text-h3 pricing-title mb-2">
|
||||
{{ props.title ? props.title : 'Pricing Plans' }}
|
||||
</h3>
|
||||
<p class="mb-0">
|
||||
All plans include 40+ advanced tools and features to boost your product.
|
||||
</p>
|
||||
<p class="mb-2">
|
||||
Choose the best plan to fit your needs.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 👉 Annual and monthly price toggler -->
|
||||
|
||||
<div class="d-flex font-weight-medium text-body-1 align-center justify-center mx-auto mt-12 mb-6">
|
||||
<VLabel
|
||||
for="pricing-plan-toggle"
|
||||
class="me-3"
|
||||
>
|
||||
Monthly
|
||||
</VLabel>
|
||||
|
||||
<div class="position-relative">
|
||||
<VSwitch
|
||||
id="pricing-plan-toggle"
|
||||
v-model="annualMonthlyPlanPriceToggler"
|
||||
>
|
||||
<template #label>
|
||||
<div class="text-body-1 font-weight-medium">
|
||||
Annually
|
||||
</div>
|
||||
</template>
|
||||
</VSwitch>
|
||||
|
||||
<div class="save-upto-chip position-absolute align-center d-none d-md-flex gap-1">
|
||||
<VIcon
|
||||
icon="tabler-corner-left-down"
|
||||
size="24"
|
||||
class="flip-in-rtl mt-2 text-disabled"
|
||||
/>
|
||||
<VChip
|
||||
label
|
||||
color="primary"
|
||||
size="small"
|
||||
>
|
||||
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: 3.75rem;"
|
||||
class="text-end"
|
||||
>
|
||||
<!-- 👉 Popular -->
|
||||
<VChip
|
||||
v-show="plan.isPopular"
|
||||
label
|
||||
color="primary"
|
||||
size="small"
|
||||
>
|
||||
Popular
|
||||
</VChip>
|
||||
</VCardText>
|
||||
|
||||
<!-- 👉 Plan logo -->
|
||||
<VCardText>
|
||||
<VImg
|
||||
:height="120"
|
||||
:width="120"
|
||||
:src="plan.logo"
|
||||
class="mx-auto mb-5"
|
||||
/>
|
||||
|
||||
<!-- 👉 Plan name -->
|
||||
<h4 class="text-h4 mb-1 text-center">
|
||||
{{ plan.name }}
|
||||
</h4>
|
||||
<p class="mb-0 text-body-1 text-center">
|
||||
{{ plan.tagLine }}
|
||||
</p>
|
||||
|
||||
<!-- 👉 Plan price -->
|
||||
|
||||
<div class="position-relative">
|
||||
<div class="d-flex justify-center pt-5 pb-10">
|
||||
<div class="text-body-1 align-self-start font-weight-medium">
|
||||
$
|
||||
</div>
|
||||
<h1 class="text-h1 font-weight-medium text-primary">
|
||||
{{ annualMonthlyPlanPriceToggler ? Math.floor(Number(plan.yearlyPrice) / 12) : plan.monthlyPrice }}
|
||||
</h1>
|
||||
<div class="text-body-1 font-weight-medium align-self-end">
|
||||
/month
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 👉 Annual Price -->
|
||||
<span
|
||||
v-show="annualMonthlyPlanPriceToggler"
|
||||
class="annual-price-text position-absolute text-caption text-disabled pb-4"
|
||||
>
|
||||
{{ plan.yearlyPrice === 0 ? 'free' : `USD ${plan.yearlyPrice}/Year` }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 👉 Plan features -->
|
||||
|
||||
<VList class="card-list mb-4">
|
||||
<VListItem
|
||||
v-for="feature in plan.features"
|
||||
:key="feature"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon
|
||||
size="8"
|
||||
icon="tabler-circle-filled"
|
||||
color="rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<VListItemTitle class="text-body-1">
|
||||
{{ feature }}
|
||||
</VListItemTitle>
|
||||
</VListItem>
|
||||
</VList>
|
||||
|
||||
<!-- 👉 Plan actions -->
|
||||
<VBtn
|
||||
block
|
||||
:color="plan.current ? 'success' : 'primary'"
|
||||
:variant="plan.isPopular ? 'elevated' : 'tonal'"
|
||||
:to="{ name: 'front-pages-payment' }"
|
||||
:active="false"
|
||||
>
|
||||
{{ plan.yearlyPrice === 0 ? 'Your Current Plan' : 'Upgrade' }}
|
||||
</VBtn>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<!-- !SECTION -->
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-list {
|
||||
--v-card-list-gap: 1rem;
|
||||
}
|
||||
|
||||
.save-upto-chip {
|
||||
inset-block-start: -2.4rem;
|
||||
inset-inline-end: -6rem;
|
||||
}
|
||||
|
||||
.annual-price-text {
|
||||
inset-block-end: 3%;
|
||||
inset-inline-start: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
</style>
|
||||
77
resources/ts/components/AppSearchHeader.vue
Normal file
77
resources/ts/components/AppSearchHeader.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import AppSearchHeaderBg from '@images/pages/app-search-header-bg.png'
|
||||
|
||||
interface Props {
|
||||
title?: string
|
||||
subtitle?: string
|
||||
customClass?: string
|
||||
placeholder?: string
|
||||
density?: 'comfortable' | 'compact' | 'default'
|
||||
isReverse?: boolean
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
density: 'comfortable',
|
||||
isReverse: false,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 👉 Search Banner -->
|
||||
<VCard
|
||||
flat
|
||||
class="text-center search-header"
|
||||
:class="props.customClass"
|
||||
:style="`background: url(${AppSearchHeaderBg});`"
|
||||
>
|
||||
<VCardText>
|
||||
<slot name="title">
|
||||
<h4 class="text-h4 mb-2 font-weight-medium">
|
||||
{{ props.title }}
|
||||
</h4>
|
||||
</slot>
|
||||
<div
|
||||
class="d-flex"
|
||||
:class="isReverse ? 'flex-column' : 'flex-column-reverse' "
|
||||
>
|
||||
<p class="mb-0">
|
||||
{{ props.subtitle }}
|
||||
</p>
|
||||
<!-- 👉 Search Input -->
|
||||
<div>
|
||||
<AppTextField
|
||||
v-bind="$attrs"
|
||||
class="search-header-input mx-auto my-4"
|
||||
:placeholder="props.placeholder"
|
||||
:density="props.density"
|
||||
prepend-inner-icon="tabler-search"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.search-header {
|
||||
padding: 4rem !important;
|
||||
background-size: cover !important;
|
||||
}
|
||||
|
||||
// search input
|
||||
.search-header-input {
|
||||
border-radius: 0.375rem !important;
|
||||
background-color: rgb(var(--v-theme-surface));
|
||||
max-inline-size: 28.125rem !important;
|
||||
}
|
||||
|
||||
@media (max-width: 37.5rem) {
|
||||
.search-header {
|
||||
padding: 1.5rem !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
40
resources/ts/components/ErrorHeader.vue
Normal file
40
resources/ts/components/ErrorHeader.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
statusCode?: string | number
|
||||
title?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text-center">
|
||||
<!-- 👉 Title and subtitle -->
|
||||
<h1
|
||||
v-if="props.statusCode"
|
||||
class="header-title font-weight-medium mb-2"
|
||||
>
|
||||
{{ props.statusCode }}
|
||||
</h1>
|
||||
<h4
|
||||
v-if="props.title"
|
||||
class="text-h4 font-weight-medium mb-2"
|
||||
>
|
||||
{{ props.title }}
|
||||
</h4>
|
||||
<p
|
||||
v-if="props.description"
|
||||
class="text-body-1 mb-6"
|
||||
>
|
||||
{{ props.description }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-title {
|
||||
font-size: clamp(3rem, 5vw, 6rem);
|
||||
line-height: clamp(3rem, 5vw, 6rem);
|
||||
}
|
||||
</style>
|
||||
101
resources/ts/components/dialogs/AddAuthenticatorAppDialog.vue
Normal file
101
resources/ts/components/dialogs/AddAuthenticatorAppDialog.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<script setup lang="ts">
|
||||
import themeselectionQr from '@images/pages/themeselection-qr.png'
|
||||
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', value: boolean): void
|
||||
(e: 'submit', value: string): void
|
||||
}
|
||||
interface Props {
|
||||
authCode?: string
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
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
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 900"
|
||||
:model-value="props.isDialogVisible"
|
||||
@update:model-value="(val) => $emit('update:isDialogVisible', val)"
|
||||
>
|
||||
<!-- Dialog close btn -->
|
||||
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
|
||||
|
||||
<VCard class="pa-2 pa-sm-10">
|
||||
<VCardText>
|
||||
<!-- 👉 Title -->
|
||||
<h4 class="text-h4 text-center mb-6">
|
||||
Add Authenticator App
|
||||
</h4>
|
||||
<h5 class="text-h5 mb-2">
|
||||
Authenticator Apps
|
||||
</h5>
|
||||
|
||||
<p class="text-body-1 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-6">
|
||||
<VImg
|
||||
width="150"
|
||||
:src="themeselectionQr"
|
||||
class="mx-auto"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<VAlert
|
||||
title="ASDLKNASDA9AHS678dGhASD78AB"
|
||||
text="If you are unable to scan the QR code, you can manually enter the secret key below."
|
||||
variant="tonal"
|
||||
color="warning"
|
||||
/>
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<AppTextField
|
||||
v-model="authCode"
|
||||
name="auth-code"
|
||||
label="Enter Authentication Code"
|
||||
placeholder="123 456"
|
||||
class="mt-4 mb-6"
|
||||
/>
|
||||
|
||||
<div class="d-flex justify-end flex-wrap gap-4">
|
||||
<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>
|
||||
238
resources/ts/components/dialogs/AddEditAddressDialog.vue
Normal file
238
resources/ts/components/dialogs/AddEditAddressDialog.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<script setup lang="ts">
|
||||
import home from '@images/svg/home.svg'
|
||||
import office from '@images/svg/office.svg'
|
||||
|
||||
interface BillingAddress {
|
||||
firstName: string | undefined
|
||||
lastName: string | undefined
|
||||
selectedCountry: string | null
|
||||
addressLine1: string
|
||||
addressLine2: string
|
||||
landmark: string
|
||||
contact: string
|
||||
country: string | null
|
||||
city: string
|
||||
state: string
|
||||
zipCode: number | null
|
||||
}
|
||||
interface Props {
|
||||
billingAddress?: BillingAddress
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', value: boolean): void
|
||||
(e: 'submit', value: BillingAddress): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
billingAddress: () => ({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
selectedCountry: null,
|
||||
addressLine1: '',
|
||||
addressLine2: '',
|
||||
landmark: '',
|
||||
contact: '',
|
||||
country: null,
|
||||
city: '',
|
||||
state: '',
|
||||
zipCode: null,
|
||||
}),
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const billingAddress = ref<BillingAddress>(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: home, size: '28' },
|
||||
title: 'Home',
|
||||
desc: 'Delivery Time (9am - 9pm)',
|
||||
value: 'Home',
|
||||
},
|
||||
{
|
||||
icon: { icon: office, size: '28' },
|
||||
title: 'Office',
|
||||
desc: 'Delivery Time (9am - 5pm)',
|
||||
value: 'Office',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 900 "
|
||||
: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-10 pa-2"
|
||||
>
|
||||
<VCardText>
|
||||
<!-- 👉 Title -->
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
{{ (props.billingAddress.addressLine1 || props.billingAddress.addressLine2) ? 'Edit' : 'Add New' }} Address
|
||||
</h4>
|
||||
<p class="text-body-1 text-center mb-6">
|
||||
Add new address for express delivery
|
||||
</p>
|
||||
|
||||
<div class="d-flex mb-6">
|
||||
<CustomRadiosWithIcon
|
||||
v-model:selected-radio="selectedAddress"
|
||||
:radio-content="addressTypes"
|
||||
:grid-column="{ sm: '6', cols: '12' }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 👉 Form -->
|
||||
<VForm @submit.prevent="onFormSubmit">
|
||||
<VRow>
|
||||
<!-- 👉 First Name -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="billingAddress.firstName"
|
||||
label="First Name"
|
||||
placeholder="John"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Last Name -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="billingAddress.lastName"
|
||||
label="Last Name"
|
||||
placeholder="Doe"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Select Country -->
|
||||
<VCol cols="12">
|
||||
<AppSelect
|
||||
v-model="billingAddress.selectedCountry"
|
||||
label="Select Country"
|
||||
placeholder="Select Country"
|
||||
:items="['USA', 'Aus', 'Canada', 'NZ']"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Address Line 1 -->
|
||||
<VCol cols="12">
|
||||
<AppTextField
|
||||
v-model="billingAddress.addressLine1"
|
||||
label="Address Line 1"
|
||||
placeholder="12, Business Park"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Address Line 2 -->
|
||||
<VCol cols="12">
|
||||
<AppTextField
|
||||
v-model="billingAddress.addressLine2"
|
||||
label="Address Line 2"
|
||||
placeholder="Mall Road"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Landmark -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="billingAddress.landmark"
|
||||
label="Landmark"
|
||||
placeholder="Nr. Hard Rock Cafe"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 City -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="billingAddress.city"
|
||||
label="City"
|
||||
placeholder="Los Angeles"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 State -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="billingAddress.state"
|
||||
label="State"
|
||||
placeholder="California"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Zip Code -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="billingAddress.zipCode"
|
||||
label="Zip Code"
|
||||
placeholder="99950"
|
||||
type="number"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<VCol cols="12">
|
||||
<VSwitch label="Use as a billing address?" />
|
||||
</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>
|
||||
93
resources/ts/components/dialogs/AddEditPermissionDialog.vue
Normal file
93
resources/ts/components/dialogs/AddEditPermissionDialog.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
isDialogVisible: boolean
|
||||
permissionName?: string
|
||||
}
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', value: boolean): void
|
||||
(e: 'update:permissionName', value: string): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
permissionName: '',
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const currentPermissionName = ref('')
|
||||
|
||||
const onReset = () => {
|
||||
emit('update:isDialogVisible', false)
|
||||
currentPermissionName.value = ''
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
emit('update:isDialogVisible', false)
|
||||
emit('update:permissionName', currentPermissionName.value)
|
||||
}
|
||||
|
||||
watch(() => props, () => {
|
||||
currentPermissionName.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-2 pa-sm-10">
|
||||
<VCardText>
|
||||
<!-- 👉 Title -->
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
{{ props.permissionName ? 'Edit' : 'Add' }} Permission
|
||||
</h4>
|
||||
<p class="text-body-1 text-center mb-6">
|
||||
{{ props.permissionName ? 'Edit' : 'Add' }} permission as per your requirements.
|
||||
</p>
|
||||
|
||||
<!-- 👉 Form -->
|
||||
<VForm>
|
||||
<VAlert
|
||||
type="warning"
|
||||
title="Warning!"
|
||||
variant="tonal"
|
||||
class="mb-6"
|
||||
>
|
||||
<template #text>
|
||||
By {{ props.permissionName ? 'editing' : 'adding' }} the permission name, you might break the system permissions functionality.
|
||||
</template>
|
||||
</VAlert>
|
||||
|
||||
<!-- 👉 Role name -->
|
||||
<div class="d-flex gap-4 mb-6 flex-wrap flex-column flex-sm-row">
|
||||
<AppTextField
|
||||
v-model="currentPermissionName"
|
||||
placeholder="Enter Permission Name"
|
||||
/>
|
||||
|
||||
<VBtn @click="onSubmit">
|
||||
{{ props.permissionName ? 'Update' : 'Add' }}
|
||||
</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>
|
||||
302
resources/ts/components/dialogs/AddEditRoleDialog.vue
Normal file
302
resources/ts/components/dialogs/AddEditRoleDialog.vue
Normal file
@@ -0,0 +1,302 @@
|
||||
<script setup lang="ts">
|
||||
import { VForm } from 'vuetify/components/VForm'
|
||||
|
||||
interface Permission {
|
||||
name: string
|
||||
read: boolean
|
||||
write: boolean
|
||||
create: boolean
|
||||
}
|
||||
|
||||
interface Roles {
|
||||
name: string
|
||||
permissions: Permission[]
|
||||
}
|
||||
|
||||
interface Props {
|
||||
rolePermissions?: Roles
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', value: boolean): void
|
||||
(e: 'update:rolePermissions', value: Roles): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
rolePermissions: () => ({
|
||||
name: '',
|
||||
permissions: [],
|
||||
}),
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
// 👉 Permission List
|
||||
const permissions = ref<Permission[]>([
|
||||
{
|
||||
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<VForm>()
|
||||
|
||||
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-10 pa-2">
|
||||
<VCardText>
|
||||
<!-- 👉 Title -->
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
{{ props.rolePermissions.name ? 'Edit' : 'Add New' }} Role
|
||||
</h4>
|
||||
<p class="text-body-1 text-center mb-6">
|
||||
Set Role Permissions
|
||||
</p>
|
||||
|
||||
<!-- 👉 Form -->
|
||||
<VForm ref="refPermissionForm">
|
||||
<!-- 👉 Role name -->
|
||||
<AppTextField
|
||||
v-model="role"
|
||||
label="Role Name"
|
||||
placeholder="Enter Role Name"
|
||||
/>
|
||||
|
||||
<h5 class="text-h5 my-6">
|
||||
Role Permissions
|
||||
</h5>
|
||||
|
||||
<!-- 👉 Role Permissions -->
|
||||
|
||||
<VTable class="permission-table text-no-wrap mb-6">
|
||||
<!-- 👉 Admin -->
|
||||
<tr>
|
||||
<td>
|
||||
<h6 class="text-h6">
|
||||
Administrator Access
|
||||
</h6>
|
||||
</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>
|
||||
<h6 class="text-h6">
|
||||
{{ permission.name }}
|
||||
</h6>
|
||||
</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-4">
|
||||
<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>
|
||||
120
resources/ts/components/dialogs/AddPaymentMethodDialog.vue
Normal file
120
resources/ts/components/dialogs/AddPaymentMethodDialog.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<script setup lang="ts">
|
||||
import americanExDark from '@images/icons/payments/img/ae-dark.png'
|
||||
import americanExLight from '@images/icons/payments/img/american-express.png'
|
||||
import dcDark from '@images/icons/payments/img/dc-dark.png'
|
||||
import dcLight from '@images/icons/payments/img/dc-light.png'
|
||||
import jcbDark from '@images/icons/payments/img/jcb-dark.png'
|
||||
import jcbLight from '@images/icons/payments/img/jcb-light.png'
|
||||
import masterCardDark from '@images/icons/payments/img/master-dark.png'
|
||||
import masterCardLight from '@images/icons/payments/img/mastercard.png'
|
||||
import visaDark from '@images/icons/payments/img/visa-dark.png'
|
||||
import visaLight from '@images/icons/payments/img/visa-light.png'
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const visa = useGenerateImageVariant(visaLight, visaDark)
|
||||
const masterCard = useGenerateImageVariant(masterCardLight, masterCardDark)
|
||||
const americanEx = useGenerateImageVariant(americanExLight, americanExDark)
|
||||
const jcb = useGenerateImageVariant(jcbLight, jcbDark)
|
||||
const dc = useGenerateImageVariant(dcLight, dcDark)
|
||||
|
||||
interface Props {
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', val: boolean): void
|
||||
}
|
||||
|
||||
const dialogVisibleUpdate = (val: boolean) => {
|
||||
emit('update:isDialogVisible', val)
|
||||
}
|
||||
|
||||
const paymentMethodsData = [
|
||||
{
|
||||
title: 'Visa',
|
||||
type: 'Credit Card',
|
||||
img: visa,
|
||||
},
|
||||
{
|
||||
title: 'American Express',
|
||||
type: 'Credit Card',
|
||||
img: americanEx,
|
||||
},
|
||||
{
|
||||
title: 'Mastercard',
|
||||
type: 'Credit Card',
|
||||
img: masterCard,
|
||||
},
|
||||
{
|
||||
title: 'JCB',
|
||||
type: 'Credit Card',
|
||||
img: jcb,
|
||||
},
|
||||
{
|
||||
title: 'Diners Club',
|
||||
type: 'Credit Card',
|
||||
img: dc,
|
||||
},
|
||||
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:model-value="props.isDialogVisible"
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 750"
|
||||
@update:model-value="dialogVisibleUpdate"
|
||||
>
|
||||
<!-- 👉 dialog close btn -->
|
||||
<DialogCloseBtn @click="emit('update:isDialogVisible', false)" />
|
||||
|
||||
<VCard class="pa-2 pa-sm-10">
|
||||
<VCardText>
|
||||
<!-- 👉 Title -->
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
Add payment methods
|
||||
</h4>
|
||||
<p class="text-body-1 text-center mb-6">
|
||||
Supported payment methods
|
||||
</p>
|
||||
|
||||
<div
|
||||
v-for="(item, index) in paymentMethodsData"
|
||||
:key="index"
|
||||
>
|
||||
<div class="d-flex justify-space-between align-center py-4 gap-x-4">
|
||||
<div class="d-flex align-center">
|
||||
<VImg
|
||||
:src="item.img.value"
|
||||
height="30"
|
||||
width="50"
|
||||
class="me-4"
|
||||
/>
|
||||
<h6 class="text-h6">
|
||||
{{ item.title }}
|
||||
</h6>
|
||||
</div>
|
||||
<div class="d-none d-sm-block text-body-1">
|
||||
{{ item.type }}
|
||||
</div>
|
||||
</div>
|
||||
<VDivider v-if="index !== paymentMethodsData.length - 1" />
|
||||
</div>
|
||||
</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>
|
||||
153
resources/ts/components/dialogs/CardAddEditDialog.vue
Normal file
153
resources/ts/components/dialogs/CardAddEditDialog.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<script setup lang="ts">
|
||||
interface Details {
|
||||
number: string | number
|
||||
name: string
|
||||
expiry: string
|
||||
cvv: string
|
||||
isPrimary: boolean
|
||||
type: string
|
||||
}
|
||||
interface Emit {
|
||||
(e: 'submit', value: Details): void
|
||||
(e: 'update:isDialogVisible', value: boolean): void
|
||||
}
|
||||
|
||||
interface Props {
|
||||
cardDetails?: Details
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
cardDetails: () => ({
|
||||
number: '',
|
||||
name: '',
|
||||
expiry: '',
|
||||
cvv: '',
|
||||
isPrimary: false,
|
||||
type: '',
|
||||
}),
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const cardDetails = ref<Details>(structuredClone(toRaw(props.cardDetails)))
|
||||
|
||||
watch(() => props, () => {
|
||||
cardDetails.value = structuredClone(toRaw(props.cardDetails))
|
||||
})
|
||||
|
||||
const formSubmit = () => {
|
||||
emit('submit', cardDetails.value)
|
||||
}
|
||||
|
||||
const dialogModelValueUpdate = (val: boolean) => {
|
||||
emit('update:isDialogVisible', val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 600"
|
||||
:model-value="props.isDialogVisible"
|
||||
@update:model-value="dialogModelValueUpdate"
|
||||
>
|
||||
<!-- Dialog close btn -->
|
||||
<DialogCloseBtn @click="dialogModelValueUpdate(false)" />
|
||||
|
||||
<VCard class="pa-2 pa-sm-10">
|
||||
<!-- 👉 Title -->
|
||||
<VCardItem class="text-center">
|
||||
<VCardTitle>
|
||||
<h4 class="text-h4 mb-2">
|
||||
{{ props.cardDetails.name ? 'Edit Card' : 'Add New Card' }}
|
||||
</h4>
|
||||
</VCardTitle>
|
||||
<p class="text-body-1 mb-0">
|
||||
{{ props.cardDetails.name ? 'Edit your saved card details' : 'Add card for future billing' }}
|
||||
</p>
|
||||
</VCardItem>
|
||||
|
||||
<VCardText class="pt-6">
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<VRow>
|
||||
<!-- 👉 Card Number -->
|
||||
<VCol cols="12">
|
||||
<AppTextField
|
||||
v-model="cardDetails.number"
|
||||
label="Card Number"
|
||||
placeholder="1356 3215 6548 7898"
|
||||
type="number"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Card Name -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="cardDetails.name"
|
||||
label="Name"
|
||||
placeholder="John Doe"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Card Expiry -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="3"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="cardDetails.expiry"
|
||||
label="Expiry Date"
|
||||
placeholder="MM/YY"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Card CVV -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="3"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="cardDetails.cvv"
|
||||
type="number"
|
||||
label="CVV Code"
|
||||
placeholder="654"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Card Primary Set -->
|
||||
<VCol cols="12">
|
||||
<VSwitch
|
||||
v-model="cardDetails.isPrimary"
|
||||
label="Save Card for future billing?"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Card actions -->
|
||||
<VCol
|
||||
cols="12"
|
||||
class="text-center"
|
||||
>
|
||||
<VBtn
|
||||
class="me-4"
|
||||
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>
|
||||
151
resources/ts/components/dialogs/ConfirmDialog.vue
Normal file
151
resources/ts/components/dialogs/ConfirmDialog.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
confirmationQuestion: string
|
||||
isDialogVisible: boolean
|
||||
confirmTitle: string
|
||||
confirmMsg: string
|
||||
cancelTitle: string
|
||||
cancelMsg: string
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', value: boolean): void
|
||||
(e: 'confirm', value: boolean): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const unsubscribed = ref(false)
|
||||
const cancelled = ref(false)
|
||||
|
||||
const updateModelValue = (val: boolean) => {
|
||||
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;"
|
||||
>
|
||||
<VIcon
|
||||
icon="tabler-check"
|
||||
size="38"
|
||||
/>
|
||||
</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>
|
||||
459
resources/ts/components/dialogs/CreateAppDialog.vue
Normal file
459
resources/ts/components/dialogs/CreateAppDialog.vue
Normal file
@@ -0,0 +1,459 @@
|
||||
<script setup lang="ts">
|
||||
import laptopGirl from '@images/illustrations/laptop-girl.png'
|
||||
|
||||
const props = defineProps<{
|
||||
isDialogVisible: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:isDialogVisible', val: boolean): void
|
||||
(e: 'updatedData', val: unknown): void
|
||||
}>()
|
||||
|
||||
const currentStep = ref(0)
|
||||
|
||||
const createApp = [
|
||||
{
|
||||
icon: 'tabler-file-text',
|
||||
title: 'DETAILS',
|
||||
subtitle: 'Enter Details',
|
||||
},
|
||||
{
|
||||
icon: 'tabler-id',
|
||||
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-file-text',
|
||||
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-vue',
|
||||
color: 'success',
|
||||
title: 'Vue',
|
||||
subtitle: 'JS web frameworks',
|
||||
slug: 'js-framework',
|
||||
},
|
||||
{
|
||||
icon: 'tabler-brand-html5',
|
||||
color: 'warning',
|
||||
title: 'HTML',
|
||||
subtitle: 'Progressive Framework',
|
||||
slug: 'html-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: 'js-framework',
|
||||
database: 'firebase-database',
|
||||
cardNumber: null,
|
||||
cardName: '',
|
||||
cardExpiry: '',
|
||||
cardCvv: '',
|
||||
isSave: true,
|
||||
})
|
||||
|
||||
const dialogVisibleUpdate = (val: boolean) => {
|
||||
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"
|
||||
min-height="590"
|
||||
@update:model-value="dialogVisibleUpdate"
|
||||
>
|
||||
<!-- 👉 dialog close btn -->
|
||||
<DialogCloseBtn
|
||||
size="small"
|
||||
@click="emit('update:isDialogVisible', false)"
|
||||
/>
|
||||
<VCard
|
||||
class="create-app-dialog"
|
||||
min-height="590"
|
||||
>
|
||||
<VCardText class="pa-5 pa-sm-16">
|
||||
<!-- 👉 Title -->
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
Create App
|
||||
</h4>
|
||||
<p class="text-body-1 text-center mb-6">
|
||||
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="22"
|
||||
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"
|
||||
placeholder="Application Name"
|
||||
/>
|
||||
|
||||
<h5 class="text-h5 mt-6 mb-4">
|
||||
Category
|
||||
</h5>
|
||||
<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="46"
|
||||
rounded
|
||||
variant="tonal"
|
||||
:color="category.color"
|
||||
>
|
||||
<VIcon
|
||||
:icon="category.icon"
|
||||
size="30"
|
||||
/>
|
||||
</VAvatar>
|
||||
</template>
|
||||
|
||||
<VListItemTitle>
|
||||
<h6 class="text-h6 mb-1">
|
||||
{{ category.title }}
|
||||
</h6>
|
||||
</VListItemTitle>
|
||||
<VListItemSubtitle>
|
||||
{{ category.subtitle }}
|
||||
</VListItemSubtitle>
|
||||
|
||||
<template #append>
|
||||
<VRadio :value="category.slug" />
|
||||
</template>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VRadioGroup>
|
||||
</VWindowItem>
|
||||
|
||||
<!-- 👉 Frameworks -->
|
||||
<VWindowItem>
|
||||
<h5 class="text-h5 mb-4">
|
||||
Select Framework
|
||||
</h5>
|
||||
<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="46"
|
||||
rounded
|
||||
variant="tonal"
|
||||
:color="framework.color"
|
||||
>
|
||||
<VIcon
|
||||
:icon="framework.icon"
|
||||
size="30"
|
||||
/>
|
||||
</VAvatar>
|
||||
</template>
|
||||
<VListItemTitle>
|
||||
<h6 class="text-h6 mb-1">
|
||||
{{ framework.title }}
|
||||
</h6>
|
||||
</VListItemTitle>
|
||||
<VListItemSubtitle>
|
||||
{{ framework.subtitle }}
|
||||
</VListItemSubtitle>
|
||||
<template #append>
|
||||
<VRadio :value="framework.slug" />
|
||||
</template>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VRadioGroup>
|
||||
</VWindowItem>
|
||||
|
||||
<!-- 👉 Database Engine -->
|
||||
<VWindowItem>
|
||||
<AppTextField
|
||||
label="Database Name"
|
||||
placeholder="UserDB"
|
||||
/>
|
||||
|
||||
<h5 class="text-h5 mt-6 mb-4">
|
||||
Select Database Engine
|
||||
</h5>
|
||||
<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="46"
|
||||
rounded
|
||||
variant="tonal"
|
||||
:color="database.color"
|
||||
>
|
||||
<VIcon
|
||||
:icon="database.icon"
|
||||
size="30"
|
||||
/>
|
||||
</VAvatar>
|
||||
</template>
|
||||
<VListItemTitle>
|
||||
<h6 class="text-h6 mb-1">
|
||||
{{ database.title }}
|
||||
</h6>
|
||||
</VListItemTitle>
|
||||
<VListItemSubtitle>
|
||||
{{ database.subtitle }}
|
||||
</VListItemSubtitle>
|
||||
<template #append>
|
||||
<VRadio :value="database.slug" />
|
||||
</template>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VRadioGroup>
|
||||
</VWindowItem>
|
||||
|
||||
<!-- 👉 Billing form -->
|
||||
<VWindowItem>
|
||||
<h6 class="text-h6 mb-6">
|
||||
Payment Details
|
||||
</h6>
|
||||
|
||||
<VForm>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<AppTextField
|
||||
v-model="createAppData.cardNumber"
|
||||
label="Card Number"
|
||||
placeholder="1234 1234 1234 1234"
|
||||
type="number"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="createAppData.cardName"
|
||||
label="Name on Card"
|
||||
placeholder="John Doe"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<VCol
|
||||
cols="6"
|
||||
md="3"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="createAppData.cardExpiry"
|
||||
label="Expiry"
|
||||
placeholder="MM/YY"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<VCol
|
||||
cols="6"
|
||||
md="3"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="createAppData.cardCvv"
|
||||
label="CVV"
|
||||
placeholder="123"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<VCol cols="12">
|
||||
<VSwitch
|
||||
v-model="createAppData.isSave"
|
||||
label="Save Card for future billing?"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
</VWindowItem>
|
||||
|
||||
<VWindowItem class="text-center">
|
||||
<h5 class="text-h5 mb-1">
|
||||
Submit
|
||||
</h5>
|
||||
<p class="text-sm mb-4">
|
||||
Submit to kickstart your project.
|
||||
</p>
|
||||
|
||||
<VImg
|
||||
:src="laptopGirl"
|
||||
width="176"
|
||||
class="mx-auto"
|
||||
/>
|
||||
</VWindowItem>
|
||||
</VWindow>
|
||||
|
||||
<div class="d-flex justify-space-between mt-6">
|
||||
<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
|
||||
</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: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,85 @@
|
||||
<script setup lang="ts">
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', value: boolean): void
|
||||
(e: 'submit', value: string): void
|
||||
}
|
||||
interface Props {
|
||||
mobileNumber?: string
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
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: boolean) => {
|
||||
emit('update:isDialogVisible', val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 900"
|
||||
:model-value="props.isDialogVisible"
|
||||
@update:model-value="dialogModelValueUpdate"
|
||||
>
|
||||
<!-- Dialog close btn -->
|
||||
<DialogCloseBtn @click="dialogModelValueUpdate(false)" />
|
||||
|
||||
<VCard class="pa-2 pa-sm-10">
|
||||
<VCardText>
|
||||
<!-- 👉 Title -->
|
||||
<h5 class="text-h5 mb-2">
|
||||
Verify Your Mobile Number for SMS
|
||||
</h5>
|
||||
<p class="text-body-1 mb-6">
|
||||
Enter your mobile phone number with country code and we will send you a verification code.
|
||||
</p>
|
||||
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<AppTextField
|
||||
v-model="phoneNumber"
|
||||
name="mobile"
|
||||
label="Phone Number"
|
||||
placeholder="+1 123 456 7890"
|
||||
type="number"
|
||||
class="mb-6"
|
||||
/>
|
||||
|
||||
<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>
|
||||
122
resources/ts/components/dialogs/PaymentProvidersDialog.vue
Normal file
122
resources/ts/components/dialogs/PaymentProvidersDialog.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<script setup lang="ts">
|
||||
import americanExDark from '@images/icons/payments/img/ae-dark.png'
|
||||
import americanExLight from '@images/icons/payments/img/american-express.png'
|
||||
import dcDark from '@images/icons/payments/img/dc-dark.png'
|
||||
import dcLight from '@images/icons/payments/img/dc-light.png'
|
||||
import jcbDark from '@images/icons/payments/img/jcb-dark.png'
|
||||
import jcbLight from '@images/icons/payments/img/jcb-light.png'
|
||||
import masterCardDark from '@images/icons/payments/img/master-dark.png'
|
||||
import masterCardLight from '@images/icons/payments/img/mastercard.png'
|
||||
import visaDark from '@images/icons/payments/img/visa-dark.png'
|
||||
import visaLight from '@images/icons/payments/img/visa-light.png'
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emit>()
|
||||
const visa = useGenerateImageVariant(visaLight, visaDark)
|
||||
const masterCard = useGenerateImageVariant(masterCardLight, masterCardDark)
|
||||
const americanEx = useGenerateImageVariant(americanExLight, americanExDark)
|
||||
const jcb = useGenerateImageVariant(jcbLight, jcbDark)
|
||||
const dc = useGenerateImageVariant(dcLight, dcDark)
|
||||
|
||||
interface Props {
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', val: boolean): void
|
||||
}
|
||||
|
||||
const dialogVisibleUpdate = (val: boolean) => {
|
||||
emit('update:isDialogVisible', val)
|
||||
}
|
||||
|
||||
const paymentProvidersData = [
|
||||
{
|
||||
title: 'Adyen',
|
||||
providers: [visa, masterCard, americanEx, jcb, dc],
|
||||
},
|
||||
{
|
||||
title: '2Checkout',
|
||||
providers: [visa, americanEx, jcb, dc],
|
||||
},
|
||||
{
|
||||
title: 'Airpay',
|
||||
providers: [visa, americanEx, masterCard, jcb],
|
||||
},
|
||||
{
|
||||
title: 'Authorize.net',
|
||||
providers: [americanEx, jcb, dc],
|
||||
},
|
||||
{
|
||||
title: 'Bambora',
|
||||
providers: [masterCard, americanEx, jcb],
|
||||
},
|
||||
{
|
||||
title: 'Bambora',
|
||||
providers: [visa, masterCard, americanEx, jcb, dc],
|
||||
},
|
||||
{
|
||||
title: 'Chase Paymentech (Orbital)',
|
||||
providers: [visa, americanEx, jcb, dc],
|
||||
},
|
||||
{
|
||||
title: 'Checkout.com',
|
||||
providers: [visa, masterCard],
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:model-value="props.isDialogVisible"
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 900"
|
||||
@update:model-value="dialogVisibleUpdate"
|
||||
>
|
||||
<DialogCloseBtn @click="emit('update:isDialogVisible', false)" />
|
||||
|
||||
<VCard class="pa-2 pa-sm-10">
|
||||
<VCardText>
|
||||
<!-- 👉 Title -->
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
Select Payment Providers
|
||||
</h4>
|
||||
<p class="text-body-1 text-center mb-6">
|
||||
Third-party payment providers
|
||||
</p>
|
||||
|
||||
<div
|
||||
v-for="(item, index) in paymentProvidersData"
|
||||
:key="index"
|
||||
>
|
||||
<div class="d-flex flex-column flex-sm-row justify-space-between gap-4 flex-wrap py-4">
|
||||
<h6 class="text-h6">
|
||||
{{ item.title }}
|
||||
</h6>
|
||||
<div class="d-flex gap-4 flex-wrap">
|
||||
<img
|
||||
v-for="(img, iterator) in item.providers"
|
||||
:key="iterator"
|
||||
:src="img.value"
|
||||
height="30"
|
||||
width="50"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<VDivider v-if="index !== paymentProvidersData.length - 1" />
|
||||
</div>
|
||||
</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>
|
||||
34
resources/ts/components/dialogs/PricingPlanDialog.vue
Normal file
34
resources/ts/components/dialogs/PricingPlanDialog.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', val: boolean): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const dialogVisibleUpdate = (val: boolean) => {
|
||||
emit('update:isDialogVisible', val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:model-value="props.isDialogVisible"
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 1200"
|
||||
@update:model-value="dialogVisibleUpdate"
|
||||
>
|
||||
<!-- 👉 Dialog close btn -->
|
||||
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
|
||||
|
||||
<VCard class="pricing-dialog pa-2 pa-sm-10">
|
||||
<VCardText>
|
||||
<AppPricing md="4" />
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</template>
|
||||
181
resources/ts/components/dialogs/ReferAndEarnDialog.vue
Normal file
181
resources/ts/components/dialogs/ReferAndEarnDialog.vue
Normal file
@@ -0,0 +1,181 @@
|
||||
<script setup lang="ts">
|
||||
import keyboard from '@images/svg/keyboard.svg'
|
||||
import paper from '@images/svg/paper-send.svg'
|
||||
import rocket from '@images/svg/rocket.svg'
|
||||
import { themeConfig } from '@themeConfig'
|
||||
|
||||
interface Props {
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', val: boolean): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const dialogVisibleUpdate = (val: boolean) => {
|
||||
emit('update:isDialogVisible', val)
|
||||
}
|
||||
|
||||
const referAndEarnSteps = [
|
||||
{
|
||||
icon: paper,
|
||||
title: 'Send Invitation 👍🏻',
|
||||
subtitle: 'Send your referral link to your friend',
|
||||
},
|
||||
{
|
||||
icon: keyboard,
|
||||
title: 'Registration 😎',
|
||||
subtitle: 'Let them register to our services',
|
||||
},
|
||||
{
|
||||
icon: rocket,
|
||||
title: 'Free Trial 🎉',
|
||||
subtitle: 'Your friend will get 30 days free trial',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:model-value="props.isDialogVisible"
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 800"
|
||||
@update:model-value="dialogVisibleUpdate"
|
||||
>
|
||||
<!-- 👉 Dialog close btn -->
|
||||
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
|
||||
|
||||
<VCard class="pa-2 pa-sm-10">
|
||||
<VCardText>
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
Refer & Earn
|
||||
</h4>
|
||||
<p class="text-body-1 mb-6 text-center">
|
||||
Invite your friend to <span class="text-capitalize">{{ themeConfig.app.title }}</span>, 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="88"
|
||||
color="primary"
|
||||
rounded
|
||||
>
|
||||
<VIcon
|
||||
size="40"
|
||||
:icon="step.icon"
|
||||
/>
|
||||
</VAvatar>
|
||||
|
||||
<h5 class="text-h5 mt-4 mb-2">
|
||||
{{ step.title }}
|
||||
</h5>
|
||||
<div>{{ step.subtitle }}</div>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
<VDivider class="mt-12 mb-6" />
|
||||
|
||||
<h5 class="text-h5 mb-6">
|
||||
Invite your friends
|
||||
</h5>
|
||||
|
||||
<VForm
|
||||
class="d-flex align-center flex-wrap gap-4"
|
||||
@submit.prevent="() => {}"
|
||||
>
|
||||
<AppTextField
|
||||
placeholder="johnDoe@gmail.com"
|
||||
label="Enter your friend's email address and invite them to join Vuexy 😍"
|
||||
/>
|
||||
|
||||
<VBtn
|
||||
class="align-self-end"
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</VBtn>
|
||||
</VForm>
|
||||
|
||||
<h5 class="text-h5 my-6">
|
||||
Share the referral link
|
||||
</h5>
|
||||
|
||||
<VForm
|
||||
class="d-flex align-center flex-wrap gap-4"
|
||||
@submit.prevent="() => {}"
|
||||
>
|
||||
<AppTextField
|
||||
placeholder="http://pixinvent.link"
|
||||
label="You can also copy and send it or share it on your social media. 🚀"
|
||||
class="refer-link-input"
|
||||
>
|
||||
<template #append-inner>
|
||||
<VBtn variant="text">
|
||||
Copy link
|
||||
</VBtn>
|
||||
</template>
|
||||
</AppTextField>
|
||||
|
||||
<div class="d-flex align-self-end gap-1">
|
||||
<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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
198
resources/ts/components/dialogs/ShareProjectDialog.vue
Normal file
198
resources/ts/components/dialogs/ShareProjectDialog.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<script setup lang="ts">
|
||||
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'
|
||||
|
||||
interface Props {
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', val: boolean): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const dialogVisibleUpdate = (val: boolean) => {
|
||||
emit('update:isDialogVisible', val)
|
||||
}
|
||||
|
||||
type Permission = 'Owner' | 'Can Edit' | 'Can Comment' | 'Can View'
|
||||
|
||||
interface Member {
|
||||
avatar: string
|
||||
name: string
|
||||
email: string
|
||||
permission: Permission
|
||||
}
|
||||
|
||||
const membersList: Member[] = [
|
||||
{
|
||||
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"
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 900"
|
||||
@update:model-value="dialogVisibleUpdate"
|
||||
>
|
||||
<!-- 👉 Dialog close btn -->
|
||||
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
|
||||
|
||||
<VCard class="share-project-dialog pa-2 pa-sm-10">
|
||||
<VCardText>
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
Share Project
|
||||
</h4>
|
||||
<p class="text-body-1 text-center mb-6">
|
||||
Share project with a team members
|
||||
</p>
|
||||
|
||||
<AppAutocomplete
|
||||
label="Add Members"
|
||||
:items="membersList"
|
||||
item-title="name"
|
||||
item-value="name"
|
||||
placeholder="Add project members..."
|
||||
>
|
||||
<template #item="{ props: listItemProp, item }">
|
||||
<VListItem v-bind="listItemProp">
|
||||
<template #prepend>
|
||||
<VAvatar
|
||||
:image="item.raw.avatar"
|
||||
size="30"
|
||||
/>
|
||||
</template>
|
||||
</VListItem>
|
||||
</template>
|
||||
</AppAutocomplete>
|
||||
|
||||
<h5 class="text-h5 mb-4 mt-6">
|
||||
8 Members
|
||||
</h5>
|
||||
|
||||
<VList class="card-list">
|
||||
<VListItem
|
||||
v-for="member in membersList"
|
||||
:key="member.name"
|
||||
>
|
||||
<template #prepend>
|
||||
<VAvatar :image="member.avatar" />
|
||||
</template>
|
||||
|
||||
<VListItemTitle>
|
||||
{{ member.name }}
|
||||
</VListItemTitle>
|
||||
<VListItemSubtitle>
|
||||
{{ member.email }}
|
||||
</VListItemSubtitle>
|
||||
|
||||
<template #append>
|
||||
<VBtn
|
||||
variant="text"
|
||||
color="secondary"
|
||||
:icon="$vuetify.display.xs"
|
||||
>
|
||||
<span class="d-none d-sm-block me-1">{{ 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-center justify-sm-space-between flex-wrap gap-3 mt-6">
|
||||
<h6 class="text-h6 font-weight-medium d-flex align-start">
|
||||
<VIcon
|
||||
icon="tabler-users"
|
||||
class="me-2"
|
||||
size="20"
|
||||
/>
|
||||
<div>Public to Vuexy - Pixinvent</div>
|
||||
</h6>
|
||||
|
||||
<VBtn
|
||||
class="text-capitalize"
|
||||
prepend-icon="tabler-link"
|
||||
>
|
||||
Copy Project Link
|
||||
</VBtn>
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.share-project-dialog {
|
||||
.card-list {
|
||||
--v-card-list-gap: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
120
resources/ts/components/dialogs/TwoFactorAuthDialog.vue
Normal file
120
resources/ts/components/dialogs/TwoFactorAuthDialog.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<script setup lang="ts">
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', value: boolean): void
|
||||
}
|
||||
interface Props {
|
||||
isDialogVisible: boolean
|
||||
smsCode?: string
|
||||
authAppCode?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isDialogVisible: false,
|
||||
smsCode: '',
|
||||
authAppCode: '',
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const authMethods = [
|
||||
{
|
||||
icon: 'tabler-settings',
|
||||
title: 'Authenticator Apps',
|
||||
desc: 'Get code from an app like Google Authenticator or Microsoft Authenticator.',
|
||||
value: 'authApp',
|
||||
},
|
||||
{
|
||||
icon: 'tabler-message',
|
||||
title: 'SMS',
|
||||
desc: 'We will send a code via SMS if you need to use your backup login method.',
|
||||
value: 'sms',
|
||||
},
|
||||
]
|
||||
|
||||
const selectedMethod = ref('authApp')
|
||||
const isAuthAppDialogVisible = ref(false)
|
||||
const isSmsDialogVisible = ref(false)
|
||||
|
||||
const openSelectedMethodDialog = () => {
|
||||
if (selectedMethod.value === 'authApp') {
|
||||
isAuthAppDialogVisible.value = true
|
||||
isSmsDialogVisible.value = false
|
||||
emit('update:isDialogVisible', false)
|
||||
}
|
||||
if (selectedMethod.value === 'sms') {
|
||||
isAuthAppDialogVisible.value = false
|
||||
isSmsDialogVisible.value = true
|
||||
emit('update:isDialogVisible', false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 800"
|
||||
:model-value="props.isDialogVisible"
|
||||
@update:model-value="(val) => $emit('update:isDialogVisible', val)"
|
||||
>
|
||||
<!-- Dialog close btn -->
|
||||
<DialogCloseBtn @click="$emit('update:isDialogVisible', false)" />
|
||||
|
||||
<VCard class="pa-2 pa-sm-10">
|
||||
<VCardText>
|
||||
<!-- 👉 Title -->
|
||||
<div class="mb-6">
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
Select Authentication Method
|
||||
</h4>
|
||||
<p class="text-body-1 text-center mb-6">
|
||||
You also need to select a method by which the proxy authenticates to the directory serve.
|
||||
</p>
|
||||
<CustomRadios
|
||||
v-model:selected-radio="selectedMethod"
|
||||
:radio-content="authMethods"
|
||||
:grid-column="{ cols: '12' }"
|
||||
>
|
||||
<template #default="items">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="d-flex gap-1 mb-2">
|
||||
<VIcon
|
||||
:icon="items.item.icon"
|
||||
size="20"
|
||||
class="text-high-emphasis"
|
||||
/>
|
||||
<h6 class="text-h6">
|
||||
{{ items.item.title }}
|
||||
</h6>
|
||||
</div>
|
||||
<p class="text-body-2 mb-0">
|
||||
{{ items.item.desc }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</CustomRadios>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-4 justify-center">
|
||||
<VBtn @click="openSelectedMethodDialog">
|
||||
submit
|
||||
</VBtn>
|
||||
<VBtn
|
||||
color="secondary"
|
||||
variant="tonal"
|
||||
@click="$emit('update:isDialogVisible', false)"
|
||||
>
|
||||
Cancel
|
||||
</VBtn>
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
|
||||
<AddAuthenticatorAppDialog
|
||||
v-model:is-dialog-visible="isAuthAppDialogVisible"
|
||||
:auth-code="props.authAppCode"
|
||||
/>
|
||||
<EnableOneTimePasswordDialog
|
||||
v-model:is-dialog-visible="isSmsDialogVisible"
|
||||
:mobile-number="props.smsCode"
|
||||
/>
|
||||
</template>
|
||||
242
resources/ts/components/dialogs/UserInfoEditDialog.vue
Normal file
242
resources/ts/components/dialogs/UserInfoEditDialog.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<script setup lang="ts">
|
||||
interface UserData {
|
||||
id: number | null
|
||||
fullName: string
|
||||
company: string
|
||||
username: string
|
||||
role: string
|
||||
country: string
|
||||
contact: string | undefined
|
||||
email: string | undefined
|
||||
currentPlan: string
|
||||
status: string | undefined
|
||||
avatar: string
|
||||
taskDone: number | null
|
||||
projectDone: number | null
|
||||
taxId: string
|
||||
language: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
userData?: UserData
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(e: 'submit', value: UserData): void
|
||||
(e: 'update:isDialogVisible', val: boolean): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
userData: () => ({
|
||||
id: 0,
|
||||
fullName: '',
|
||||
company: '',
|
||||
role: '',
|
||||
username: '',
|
||||
country: '',
|
||||
contact: '',
|
||||
email: '',
|
||||
currentPlan: '',
|
||||
status: '',
|
||||
avatar: '',
|
||||
taskDone: null,
|
||||
projectDone: null,
|
||||
taxId: '',
|
||||
language: '',
|
||||
}),
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const userData = ref<UserData>(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: boolean) => {
|
||||
emit('update:isDialogVisible', val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 900"
|
||||
:model-value="props.isDialogVisible"
|
||||
@update:model-value="dialogModelValueUpdate"
|
||||
>
|
||||
<!-- Dialog close btn -->
|
||||
<DialogCloseBtn @click="dialogModelValueUpdate(false)" />
|
||||
|
||||
<VCard class="pa-sm-10 pa-2">
|
||||
<VCardText>
|
||||
<!-- 👉 Title -->
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
Edit User Information
|
||||
</h4>
|
||||
<p class="text-body-1 text-center mb-6">
|
||||
Updating user details will receive a privacy audit.
|
||||
</p>
|
||||
|
||||
<!-- 👉 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"
|
||||
placeholder="John"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Last Name -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="userData.fullName.split(' ')[1]"
|
||||
label="Last Name"
|
||||
placeholder="Doe"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Username -->
|
||||
<VCol cols="12">
|
||||
<AppTextField
|
||||
v-model="userData.username"
|
||||
label="Username"
|
||||
placeholder="john.doe.007"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Billing Email -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="userData.email"
|
||||
label="Email"
|
||||
placeholder="johndoe@email.com"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Status -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppSelect
|
||||
v-model="userData.status"
|
||||
label="Status"
|
||||
placeholder="Active"
|
||||
:items="['Active', 'Inactive', 'Pending']"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Tax Id -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="userData.taxId"
|
||||
label="Tax ID"
|
||||
placeholder="123456789"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Contact -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="userData.contact"
|
||||
label="Phone Number"
|
||||
placeholder="+1 9876543210"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Language -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppSelect
|
||||
v-model="userData.language"
|
||||
closable-chips
|
||||
chips
|
||||
multiple
|
||||
label="Language"
|
||||
placeholder="English"
|
||||
:items="['English', 'Spanish', 'French']"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Country -->
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppSelect
|
||||
v-model="userData.country"
|
||||
label="Country"
|
||||
placeholder="United States"
|
||||
:items="['United States', 'United Kingdom', 'France']"
|
||||
/>
|
||||
</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>
|
||||
101
resources/ts/components/dialogs/UserUpgradePlanDialog.vue
Normal file
101
resources/ts/components/dialogs/UserUpgradePlanDialog.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<script setup lang="ts">
|
||||
interface Emit {
|
||||
(e: 'update:isDialogVisible', val: boolean): void
|
||||
}
|
||||
|
||||
interface Prop {
|
||||
isDialogVisible: boolean
|
||||
}
|
||||
|
||||
const props = defineProps<Prop>()
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
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: boolean) => {
|
||||
emit('update:isDialogVisible', val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 👉 upgrade plan -->
|
||||
<VDialog
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 650"
|
||||
:model-value="props.isDialogVisible"
|
||||
@update:model-value="dialogModelValueUpdate"
|
||||
>
|
||||
<!-- Dialog close btn -->
|
||||
<DialogCloseBtn @click="dialogModelValueUpdate(false)" />
|
||||
|
||||
<VCard class="pa-2 pa-sm-10">
|
||||
<VCardText>
|
||||
<!-- 👉 Title -->
|
||||
<h4 class="text-h4 text-center mb-2">
|
||||
Upgrade Plan
|
||||
</h4>
|
||||
<p class="text-body-1 text-center mb-6">
|
||||
Choose the best plan for user.
|
||||
</p>
|
||||
|
||||
<div class="d-flex justify-space-between flex-column flex-sm-row gap-4">
|
||||
<AppSelect
|
||||
v-model="selectedPlan"
|
||||
:items="plansList"
|
||||
label="Choose a plan"
|
||||
placeholder="Basic"
|
||||
/>
|
||||
<VBtn
|
||||
class="align-self-end"
|
||||
:block="$vuetify.display.xs"
|
||||
>
|
||||
Upgrade
|
||||
</VBtn>
|
||||
</div>
|
||||
|
||||
<VDivider class="my-6" />
|
||||
|
||||
<p class="text-body-1 mb-1">
|
||||
User current plan is standard plan
|
||||
</p>
|
||||
<div class="d-flex justify-space-between align-center flex-wrap">
|
||||
<div class="d-flex align-center gap-1 me-3">
|
||||
<sup class="text-body-1 text-primary">$</sup>
|
||||
<h1 class="text-h1 text-primary">
|
||||
99
|
||||
</h1>
|
||||
<sub class="text-body-2 mt-5">
|
||||
/ month
|
||||
</sub>
|
||||
</div>
|
||||
<VBtn
|
||||
color="error"
|
||||
variant="tonal"
|
||||
@click="isConfirmDialogVisible = true"
|
||||
>
|
||||
Cancel Subscription
|
||||
</VBtn>
|
||||
</div>
|
||||
</VCardText>
|
||||
|
||||
<!-- 👉 Confirm Dialog -->
|
||||
<ConfirmDialog
|
||||
v-model:is-dialog-visible="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>
|
||||
Reference in New Issue
Block a user