128 lines
3 KiB
Vue
128 lines
3 KiB
Vue
<script setup>
|
|
import { ref, watch } from 'vue'
|
|
import { updateItem } from '../api.js'
|
|
|
|
const props = defineProps({ item: { type: Object, default: null } })
|
|
const emit = defineEmits(['close', 'saved'])
|
|
|
|
const senderId = ref('')
|
|
const type = ref('')
|
|
const stage = ref('')
|
|
const notes = ref('')
|
|
const followUpDate = ref('')
|
|
|
|
const DONATION_STAGES = ['new', 'replied', 'pickup_scheduled', 'picked_up', 'logged_in_bookmark']
|
|
const OTHER_STAGES = ['new', 'done']
|
|
const OUTBOUND_STAGES = [
|
|
'new', 'reviewed', 'outreach_drafted', 'outreach_sent',
|
|
'replied', 'pickup_scheduled', 'picked_up', 'logged_in_bookmark',
|
|
]
|
|
|
|
watch(() => props.item, (item) => {
|
|
if (!item) return
|
|
senderId.value = item.sender_id || ''
|
|
type.value = item.type || ''
|
|
stage.value = item.stage || 'new'
|
|
notes.value = item.notes || ''
|
|
followUpDate.value = item.follow_up_date || ''
|
|
}, { immediate: true })
|
|
|
|
function stagesForType() {
|
|
if (props.item?.direction === 'outbound') return OUTBOUND_STAGES
|
|
if (type.value === 'donation') return DONATION_STAGES
|
|
if (type.value === 'other') return OTHER_STAGES
|
|
return ['new']
|
|
}
|
|
|
|
watch(type, () => {
|
|
if (!stagesForType().includes(stage.value)) {
|
|
stage.value = 'new'
|
|
}
|
|
})
|
|
|
|
async function save() {
|
|
const updated = await updateItem(props.item.id, {
|
|
sender_id: senderId.value || null,
|
|
type: type.value || null,
|
|
stage: stage.value,
|
|
notes: notes.value || null,
|
|
follow_up_date: followUpDate.value || null,
|
|
})
|
|
emit('saved', updated)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="item" class="modal-backdrop" @click.self="emit('close')">
|
|
<div class="modal">
|
|
<p class="raw-content">{{ item.raw_content }}</p>
|
|
|
|
<label>Sender/ID
|
|
<input v-model="senderId" type="text" />
|
|
</label>
|
|
|
|
<label>Type
|
|
<select v-model="type">
|
|
<option value="">unset</option>
|
|
<option value="donation">Donation</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
</label>
|
|
|
|
<label>Stage
|
|
<select v-model="stage">
|
|
<option v-for="s in stagesForType()" :key="s" :value="s">{{ s }}</option>
|
|
</select>
|
|
</label>
|
|
|
|
<label>Notes
|
|
<textarea v-model="notes" placeholder="Save for later..."></textarea>
|
|
</label>
|
|
|
|
<label>Follow-up date
|
|
<input v-model="followUpDate" type="date" />
|
|
</label>
|
|
|
|
<div class="actions">
|
|
<button @click="save">Save</button>
|
|
<button @click="emit('close')">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.modal-backdrop {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.4);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.modal {
|
|
background: var(--color-bg);
|
|
border-radius: var(--radius);
|
|
padding: var(--spacing-lg);
|
|
max-width: 480px;
|
|
width: 90%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
label {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
font-size: 0.9rem;
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
.actions {
|
|
display: flex;
|
|
gap: var(--spacing-sm);
|
|
justify-content: flex-end;
|
|
}
|
|
</style>
|