port-nimara-client-portal/components/CreateInterestModal.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>