422 lines
14 KiB
Vue
422 lines
14 KiB
Vue
<template>
|
|
<v-dialog
|
|
v-model="isOpen"
|
|
fullscreen
|
|
hide-overlay
|
|
transition="dialog-bottom-transition"
|
|
>
|
|
<v-card>
|
|
<v-toolbar dark color="primary">
|
|
<v-btn icon dark @click="closeModal">
|
|
<v-icon>mdi-close</v-icon>
|
|
</v-btn>
|
|
<v-toolbar-title>
|
|
<v-icon class="mr-2">mdi-account-plus</v-icon>
|
|
Create New Interest
|
|
</v-toolbar-title>
|
|
<v-spacer></v-spacer>
|
|
<v-toolbar-items>
|
|
<v-btn
|
|
variant="flat"
|
|
color="success"
|
|
:size="mobile ? 'default' : 'large'"
|
|
@click="createInterest"
|
|
:loading="isCreating"
|
|
:disabled="isCreating"
|
|
class="ml-2"
|
|
:icon="mobile"
|
|
>
|
|
<v-icon v-if="mobile">mdi-content-save</v-icon>
|
|
<template v-else>
|
|
<v-icon start>mdi-content-save</v-icon>
|
|
Create Interest
|
|
</template>
|
|
</v-btn>
|
|
</v-toolbar-items>
|
|
</v-toolbar>
|
|
|
|
<v-card-text>
|
|
<v-alert
|
|
type="info"
|
|
variant="tonal"
|
|
class="mb-6"
|
|
>
|
|
Berths and berth recommendations can only be assigned after creating the interest.
|
|
</v-alert>
|
|
|
|
<v-form ref="form" @submit.prevent="createInterest">
|
|
<!-- Contact Information Section -->
|
|
<v-card variant="flat" class="mb-6">
|
|
<v-card-title class="text-h6 d-flex align-center pb-4">
|
|
<v-icon class="mr-2" color="primary">mdi-account-circle</v-icon>
|
|
Contact Information
|
|
</v-card-title>
|
|
<v-card-text class="pt-2">
|
|
<v-row dense>
|
|
<v-col cols="12" md="4">
|
|
<v-text-field
|
|
v-model="newInterest['Full Name']"
|
|
label="Full Name *"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-account"
|
|
:rules="[rules.required]"
|
|
required
|
|
></v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" md="4">
|
|
<v-text-field
|
|
v-model="newInterest['Email Address']"
|
|
label="Email Address *"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-email"
|
|
:rules="[rules.required, rules.email]"
|
|
required
|
|
></v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" md="4">
|
|
<PhoneInput
|
|
v-model="newInterest['Phone Number']"
|
|
label="Phone Number"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
default-country="US"
|
|
:preferred-countries="['US', 'FR', 'ES', 'PT', 'GB']"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="newInterest.Address"
|
|
label="Address"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-map-marker"
|
|
></v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-select
|
|
v-model="newInterest['Contact Method Preferred']"
|
|
label="Contact Method Preferred"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
:items="ContactMethodPreferredFlow"
|
|
prepend-inner-icon="mdi-message-text"
|
|
></v-select>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<!-- Yacht Information Section -->
|
|
<v-card variant="flat" class="mb-6">
|
|
<v-card-title class="text-h6 d-flex align-center pb-4">
|
|
<v-icon class="mr-2" color="primary">mdi-sail-boat</v-icon>
|
|
Yacht Information
|
|
</v-card-title>
|
|
<v-card-text class="pt-2">
|
|
<v-row dense>
|
|
<v-col cols="12" md="3">
|
|
<v-text-field
|
|
v-model="newInterest['Yacht Name']"
|
|
label="Yacht Name"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-ferry"
|
|
></v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" md="3">
|
|
<v-text-field
|
|
v-model="newInterest.Length"
|
|
label="Length"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-ruler"
|
|
></v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" md="3">
|
|
<v-text-field
|
|
v-model="newInterest.Width"
|
|
label="Width"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-arrow-expand-horizontal"
|
|
></v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" md="3">
|
|
<v-text-field
|
|
v-model="newInterest.Depth"
|
|
label="Depth"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-arrow-expand-vertical"
|
|
></v-text-field>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<!-- Berth & Sales Information Section -->
|
|
<v-card variant="flat" class="mb-6">
|
|
<v-card-title class="text-h6 d-flex align-center pb-4">
|
|
<v-icon class="mr-2" color="primary">mdi-anchor</v-icon>
|
|
Berth & Sales Information
|
|
</v-card-title>
|
|
<v-card-text class="pt-2">
|
|
<v-row dense>
|
|
<v-col cols="12" md="4">
|
|
<v-text-field
|
|
v-model="newInterest['Berth Size Desired']"
|
|
label="Berth Size Desired"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-tape-measure"
|
|
></v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" md="4">
|
|
<v-select
|
|
v-model="newInterest['Sales Process Level']"
|
|
label="Sales Process Level *"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
:items="InterestSalesProcessLevelFlow"
|
|
prepend-inner-icon="mdi-chart-line"
|
|
:rules="[rules.required]"
|
|
required
|
|
></v-select>
|
|
</v-col>
|
|
<v-col cols="12" md="4">
|
|
<v-select
|
|
v-model="newInterest['Lead Category']"
|
|
label="Lead Category"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
:items="InterestLeadCategoryFlow"
|
|
prepend-inner-icon="mdi-tag"
|
|
></v-select>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<!-- Process Status Section -->
|
|
<v-card variant="flat" class="mb-6">
|
|
<v-card-title class="text-h6 d-flex align-center pb-4">
|
|
<v-icon class="mr-2" color="primary">mdi-progress-check</v-icon>
|
|
Process Status
|
|
</v-card-title>
|
|
<v-card-text class="pt-2">
|
|
<v-row dense>
|
|
<v-col cols="12">
|
|
<v-row dense>
|
|
<v-col cols="12" md="2">
|
|
<v-select
|
|
v-model="newInterest['EOI Status']"
|
|
label="EOI Status"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
:items="EOIStatusFlow"
|
|
prepend-inner-icon="mdi-file-document-outline"
|
|
></v-select>
|
|
</v-col>
|
|
<v-col cols="12" md="2">
|
|
<v-select
|
|
v-model="newInterest['Berth Info Sent Status']"
|
|
label="Berth Info Sent"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
:items="BerthInfoSentStatusFlow"
|
|
prepend-inner-icon="mdi-information-outline"
|
|
></v-select>
|
|
</v-col>
|
|
<v-col cols="12" md="2">
|
|
<v-select
|
|
v-model="newInterest['Contract Sent Status']"
|
|
label="Contract Sent"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
:items="ContractSentStatusFlow"
|
|
prepend-inner-icon="mdi-email-send"
|
|
></v-select>
|
|
</v-col>
|
|
<v-col cols="12" md="3">
|
|
<v-select
|
|
v-model="newInterest['Deposit 10% Status']"
|
|
label="Deposit 10%"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
:items="Deposit10PercentStatusFlow"
|
|
prepend-inner-icon="mdi-cash"
|
|
></v-select>
|
|
</v-col>
|
|
<v-col cols="12" md="3">
|
|
<v-select
|
|
v-model="newInterest['Contract Status']"
|
|
label="Contract Status"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
:items="ContractStatusFlow"
|
|
prepend-inner-icon="mdi-file-document"
|
|
></v-select>
|
|
</v-col>
|
|
</v-row>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<!-- Additional Information Section -->
|
|
<v-card variant="flat" class="mb-6">
|
|
<v-card-title class="text-h6 d-flex align-center pb-4">
|
|
<v-icon class="mr-2" color="primary">mdi-information</v-icon>
|
|
Additional Information
|
|
</v-card-title>
|
|
<v-card-text class="pt-2">
|
|
<v-row dense>
|
|
<v-col cols="12" md="4">
|
|
<v-text-field
|
|
v-model="newInterest.Source"
|
|
label="Source"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-source-branch"
|
|
></v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" md="8">
|
|
<v-text-field
|
|
v-model="newInterest['Extra Comments']"
|
|
label="Extra Comments"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-comment-text"
|
|
></v-text-field>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-form>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-dialog>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref, computed, watch } from "vue";
|
|
import type { Interest } from "@/utils/types";
|
|
import PhoneInput from "./PhoneInput.vue";
|
|
import {
|
|
InterestSalesProcessLevelFlow,
|
|
InterestLeadCategoryFlow,
|
|
ContactMethodPreferredFlow,
|
|
EOIStatusFlow,
|
|
BerthInfoSentStatusFlow,
|
|
ContractSentStatusFlow,
|
|
Deposit10PercentStatusFlow,
|
|
ContractStatusFlow,
|
|
} from "@/utils/types";
|
|
|
|
interface Props {
|
|
modelValue: boolean;
|
|
}
|
|
|
|
interface Emits {
|
|
(e: "update:modelValue", value: boolean): void;
|
|
(e: "created", interest: Interest): void;
|
|
}
|
|
|
|
const props = defineProps<Props>();
|
|
const emit = defineEmits<Emits>();
|
|
|
|
const { mobile } = useDisplay();
|
|
const user = useDirectusUser();
|
|
const toast = useToast();
|
|
|
|
// Form ref
|
|
const form = ref();
|
|
|
|
// Loading state
|
|
const isCreating = ref(false);
|
|
|
|
// Initial interest data
|
|
const getInitialInterest = () => ({
|
|
"Full Name": "",
|
|
"Email Address": "",
|
|
"Phone Number": "",
|
|
"Contact Method Preferred": "Email",
|
|
Address: "",
|
|
"Yacht Name": "",
|
|
"Berth Size Desired": "",
|
|
Length: "",
|
|
Width: "",
|
|
Depth: "",
|
|
"Sales Process Level": "General Qualified Interest",
|
|
"Lead Category": "General",
|
|
Source: "",
|
|
"Extra Comments": "",
|
|
"Date Added": new Date().toLocaleDateString("en-GB").replace(/\//g, "-"),
|
|
"Created At": new Date().toLocaleDateString("en-GB").replace(/\//g, "-"),
|
|
"EOI Status": "Awaiting Further Details",
|
|
"Berth Info Sent Status": "Pending",
|
|
"Contract Sent Status": "Pending",
|
|
"Deposit 10% Status": "Awaiting Further Details",
|
|
"Contract Status": "Pending",
|
|
});
|
|
|
|
// New interest data
|
|
const newInterest = ref(getInitialInterest());
|
|
|
|
// Validation rules
|
|
const rules = {
|
|
required: (v: any) => !!v || "This field is required",
|
|
email: (v: string) => {
|
|
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return pattern.test(v) || "Invalid email address";
|
|
},
|
|
};
|
|
|
|
// Computed property for v-model binding
|
|
const isOpen = computed({
|
|
get: () => props.modelValue,
|
|
set: (value: boolean) => emit("update:modelValue", value),
|
|
});
|
|
|
|
// Reset form when dialog opens
|
|
watch(isOpen, (newValue) => {
|
|
if (newValue) {
|
|
newInterest.value = getInitialInterest();
|
|
form.value?.resetValidation();
|
|
}
|
|
});
|
|
|
|
const closeModal = () => {
|
|
isOpen.value = false;
|
|
};
|
|
|
|
const createInterest = async () => {
|
|
// Validate form
|
|
const { valid } = await form.value.validate();
|
|
if (!valid) {
|
|
toast.error("Please fill in all required fields");
|
|
return;
|
|
}
|
|
|
|
isCreating.value = true;
|
|
try {
|
|
// Call the create-interest API - let browser send session cookies (Directus or OIDC)
|
|
const response = await $fetch<Interest>("/api/create-interest", {
|
|
method: "POST",
|
|
body: newInterest.value,
|
|
});
|
|
|
|
toast.success("Interest created successfully!");
|
|
emit("created", response);
|
|
closeModal();
|
|
} catch (error) {
|
|
console.error("Failed to create interest:", error);
|
|
toast.error("Failed to create interest. Please try again.");
|
|
} finally {
|
|
isCreating.value = false;
|
|
}
|
|
};
|
|
</script>
|