User: я тебе кину 2 компонента , первый это список контактов, а второй карточка контакта , из них надо сдлеать форму, используй tailwind, vee validate/nuxt, nuxt , typescript, щас кину компоненты 
Assistant:  ОтОтлично, жду твои компоненты. После этого я помогу сделать из них форму с использованием Tailwind, vee-validate, Nuxt и TypeScript.
User: карточка контакта <script lang="ts" setup>
import { useClipboard } from "@vueuse/core";

export type Contact = {
  phoneNumber: string;
  email: string;
  link: string;
  comment: string;
  fullName: string;
  position: string;
};

type Props = {
  item: Contact;
  isEdit: boolean;
};

const snackbar = useSnackbar();

const props = defineProps<Props>();

const { copy } = useClipboard();

const handleCopy = async (value: string) => {
  try {
    await copy(value);
    snackbar.add({ type: "success", text: "Скопировано" });
  } catch (e) {
    console.error(e);
  }
};
</script>

<template>
  <div class="bg-dark-black rounded-xl p-4 flex flex-col gap-4">
    <div class="flex flex-row justify-between items-center">
      <div class="flex flex-col gap-2 text-start">
        <NText class="text-lg font-semibold">{{ item.fullName }}</NText>
        <NText class="text-primary" size="sm">{{ item.position }}</NText>
      </div>
      <div v-if="isEdit" class="bg-danger/10 p-2 rounded-md flex-shrink-0">
        <NIcon name="trash" class="text-danger w-6 h-6" size="24" />
      </div>
    </div>

    <div class="flex flex-col gap-2">
      <div
        class="flex flex-row gap-2 py-1 pr-2 pl-1 items-center bg-[#1D1F22] rounded-lg cursor-pointer"
      >
        <div class="bg-primary p-1 rounded-lg">
          <NIcon name="phone" class="text-white" size="20" />
        </div>
        <NText size="sm">{{ item.phoneNumber }}</NText>
        <div @click="handleCopy(item.phoneNumber)" class="ml-auto">
          <NIcon name="copy" class="text-white hover:text-primary" size="20" />
        </div>
      </div>

      <div
        class="flex flex-row gap-2 py-1 pr-2 pl-1 items-center bg-[#1D1F22] rounded-lg cursor-pointer"
      >
        <div class="bg-primary p-1 rounded-lg">
          <NIcon name="mail" class="text-white" size="20" />
        </div>
        <NText size="sm">{{ item.email }}</NText>
        <div @click="handleCopy(item.email)" class="ml-auto">
          <NIcon name="copy" class="text-white hover:text-primary" size="20" />
        </div>
      </div>

      <div
        class="flex flex-row gap-2 py-1 pr-2 pl-1 items-center bg-[#1D1F22] rounded-lg cursor-pointer"
      >
        <div class="bg-primary p-1 rounded-lg">
          <NIcon name="message" class="text-white" size="20" />
        </div>
        <NText size="sm">{{ item.link }}</NText>
        <div @click="handleCopy(item.link)" class="ml-auto">
          <NIcon name="copy" class="text-white hover:text-primary" size="20" />
        </div>
      </div>
      <div
        v-if="item.comment"
        class="py-1 pr-2 pl-1 bg-[#1D1F22] rounded-lg cursor-pointer"
      >
        <NText size="sm">{{ item.comment }}</NText>
      </div>
    </div>
  </div>
</template>

Assistant:  ОтОтлично, теперь жду второй компонент — список контактов. После этого я сделаю форму, используя Tailwind, vee-validate, Nuxt и TypeScript.
User: список контактов <script lang="ts" setup>
import { ref } from "vue";

const searchTerm = ref("");

const isEdit = ref(false);

const data = [
  {
    phoneNumber: "+7 (999) 999 99 99",
    email: "Lishin2212318@mail.com",
    link: "@LishinPavel2123128",
    comment: "Отвечает с 10 до 19",
    fullName: "Фамилия Имя Отчество",
    position: "Должность",
  },
];
</script>

<template>
  <NModal title="Контакты">
    <template #headerButton>
      <div
        @click="() => (isEdit = !isEdit)"
        class="hover:bg-white-trans transition-all rounded-lg p-2 cursor-pointer"
      >
        <NIcon name="pencil" size="24" class="text-white" />
      </div>
    </template>
    <div class="p-5 flex flex-col gap-3">
      <div class="flex flex-row gap-2">
        <NSearch
          v-model="searchTerm"
          param-name="q-contacts"
          input-class="w-full"
          placeholder="Поиск..."
        />
        <NButton v-if="isEdit" variant="secondary" size="sm" icon="plus"
          >Создать</NButton
        >
      </div>
      <div v-for="(item, i) in data" :key="i">
        <ContactCard :item="item" :isEdit="isEdit" />
      </div>
    </div>
  </NModal>
</template>
 
Assistant:  ОтОтлично, у нас есть компоненты карточки контакта и список контактов в модальном окне с возможностью поиска и режимом редактирования. Теперь сделаем из них форму, используя:

- Tailwind CSS для стилей
- vee-validate для валидации форм
- Nuxt + TypeScript (Composition API + `<script setup>`)

Я сделаю форму для добавления и редактирования контактов на базе вашего существующего кода, подключу vee-validate для валидации и управление состоянием. В режиме редактирования можно править поля, а если режим выключен — показывать карточку.

---

### Создаем новый компонент `ContactForm.vue`:

```vue
<script lang="ts" setup>
import { reactive, watch, toRefs } from "vue";
import { useField, useForm } from "vee-validate";
import * as yup from "yup";
import type { Contact } from "./ContactCard.vue";

const props = defineProps<{
  modelValue: Contact | null;
  isEdit: boolean;
}>();

const emit = defineEmits<{
  (e: "update:modelValue", value: Contact): void;
  (e: "save", value: Contact): void;
  (e: "cancel"): void;
}>();

// Схема валидации
const schema = yup.object({
  fullName: yup.string().required("ФИО обязательно"),
  position: yup.string().required("Должность обязательна"),
  phoneNumber: yup
    .string()
    .required("Телефон обязателен")
    .matches(/^\+?[\d\s()-]+$/, "Неверный формат телефона"),
  email: yup.string().email("Неверный email").required("Email обязателен"),
  link: yup.string().nullable(),
  comment: yup.string().nullable(),
});

const { handleSubmit, errors, resetForm, validate } = useForm<Contact>({
  validationSchema: schema,
  initialValues: props.modelValue || {
    fullName: "",
    position: "",
    phoneNumber: "",
    email: "",
    link: "",
    comment: "",
  },
});

const { value: fullName } = useField<string>("fullName");
const { value: position } = useField<string>("position");
const { value: phoneNumber } = useField<string>("phoneNumber");
const { value: email } = useField<string>("email");
const { value: link } = useField<string>("link");
const { value: comment } = useField<string>("comment");

// При изменении входящих данных сбрасываем форму
watch(
  () => props.modelValue,
  (newVal) => {
    resetForm({
      values: newVal || {
        fullName: "",
        position: "",
        phoneNumber: "",
        email: "",
        link: "",
        comment: "",
      },
    });
  },
  { immediate: true }
);

const onSubmit = handleSubmit((values) => {
  emit("save", values);
});

const onCancel = () => {
  emit("cancel");
};
</script>

<template>
  <form @submit.prevent="onSubmit" class="bg-dark-black rounded-xl p-6 flex flex-col gap-4 max-w-lg mx-auto">
    <!-- ФИО -->
    <div>
      <label for="fullName" class="block text-white font-semibold mb-1">ФИО</label>
      <input
        id="fullName"
        type="text"
        v-model="fullName"
        :disabled="!props.isEdit"
        class="w-full rounded-md p-2 bg-[#1D1F22] border border-gray-700 focus:border-primary text-white"
      />
      <span class="text-danger text-sm mt-1 block">{{ errors.fullName }}</span>
    </div>

    <!-- Должность -->
    <div>
      <label for="position" class="block text-white font-semibold mb-1">Должность</label>
      <input
        id="position"
        type="text"
        v-model="position"
        :disabled="!props.isEdit"
        class="w-full rounded-md p-2 bg-[#1D1F22] border border-gray-700 focus:border-primary text-white"
      />
      <span class="text-danger text-sm mt-1 block">{{ errors.position }}</span>
    </div>

    <!-- Телефон -->
    <div>
      <label for="phoneNumber" class="block text-white font-semibold mb-1">Телефон</label>
      <input
        id="phoneNumber"
        type="text"
        v-model="phoneNumber"
        :disabled="!props.isEdit"
        class="w-full rounded-md p-2 bg-[#1D1F22] border border-gray-700 focus:border-primary text-white"
      />
      <span class="text-danger text-sm mt-1 block">{{ errors.phoneNumber }}</span>
    </div>

    <!-- Email -->
    <div>
      <label for="email" class="block text-white font-semibold mb-1">Email</label>
      <input
        id="email"
        type="email"
        v-model="email"
        :disabled="!props.isEdit"
        class="w-full rounded-md p-2 bg-[#1D1F22] border border-gray-700 focus:border-primary text-white"
      />
      <span class="text-danger text-sm mt-1 block">{{ errors.email }}</span>
    </div>

    <!-- Ссылка -->
    <div>
      <label for="link" class="block text-white font-semibold mb-1">Ссылка</label>
      <input
        id="link"
        type="text"
        v-model="link"
        :disabled="!props.isEdit"
        class="w-full rounded-md p-2 bg-[#1D1F22] border border-gray-700 focus:border-primary text-white"
      />
      <span class="text-danger text-sm mt-1 block">{{ errors.link }}</span>
    </div>

    <!-- Комментарий -->
    <div>
      <label for="comment" class="block text-white font-semibold mb-1">Комментарий</label>
      <textarea
        id="comment"
        v-model="comment"
        :disabled="!props.isEdit"
        rows="3"
        class="w-full rounded-md p-2 bg-[#1D1F22] border border-gray-700 focus:border-primary text-white resize-none"
      />
      <span class="text-danger text-sm mt-1 block">{{ errors.comment }}</span>
    </div>

    <div v-if="props.isEdit" class="flex gap-3 justify-end mt-4">
      <button
        type="submit"
        class="bg-primary text-black px-4 py-2 rounded-md font-semibold hover:bg-primary-dark transition"
      >
        Сохранить
      </button>
      <button
        type="button"
        @click="onCancel"
        class="bg-gray-700 text-white px-4 py-2 rounded-md font-semibold hover:bg-gray-600 transition"
      >
        Отмена
      </button>
    </div>
  </form>
</template>
```

---

### Обновляем компонент списка контактов теперь с формой и списком

```vue
<script lang="ts" setup>
import { ref } from "vue";
import ContactCard, { Contact } from "./ContactCard.vue";
import ContactForm from "./ContactForm.vue";

const searchTerm = ref("");
const isEdit = ref(false);

// Исходные данные
const data = ref<Contact[]>([
  {
    phoneNumber: "+7 (999) 999 99 99",
    email: "Lishin2212318@mail.com",
    link: "@LishinPavel2123128",
    comment: "Отвечает с 10 до 19",
    fullName: "Фамилия Имя Отчество",
    position: "Должность",
  },
]);

const showForm = ref(false);
const formModel = ref<Contact | null>(null);

const onAddNew = () => {
  formModel.value = null;
  showForm.value = true;
  isEdit.value = true;
};

const onEdit = (contact: Contact) => {
  formModel.value = { ...contact };
  showForm.value = true;
  isEdit.value = true;
};

const onSave = (contact: Contact) => {
  if (formModel.value) {
    // Редактирование — обновляем в массиве
    const idx = data.value.findIndex(
      (c) => c.email === formModel.value?.email && c.phoneNumber === formModel.value?.phoneNumber
    );
    if (idx !== -1) {
      data.value[idx] = contact;
    }
  } else {
    // Добавление нового
    data.value.push(contact);
  }
  showForm.value = false;
  isEdit.value = false;
  formModel.value = null;
};

const onCancel = () => {
  showForm.value = false;
  isEdit.value = false;
  formModel.value = null;
};
</script>

<template>
  <NModal title="Контакты">
    <template #headerButton>
      <div
        @click="isEdit = !isEdit"
        class="hover:bg-white-trans transition-all rounded-lg p-2 cursor-pointer"
      >
        <NIcon name="pencil" size="24" class="text-white" />
      </div>
    </template>

    <div class="p-5 flex flex-col gap-3">
      <div class="flex flex-row gap-2 mb-4">
        <NSearch
          v-model="searchTerm"
          param-name="q-contacts"
          input-class="w-full"
          placeholder="Поиск..."
        />
        <NButton v-if="isEdit && !showForm" variant="secondary" size="sm" icon="plus" @click="onAddNew">
          Создать
        </NButton>
      </div>

      <div v-if="showForm" class="mb-4">
        <ContactForm
          :modelValue="formModel"
          :isEdit="true"
          @save="onSave"
          @cancel="onCancel"
        />
      </div>

      <div v-else>
        <div
          v-for="(item, i) in data.filter(c => {
            const term = searchTerm.toLowerCase();
            return (
              c.fullName.toLowerCase().includes(term) ||
              c.email.toLowerCase().includes(term) ||
              c.phoneNumber.includes(searchTerm) ||
              c.position.toLowerCase().includes(term)
            );
          })"
          :key="i"
          @click="isEdit ? onEdit(item) : null"
          class="cursor-pointer"
        >
          <ContactCard :item="item" :isEdit="isEdit" />
        </div>
      </div>
    </div>
  </NModal>
</template>
```

---

### Итог:

- При включении режима редактирования появляется кнопка "Создать", по клику открывается форма для добавления контакта.
- По клику на карточку в режиме редактирования открывается форма с данными для правки.
- Валидация полей реализована через vee-validate + yup.
- Стили сделаны через Tailwind (в стиле вашего текущего дизайна).
- Отправка формы эмитит событие `save` и обновляет список.

Если нужно — могу помочь еще с удалением и сохранением в API. Пиши!
User: вохможно ли кодить весь день с 1ч перерыва 
Assistant: