fix: show lost reason modal if status changed to lost

(cherry picked from commit d89c304b134a7ec1f7a16bc667f2ee13858daac9)

# Conflicts:
#	frontend/components.d.ts
This commit is contained in:
Shariq Ansari 2025-07-02 14:11:11 +05:30 committed by Mergify
parent 9b315c2e0c
commit a7bc3abcdd
4 changed files with 147 additions and 2 deletions

View File

@ -162,7 +162,11 @@ declare module 'vue' {
ListIcon: typeof import('./src/components/Icons/ListIcon.vue')['default'] ListIcon: typeof import('./src/components/Icons/ListIcon.vue')['default']
ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default'] ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default']
LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default'] LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default']
<<<<<<< HEAD
LucideCalendar: typeof import('~icons/lucide/calendar')['default'] LucideCalendar: typeof import('~icons/lucide/calendar')['default']
=======
LostReasonModal: typeof import('./src/components/Modals/LostReasonModal.vue')['default']
>>>>>>> d89c304b (fix: show lost reason modal if status changed to lost)
LucideInfo: typeof import('~icons/lucide/info')['default'] LucideInfo: typeof import('~icons/lucide/info')['default']
LucideMoreHorizontal: typeof import('~icons/lucide/more-horizontal')['default'] LucideMoreHorizontal: typeof import('~icons/lucide/more-horizontal')['default']
LucidePlus: typeof import('~icons/lucide/plus')['default'] LucidePlus: typeof import('~icons/lucide/plus')['default']

View File

@ -0,0 +1,99 @@
<template>
<Dialog
v-model="show"
:options="{ title: __('Lost reason') }"
@close="cancel"
>
<template #body-content>
<div class="-mt-3 mb-4 text-p-base text-ink-gray-7">
{{ __('Please provide a reason for marking this deal as lost') }}
</div>
<div class="flex flex-col gap-3">
<div>
<div class="mb-2 text-sm text-ink-gray-5">
{{ __('Lost reason') }}
<span class="text-ink-red-2">*</span>
</div>
<Link
class="form-control flex-1 truncate"
:value="lostReason"
doctype="CRM Lost Reason"
@change="(v) => (lostReason = v)"
:onCreate="onCreate"
/>
</div>
<div>
<div class="mb-2 text-sm text-ink-gray-5">
{{ __('Lost notes') }}
<span v-if="lostReason == 'Other'" class="text-ink-red-2">*</span>
</div>
<FormControl
class="form-control flex-1 truncate"
type="textarea"
:value="lostNotes"
@change="(e) => (lostNotes = e.target.value)"
/>
</div>
</div>
</template>
<template #actions>
<div class="flex justify-between items-center gap-2">
<div><ErrorMessage :message="error" /></div>
<div class="flex gap-2">
<Button :label="__('Cancel')" @click="cancel" />
<Button variant="solid" :label="__('Save')" @click="save" />
</div>
</div>
</template>
</Dialog>
</template>
<script setup>
import Link from '@/components/Controls/Link.vue'
import { Dialog } from 'frappe-ui'
import { ref } from 'vue'
const props = defineProps({
deal: {
type: Object,
required: true,
},
})
const show = defineModel()
const lostReason = ref('')
const lostNotes = ref('')
const error = ref('')
function cancel() {
show.value = false
error.value = ''
lostReason.value = ''
lostNotes.value = ''
props.deal.doc.status = props.deal.originalDoc.status
}
function save() {
if (!lostReason.value) {
error.value = __('Lost reason is required')
return
}
if (lostReason.value === 'Other' && !lostNotes.value) {
error.value = __('Lost notes are required when lost reason is "Other"')
return
}
error.value = ''
show.value = false
props.deal.doc.lost_reason = lostReason.value
props.deal.doc.lost_notes = lostNotes.value
props.deal.save.submit()
}
function onCreate() {
// This function can be used to handle the creation of a new lost reason
// For now, we just close the modal after creating a new reason
show.value = false
}
</script>

View File

@ -327,6 +327,11 @@
:docname="props.dealId" :docname="props.dealId"
name="Deals" name="Deals"
/> />
<LostReasonModal
v-if="showLostReasonModal"
v-model="showLostReasonModal"
:deal="document"
/>
</template> </template>
<script setup> <script setup>
import ErrorPage from '@/components/ErrorPage.vue' import ErrorPage from '@/components/ErrorPage.vue'
@ -350,6 +355,7 @@ import AttachmentIcon from '@/components/Icons/AttachmentIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Activities from '@/components/Activities/Activities.vue' import Activities from '@/components/Activities/Activities.vue'
import OrganizationModal from '@/components/Modals/OrganizationModal.vue' import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
import LostReasonModal from '@/components/Modals/LostReasonModal.vue'
import AssignTo from '@/components/AssignTo.vue' import AssignTo from '@/components/AssignTo.vue'
import FilesUploader from '@/components/FilesUploader/FilesUploader.vue' import FilesUploader from '@/components/FilesUploader/FilesUploader.vue'
import ContactModal from '@/components/Modals/ContactModal.vue' import ContactModal from '@/components/Modals/ContactModal.vue'
@ -758,7 +764,22 @@ const { assignees, document, triggerOnChange } = useDocument(
async function triggerStatusChange(value) { async function triggerStatusChange(value) {
await triggerOnChange('status', value) await triggerOnChange('status', value)
document.save.submit() setLostReason()
}
const showLostReasonModal = ref(false)
function setLostReason() {
if (
document.doc.status !== 'Lost' ||
(document.doc.lost_reason && document.doc.lost_reason !== 'Other') ||
(document.doc.lost_reason === 'Other' && document.doc.lost_notes)
) {
document.save.submit()
return
}
showLostReasonModal.value = true
} }
function reloadAssignees(data) { function reloadAssignees(data) {

View File

@ -245,6 +245,11 @@
afterInsert: (doc) => addContact(doc.name), afterInsert: (doc) => addContact(doc.name),
}" }"
/> />
<LostReasonModal
v-if="showLostReasonModal"
v-model="showLostReasonModal"
:deal="document"
/>
</template> </template>
<script setup> <script setup>
import Icon from '@/components/Icon.vue' import Icon from '@/components/Icon.vue'
@ -265,6 +270,7 @@ import SuccessIcon from '@/components/Icons/SuccessIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Activities from '@/components/Activities/Activities.vue' import Activities from '@/components/Activities/Activities.vue'
import OrganizationModal from '@/components/Modals/OrganizationModal.vue' import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
import LostReasonModal from '@/components/Modals/LostReasonModal.vue'
import AssignTo from '@/components/AssignTo.vue' import AssignTo from '@/components/AssignTo.vue'
import ContactModal from '@/components/Modals/ContactModal.vue' import ContactModal from '@/components/Modals/ContactModal.vue'
import Section from '@/components/Section.vue' import Section from '@/components/Section.vue'
@ -627,7 +633,22 @@ const { assignees, document, triggerOnChange } = useDocument(
async function triggerStatusChange(value) { async function triggerStatusChange(value) {
await triggerOnChange('status', value) await triggerOnChange('status', value)
document.save.submit() setLostReason()
}
const showLostReasonModal = ref(false)
function setLostReason() {
if (
document.doc.status !== 'Lost' ||
(document.doc.lost_reason && document.doc.lost_reason !== 'Other') ||
(document.doc.lost_reason === 'Other' && document.doc.lost_notes)
) {
document.save.submit()
return
}
showLostReasonModal.value = true
} }
function reloadAssignees(data) { function reloadAssignees(data) {