WIP: local changes

This commit is contained in:
2026-04-08 15:29:52 +03:00
parent 07842540ba
commit 81ba6cd076
56 changed files with 2015 additions and 0 deletions
+181
View File
@@ -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>