WIP: local changes
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="page-header">
|
||||
<div class="flex items-center gap-3">
|
||||
<button class="btn btn-secondary btn-sm" @click="$router.back()">← Назад</button>
|
||||
<h2>{{ contact?.name || 'Загрузка...' }}</h2>
|
||||
</div>
|
||||
<button v-if="contact" class="btn btn-primary btn-sm" @click="editing = true">Редактировать</button>
|
||||
</div>
|
||||
|
||||
<div class="page-content" v-if="contact">
|
||||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:20px;">
|
||||
<!-- Info card -->
|
||||
<div class="card">
|
||||
<h3 style="font-size:14px;margin-bottom:16px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em;">Информация</h3>
|
||||
<div class="form-group">
|
||||
<label>Email</label>
|
||||
<div>{{ contact.email || '—' }}</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Телефон</label>
|
||||
<div>{{ contact.phone || '—' }}</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Организация</label>
|
||||
<div>{{ contact.organization || '—' }}</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Должность</label>
|
||||
<div>{{ contact.position || '—' }}</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Заметки</label>
|
||||
<div style="white-space:pre-wrap;">{{ contact.notes || '—' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Relations card -->
|
||||
<div class="card">
|
||||
<div class="flex justify-between items-center" style="margin-bottom:16px;">
|
||||
<h3 style="font-size:14px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em;">Связи ({{ contactRelations.length }})</h3>
|
||||
<button class="btn btn-primary btn-sm" @click="showAddRelation = true">+ Добавить</button>
|
||||
</div>
|
||||
<div v-if="contactRelations.length === 0" class="empty-state" style="padding:20px 0;">
|
||||
<p>Нет связей с другими контактами.</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div
|
||||
v-for="rel in contactRelations"
|
||||
:key="rel.id"
|
||||
class="flex justify-between items-center"
|
||||
style="padding:8px 0; border-bottom:1px solid var(--border);"
|
||||
>
|
||||
<div>
|
||||
<span style="font-weight:500;">{{ rel.source === contact.id ? rel.target_name : rel.source_name }}</span>
|
||||
<span :class="`badge badge-${rel.relation_type}`" style="margin-left:8px;">{{ relLabel(rel.relation_type) }}</span>
|
||||
<div class="text-muted mt-1">{{ rel.description }}</div>
|
||||
</div>
|
||||
<button class="btn btn-danger btn-sm" @click="removeRelation(rel.id)">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit modal -->
|
||||
<div v-if="editing" class="modal-overlay" @click.self="editing = false">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h3>Редактировать контакт</h3>
|
||||
<button class="btn btn-secondary btn-sm" @click="editing = false">✕</button>
|
||||
</div>
|
||||
<ContactForm :initial="contact" @submit="onUpdate" @cancel="editing = false" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add relation modal -->
|
||||
<div v-if="showAddRelation" class="modal-overlay" @click.self="showAddRelation = false">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h3>Добавить связь</h3>
|
||||
<button class="btn btn-secondary btn-sm" @click="showAddRelation = false">✕</button>
|
||||
</div>
|
||||
<div v-if="relError" class="alert alert-error">{{ relError }}</div>
|
||||
<div class="form-group">
|
||||
<label>С кем связать</label>
|
||||
<select v-model="newRel.targetId" class="form-control">
|
||||
<option value="">— Выберите контакт —</option>
|
||||
<option v-for="c in otherContacts" :key="c.id" :value="c.id">{{ c.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Тип связи</label>
|
||||
<select v-model="newRel.type" class="form-control">
|
||||
<option v-for="rt in relationTypes" :key="rt.value" :value="rt.value">{{ rt.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Описание (необязательно)</label>
|
||||
<input v-model="newRel.description" class="form-control" placeholder="Например: знакомы с 2018 года" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="showAddRelation = false">Отмена</button>
|
||||
<button class="btn btn-primary" :disabled="!newRel.targetId" @click="addRelation">Создать связь</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import api from '../api'
|
||||
import { useContactsStore } from '../stores/contacts'
|
||||
import ContactForm from '../components/ContactForm.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const store = useContactsStore()
|
||||
const contact = ref(null)
|
||||
const editing = ref(false)
|
||||
const showAddRelation = ref(false)
|
||||
const relationTypes = ref([])
|
||||
const relError = ref('')
|
||||
const newRel = ref({ targetId: '', type: 'acquaintance', description: '' })
|
||||
|
||||
const contactId = computed(() => Number(route.params.id))
|
||||
|
||||
const contactRelations = computed(() =>
|
||||
store.relations.filter(
|
||||
(r) => r.source === contactId.value || r.target === contactId.value
|
||||
)
|
||||
)
|
||||
|
||||
const otherContacts = computed(() =>
|
||||
store.contacts.filter((c) => c.id !== contactId.value)
|
||||
)
|
||||
|
||||
function relLabel(type) {
|
||||
return relationTypes.value.find((r) => r.value === type)?.label || type
|
||||
}
|
||||
|
||||
async function loadContact() {
|
||||
const { data } = await api.get(`/contacts/${contactId.value}/`)
|
||||
contact.value = data
|
||||
}
|
||||
|
||||
async function onUpdate(data) {
|
||||
await store.updateContact(contactId.value, data)
|
||||
contact.value = { ...contact.value, ...data }
|
||||
editing.value = false
|
||||
}
|
||||
|
||||
async function addRelation() {
|
||||
relError.value = ''
|
||||
try {
|
||||
await store.createRelation({
|
||||
source: contactId.value,
|
||||
target: newRel.value.targetId,
|
||||
relation_type: newRel.value.type,
|
||||
description: newRel.value.description,
|
||||
})
|
||||
showAddRelation.value = false
|
||||
newRel.value = { targetId: '', type: 'acquaintance', description: '' }
|
||||
} catch (e) {
|
||||
const msg = e.response?.data
|
||||
relError.value = typeof msg === 'object' ? JSON.stringify(msg) : String(msg)
|
||||
}
|
||||
}
|
||||
|
||||
async function removeRelation(id) {
|
||||
await store.deleteRelation(id)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadContact()
|
||||
await Promise.all([store.fetchContacts(), store.fetchRelations()])
|
||||
const { data } = await api.get('/relation-types/')
|
||||
relationTypes.value = data
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user