feat: update
This commit is contained in:
757
components/InterestDetailsModal.vue
Normal file
757
components/InterestDetailsModal.vue
Normal file
@@ -0,0 +1,757 @@
|
||||
<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-details</v-icon>
|
||||
Interest Details
|
||||
</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-toolbar-items>
|
||||
<v-btn
|
||||
@click="requestMoreInfoToSales"
|
||||
variant="text"
|
||||
:loading="isRequestingMoreInfo"
|
||||
:disabled="isRequestingMoreInfo || isRequestingMoreInformation || isSendingEOI"
|
||||
>
|
||||
<v-icon start>mdi-information-outline</v-icon>
|
||||
Info to Sales
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@click="requestMoreInformation"
|
||||
variant="text"
|
||||
:loading="isRequestingMoreInformation"
|
||||
:disabled="isRequestingMoreInfo || isRequestingMoreInformation || isSendingEOI"
|
||||
>
|
||||
<v-icon start>mdi-email-outline</v-icon>
|
||||
Request Info
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@click="eoiSendToSales"
|
||||
variant="text"
|
||||
:loading="isSendingEOI"
|
||||
:disabled="isRequestingMoreInfo || isRequestingMoreInformation || isSendingEOI"
|
||||
>
|
||||
<v-icon start>mdi-send</v-icon>
|
||||
EOI to Sales
|
||||
</v-btn>
|
||||
<v-btn
|
||||
variant="flat"
|
||||
color="success"
|
||||
size="large"
|
||||
@click="saveInterest"
|
||||
:loading="isSaving"
|
||||
:disabled="isSaving"
|
||||
class="ml-2"
|
||||
>
|
||||
<v-icon start>mdi-content-save</v-icon>
|
||||
Save Changes
|
||||
</v-btn>
|
||||
</v-toolbar-items>
|
||||
</v-toolbar>
|
||||
|
||||
<v-card-text v-if="interest">
|
||||
<v-stepper
|
||||
v-model="currentStep"
|
||||
class="mb-6"
|
||||
variant="flat"
|
||||
alt-labels
|
||||
elevation="0"
|
||||
bg-color="transparent"
|
||||
>
|
||||
<v-stepper-header>
|
||||
<template
|
||||
v-for="(level, index) in InterestSalesProcessLevelFlow"
|
||||
:key="index"
|
||||
>
|
||||
<v-stepper-item
|
||||
:step="index"
|
||||
:complete="currentStep + 1 > index"
|
||||
:title="level"
|
||||
:color="currentStep === index ? 'primary' : 'grey'"
|
||||
:icon="currentStep + 1 > index ? 'mdi-check' : undefined"
|
||||
/>
|
||||
<v-divider
|
||||
v-if="index < InterestSalesProcessLevelFlow.length - 1"
|
||||
></v-divider>
|
||||
</template>
|
||||
</v-stepper-header>
|
||||
</v-stepper>
|
||||
|
||||
<!-- Non-editable fields as informational badges -->
|
||||
<v-row class="mb-4">
|
||||
<v-col cols="12">
|
||||
<div class="d-flex flex-wrap ga-2">
|
||||
<v-chip
|
||||
v-if="interest['Created At']"
|
||||
color="info"
|
||||
variant="tonal"
|
||||
prepend-icon="mdi-calendar-clock"
|
||||
>
|
||||
Created: {{ formatDate(interest['Created At']) }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="interest['Request Form Sent']"
|
||||
color="success"
|
||||
variant="tonal"
|
||||
prepend-icon="mdi-email-check"
|
||||
>
|
||||
Request Form Sent: {{ formatDate(interest['Request Form Sent']) }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="interest['EOI Time Sent']"
|
||||
color="warning"
|
||||
variant="tonal"
|
||||
prepend-icon="mdi-email-fast"
|
||||
>
|
||||
EOI Sent: {{ formatDate(interest['EOI Time Sent']) }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="interest['Time LOI Sent']"
|
||||
color="secondary"
|
||||
variant="tonal"
|
||||
prepend-icon="mdi-file-document-check"
|
||||
>
|
||||
LOI Sent: {{ formatDate(interest['Time LOI Sent']) }}
|
||||
</v-chip>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-form @submit.prevent="saveInterest">
|
||||
<!-- 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="interest['Full Name']"
|
||||
label="Full Name"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
prepend-inner-icon="mdi-account"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="interest['Email Address']"
|
||||
label="Email Address"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
prepend-inner-icon="mdi-email"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="interest['Phone Number']"
|
||||
label="Phone Number"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
prepend-inner-icon="mdi-phone"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="interest.Address"
|
||||
label="Address"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
prepend-inner-icon="mdi-map-marker"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="interest['Place of Residence']"
|
||||
label="Place of Residence"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
prepend-inner-icon="mdi-home"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-select
|
||||
v-model="interest['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="interest['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="interest.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="interest.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="interest.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="interest['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="interest['Sales Process Level']"
|
||||
label="Sales Process Level"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
:items="InterestSalesProcessLevelFlow"
|
||||
prepend-inner-icon="mdi-chart-line"
|
||||
></v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-select
|
||||
v-model="interest['Lead Category']"
|
||||
label="Lead Category"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
:items="InterestLeadCategoryFlow"
|
||||
prepend-inner-icon="mdi-tag"
|
||||
></v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-autocomplete
|
||||
v-model="selectedBerthRecommendations"
|
||||
:items="availableBerths"
|
||||
:item-title="item => item['Mooring Number']"
|
||||
:item-value="item => item.Id"
|
||||
label="Berth Recommendations"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
multiple
|
||||
chips
|
||||
closable-chips
|
||||
:loading="loadingBerths"
|
||||
prepend-inner-icon="mdi-star"
|
||||
@update:model-value="updateBerthRecommendations"
|
||||
>
|
||||
<template v-slot:chip="{ props, item }">
|
||||
<v-chip
|
||||
v-bind="props"
|
||||
:text="item.raw['Mooring Number']"
|
||||
size="small"
|
||||
></v-chip>
|
||||
</template>
|
||||
</v-autocomplete>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-autocomplete
|
||||
v-model="selectedBerths"
|
||||
:items="availableBerths"
|
||||
:item-title="item => item['Mooring Number']"
|
||||
:item-value="item => item.Id"
|
||||
label="Berths"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
multiple
|
||||
chips
|
||||
closable-chips
|
||||
:loading="loadingBerths"
|
||||
prepend-inner-icon="mdi-dock-window"
|
||||
@update:model-value="updateBerths"
|
||||
>
|
||||
<template v-slot:chip="{ props, item }">
|
||||
<v-chip
|
||||
v-bind="props"
|
||||
:text="item.raw['Mooring Number']"
|
||||
size="small"
|
||||
></v-chip>
|
||||
</template>
|
||||
</v-autocomplete>
|
||||
</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="interest.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="interest['Extra Comments']"
|
||||
label="Extra Comments"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
prepend-inner-icon="mdi-comment-text"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" v-if="interest['EOI Document'] && interest['EOI Document'].length > 0">
|
||||
<div class="d-flex align-center">
|
||||
<v-icon class="mr-2" color="primary">mdi-file-document</v-icon>
|
||||
<span class="text-subtitle-1 mr-3">EOI Documents:</span>
|
||||
<div class="d-flex flex-wrap ga-2">
|
||||
<v-chip
|
||||
v-for="(doc, index) in interest['EOI Document']"
|
||||
:key="index"
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
prepend-icon="mdi-file-pdf-box"
|
||||
:href="doc.url"
|
||||
target="_blank"
|
||||
component="a"
|
||||
clickable
|
||||
>
|
||||
{{ doc.title || `EOI Document ${index + 1}` }}
|
||||
</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
</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, onMounted } from "vue";
|
||||
import type { Interest, Berth } from "@/utils/types";
|
||||
import {
|
||||
InterestSalesProcessLevelFlow,
|
||||
InterestLeadCategoryFlow,
|
||||
ContactMethodPreferredFlow,
|
||||
} from "@/utils/types";
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean;
|
||||
selectedInterest: Interest | null;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void;
|
||||
(e: 'save', interest: Interest): void;
|
||||
(e: 'requestMoreInfoToSales', interest: Interest): void;
|
||||
(e: 'requestMoreInformation', interest: Interest): void;
|
||||
(e: 'eoiSendToSales', interest: Interest): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const user = useDirectusUser();
|
||||
|
||||
// Local copy of the interest for editing
|
||||
const interest = ref<Interest | null>(null);
|
||||
|
||||
// Loading states for buttons
|
||||
const isSaving = ref(false);
|
||||
const isRequestingMoreInfo = ref(false);
|
||||
const isRequestingMoreInformation = ref(false);
|
||||
const isSendingEOI = ref(false);
|
||||
|
||||
// Berths related data
|
||||
const availableBerths = ref<Berth[]>([]);
|
||||
const loadingBerths = ref(false);
|
||||
const selectedBerths = ref<number[]>([]);
|
||||
const selectedBerthRecommendations = ref<number[]>([]);
|
||||
const originalBerths = ref<number[]>([]);
|
||||
const originalBerthRecommendations = ref<number[]>([]);
|
||||
|
||||
// Sync the local copy with the prop
|
||||
watch(() => props.selectedInterest, async (newInterest) => {
|
||||
if (newInterest) {
|
||||
interest.value = { ...newInterest };
|
||||
// Load linked berths and recommendations
|
||||
await loadLinkedBerths();
|
||||
} else {
|
||||
interest.value = null;
|
||||
selectedBerths.value = [];
|
||||
selectedBerthRecommendations.value = [];
|
||||
originalBerths.value = [];
|
||||
originalBerthRecommendations.value = [];
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// Computed property for v-model binding
|
||||
const isOpen = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value: boolean) => emit('update:modelValue', value)
|
||||
});
|
||||
|
||||
const currentStep = computed(() => {
|
||||
return InterestSalesProcessLevelFlow.indexOf(
|
||||
interest.value?.["Sales Process Level"] || ""
|
||||
);
|
||||
});
|
||||
|
||||
const closeModal = () => {
|
||||
isOpen.value = false;
|
||||
};
|
||||
|
||||
const saveInterest = async () => {
|
||||
if (interest.value) {
|
||||
isSaving.value = true;
|
||||
try {
|
||||
// Create a copy of the interest data without the Berths and Berth Recommendations fields
|
||||
const dataToSave = { ...interest.value };
|
||||
delete dataToSave.Berths;
|
||||
delete dataToSave['Berth Recommendations'];
|
||||
|
||||
// Call the update-interest API
|
||||
await $fetch('/api/update-interest', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
body: {
|
||||
id: interest.value.Id.toString(),
|
||||
data: dataToSave
|
||||
}
|
||||
});
|
||||
|
||||
alert("Interest saved successfully!");
|
||||
emit('save', interest.value);
|
||||
closeModal();
|
||||
} catch (error) {
|
||||
console.error("Failed to save interest:", error);
|
||||
alert("Failed to save interest. Please try again.");
|
||||
} finally {
|
||||
isSaving.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const requestMoreInfoToSales = async () => {
|
||||
if (interest.value) {
|
||||
isRequestingMoreInfo.value = true;
|
||||
try {
|
||||
// Call the request-more-info-to-sales API
|
||||
await $fetch('/api/request-more-info-to-sales', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
body: {
|
||||
interestId: interest.value.Id.toString()
|
||||
}
|
||||
});
|
||||
|
||||
alert("Request More Info - To Sales sent successfully!");
|
||||
emit('requestMoreInfoToSales', interest.value);
|
||||
} catch (error) {
|
||||
console.error("Failed to send request:", error);
|
||||
alert("Failed to send request. Please try again.");
|
||||
} finally {
|
||||
isRequestingMoreInfo.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const requestMoreInformation = async () => {
|
||||
if (interest.value) {
|
||||
isRequestingMoreInformation.value = true;
|
||||
try {
|
||||
// Call the request-more-information API
|
||||
await $fetch('/api/request-more-information', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
body: {
|
||||
interestId: interest.value.Id.toString()
|
||||
}
|
||||
});
|
||||
|
||||
alert("Request More Information sent successfully!");
|
||||
emit('requestMoreInformation', interest.value);
|
||||
} catch (error) {
|
||||
console.error("Failed to send request:", error);
|
||||
alert("Failed to send request. Please try again.");
|
||||
} finally {
|
||||
isRequestingMoreInformation.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const eoiSendToSales = async () => {
|
||||
if (interest.value) {
|
||||
isSendingEOI.value = true;
|
||||
try {
|
||||
// Call the eoi-send-to-sales API
|
||||
await $fetch('/api/eoi-send-to-sales', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
body: {
|
||||
interestId: interest.value.Id.toString()
|
||||
}
|
||||
});
|
||||
|
||||
alert("EOI Send to Sales sent successfully!");
|
||||
emit('eoiSendToSales', interest.value);
|
||||
} catch (error) {
|
||||
console.error("Failed to send EOI:", error);
|
||||
alert("Failed to send EOI. Please try again.");
|
||||
} finally {
|
||||
isSendingEOI.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Load all available berths
|
||||
const loadAvailableBerths = async () => {
|
||||
loadingBerths.value = true;
|
||||
try {
|
||||
const response = await $fetch<{ list: Berth[] }>('/api/get-berths', {
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
});
|
||||
availableBerths.value = response.list || [];
|
||||
} catch (error) {
|
||||
console.error("Failed to load berths:", error);
|
||||
} finally {
|
||||
loadingBerths.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Load linked berths for the current interest
|
||||
const loadLinkedBerths = async () => {
|
||||
if (!interest.value) return;
|
||||
|
||||
try {
|
||||
// Load berths
|
||||
const berthsResponse = await $fetch<{ list: Array<{ Id: number }> }>('/api/get-interest-berths', {
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
params: {
|
||||
interestId: interest.value.Id,
|
||||
linkType: 'berths'
|
||||
}
|
||||
});
|
||||
|
||||
// Load berth recommendations
|
||||
const recommendationsResponse = await $fetch<{ list: Array<{ Id: number }> }>('/api/get-interest-berths', {
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
params: {
|
||||
interestId: interest.value.Id,
|
||||
linkType: 'recommendations'
|
||||
}
|
||||
});
|
||||
|
||||
selectedBerths.value = (berthsResponse.list || []).map(b => b.Id);
|
||||
selectedBerthRecommendations.value = (recommendationsResponse.list || []).map(b => b.Id);
|
||||
|
||||
// Store original values to detect changes
|
||||
originalBerths.value = [...selectedBerths.value];
|
||||
originalBerthRecommendations.value = [...selectedBerthRecommendations.value];
|
||||
} catch (error) {
|
||||
console.error("Failed to load linked berths:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Update berths when selection changes
|
||||
const updateBerths = async (newBerths: number[]) => {
|
||||
if (!interest.value) return;
|
||||
|
||||
try {
|
||||
// Find berths to add and remove
|
||||
const toAdd = newBerths.filter(id => !originalBerths.value.includes(id));
|
||||
const toRemove = originalBerths.value.filter(id => !newBerths.includes(id));
|
||||
|
||||
// Link new berths
|
||||
if (toAdd.length > 0) {
|
||||
await $fetch('/api/link-berths-to-interest', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
body: {
|
||||
interestId: interest.value.Id,
|
||||
berthIds: toAdd
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Unlink removed berths
|
||||
if (toRemove.length > 0) {
|
||||
await $fetch('/api/unlink-berths-from-interest', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
body: {
|
||||
interestId: interest.value.Id,
|
||||
berthIds: toRemove
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update original values
|
||||
originalBerths.value = [...newBerths];
|
||||
} catch (error) {
|
||||
console.error("Failed to update berths:", error);
|
||||
alert("Failed to update berths. Please try again.");
|
||||
// Revert to original values on error
|
||||
selectedBerths.value = [...originalBerths.value];
|
||||
}
|
||||
};
|
||||
|
||||
// Update berth recommendations when selection changes
|
||||
const updateBerthRecommendations = async (newRecommendations: number[]) => {
|
||||
if (!interest.value) return;
|
||||
|
||||
try {
|
||||
// Find recommendations to add and remove
|
||||
const toAdd = newRecommendations.filter(id => !originalBerthRecommendations.value.includes(id));
|
||||
const toRemove = originalBerthRecommendations.value.filter(id => !newRecommendations.includes(id));
|
||||
|
||||
// Link new recommendations
|
||||
if (toAdd.length > 0) {
|
||||
await $fetch('/api/link-berth-recommendations-to-interest', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
body: {
|
||||
interestId: interest.value.Id,
|
||||
berthIds: toAdd
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Unlink removed recommendations
|
||||
if (toRemove.length > 0) {
|
||||
await $fetch('/api/unlink-berth-recommendations-from-interest', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"x-tag": !user.value?.email ? "094ut234" : "pjnvü1230",
|
||||
},
|
||||
body: {
|
||||
interestId: interest.value.Id,
|
||||
berthIds: toRemove
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update original values
|
||||
originalBerthRecommendations.value = [...newRecommendations];
|
||||
} catch (error) {
|
||||
console.error("Failed to update berth recommendations:", error);
|
||||
alert("Failed to update berth recommendations. Please try again.");
|
||||
// Revert to original values on error
|
||||
selectedBerthRecommendations.value = [...originalBerthRecommendations.value];
|
||||
}
|
||||
};
|
||||
|
||||
// Format date helper function
|
||||
const formatDate = (dateString: string | null | undefined) => {
|
||||
if (!dateString) return '';
|
||||
try {
|
||||
// Handle DD-MM-YYYY format
|
||||
if (dateString.includes('-')) {
|
||||
const parts = dateString.split('-');
|
||||
if (parts.length === 3) {
|
||||
const [day, month, year] = parts;
|
||||
const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
||||
return date.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
}
|
||||
// Fallback to direct date parsing
|
||||
const date = new Date(dateString);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
return dateString;
|
||||
} catch (error) {
|
||||
return dateString;
|
||||
}
|
||||
};
|
||||
|
||||
// Load berths when component mounts
|
||||
onMounted(() => {
|
||||
loadAvailableBerths();
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user