---
title: "Завдання Ring"
description: "Повідомлення типу завдання з життєвим циклом виконавця, ескроу кредитів, деревом /tasks і можливістю перетворення на запит"
locale: "uk"
---
# Завдання Ring

> **Info**
> Використовуйте вкладки **Founder** / **Developer** на бічній панелі документації, щоб відфільтрувати цю сторінку. Ring Tasks розширюють обмін повідомленнями структурованим типом повідомлення **`task`** — це не окремий продукт CRM (завдання CRM для адміністративної пошти за адресою `/admin/crm/tasks` не пов'язані з ними).

Учасники надсилають одне одному **завдання** в чаті: опис, необов'язкового виконавця, кінцевий термін, бюджет і необов'язковий **ескроу кредитів**. Глобальне дерево **/tasks** містить список останніх завдань у всіх розмовах. Автори можуть перетворити завдання на особисту можливість **`request`**.

|| Шар | Де |
||-------|--------|
|| Типи | `features/chat/types/index.ts` — `Message.type: 'task'`, `TaskMetadata` |
|| Модуль функції | `features/tasks/**` |
|| Серверні дії | `app/_actions/tasks.ts` |
|| API списків | `GET /api/tasks`, `GET /api/tasks/conversation/[id]` |
|| Сторінки | `/tasks`, `/tasks/[chatId]` |
|| Глибоке посилання на чат | `/messages?c=` (без сегмента `/messages/[id]`) |

## Машина станів (спільна)

```mermaid
stateDiagram-v2
  [*] --> available: create_no_assignee
  [*] --> in_progress: create_with_assignee
  available --> requested: Request
  available --> in_progress: Start
  requested --> in_progress: reporter_approve
  requested --> available: reporter_reject
  in_progress --> completed: Done
  completed --> accepted: Accept
  completed --> disputed: Dispute
  available --> canceled: Cancel
  requested --> canceled: Cancel
  in_progress --> canceled: Cancel
```

|| Статус | Значення |
||--------|---------|
|| `available` | Опубліковано без виконавця |
|| `requested` | Хтось попросив призначити його виконавцем |
|| `in_progress` | Виконавця призначено |
|| `completed` | Виконано автором або виконавцем |
|| `accepted` | Автор прийняв роботу (ескроу, якщо він утримується, вивільняється) |
|| `canceled` | Автор скасував (утримуваний ескроу повертається на кредитний баланс) |
|| `disputed` | Автор оскаржив після виконання, коли ескроу утримується |

### For founders

## Чому завдання важливі для вашого клону

Одних повідомлень недостатньо, щоб завершити роботу. Завдання перетворюють чат на просту робочу домовленість: хто відповідає за роботу, коли її потрібно виконати та чи утримується оплата до прийняття.

  
- **[Повідомлення](/docs/features/messaging.md)** — Завдання відображаються як віджети в тому самому списку розмов.

  
- **[Можливості](/docs/features/opportunities.md)** — Перетворіть завдання на публічний/приватний **запит** за потреби залучити аудиторію маркетплейсу.

  
- **[Кредити гаманця](/docs/features/wallet.md)** — Ескроу v1 утримує та вивільняє **кредитний баланс** (бали).

### Типові сценарії

1. **Прямий чат** — ви призначаєте співрозмовнику результат із кінцевим терміном; він позначає його як Done, а ви натискаєте Accept.
2. **Груповий чат** — опублікуйте завдання без виконавця; будь-який учасник натискає **Start** (або **Request** для погодження).
3. **Оплачувана послуга** — додайте кредитний бюджет і ескроу; кошти утримуються до Accept; Cancel до прийняття повертає ваші кредити.
4. **Чат став замалим** — перетворіть завдання на можливість **request**, щоб більше людей могли подати заявку.

### Контрольний список оператора

- Учасники знаходять Tasks поруч із Messages на бічній панелі (`/tasks`).
- Ескроу підтримує **кредитний баланс**, **фіат (WayForPay/Stripe)** і **нативний токен** через PaymentConductor (`PaymentPurpose.task_escrow`).
- Адміністраторське вирішення спорів: `/admin/crm/task-escrows` — вивільнення, повернення або примусове скасування утримуваних ескроу.

### For developers

## Тип повідомлення та метадані

Розширте об'єднання типів чату (також додайте до allowlist у `app/api/conversations/[id]/messages/route.ts`):

`text` | `image` | `file` | `system` | `payment_request` | `env_request` | **`task`**

`TaskMetadata` (`kind: 'task'`) містить автора, виконавця, статус, кінцевий термін, бюджет, заготовку ескроу, часові мітки життєвого циклу, `opportunityId` і `audit[]`.

|| Правило вмісту | Деталі |
||--------------|--------|
|| `message.content` | Повний опис |
|| Заголовок віджета | Перші два непорожні рядки |
|| Резервний варіант | `Task: {firstLine}` |
|| Редагування | Лише коли `status === 'available'` і ескроу не має статусу `held`/`released` (дія завдання, а не 15-хвилинне вікно редагування тексту) |
|| Версіювання | Перезапис + `editedAt` + `audit[]` — без `versions[]` у вмісті |

Шлях рендерингу віджета аналогічний запитам на оплату/середовище у `features/chat/components/message-bubble.tsx` → `TaskMessageWidget`.

## Карта модулів

|| Модуль | Роль |
||--------|------|
|| `features/tasks/services/task-service.ts` | Переходи станів + аудит |
|| `features/tasks/services/task-query-service.ts` | Запит дерева (3 на чат, максимум 21) + список для окремого чату |
|| `features/tasks/services/task-escrow-service.ts` | Утримання, повернення та вивільнення кредитів / WFP / нативних коштів |
|| `features/tasks/services/notify.ts` | Сповіщення TASK_ASSIGNED / TASK_UPDATED |
|| `app/api/tasks/escrow/[id]/checkout/route.ts` | Checkout PaymentConductor для очікуваного ескроу |
|| `features/tasks/components/task-message-widget.tsx` | Дії в чаті |
|| `features/tasks/components/task-compose-dialog.tsx` | Інтерфейс створення з композитора |
|| `features/tasks/components/tasks-tree.tsx` | Вкладки фільтрів `/tasks` |
|| `app/_actions/tasks.ts` | Серверні дії |

## Дії створення та життєвого циклу

```ts
// app/_actions/tasks.ts (names)
createTaskMessage
startTask | requestTask | approveTaskRequest | rejectTaskRequest
completeTask | acceptTask | disputeTask | cancelTask
editTaskContent | deleteTask
convertTaskToOpportunity
```

Сповіщення: `NotificationType.TASK_ASSIGNED`, `TASK_UPDATED`.

## Контракт запиту `/tasks`

|| Кінцева точка | Поведінка |
||----------|----------|
|| `GET /api/tasks?filter=` | `all` \| `available` \| `in_progress` \| `completed` — до **3** найновіших завдань на розмову, глобальний ліміт **21** |
|| `GET /api/tasks/conversation/[id]` | Усі завдання в одному чаті |
|| Відповідність фільтрів | `available` → available+requested; `in_progress` → in_progress; `completed` → completed+accepted |

Маршрути: `ROUTES.TASKS`, `ROUTES.TASK(chatId)` у `constants/routes.ts`.

## Ескроу (credit_balance · fiat · native_token)

`PaymentPurpose` містить `task_escrow` (`lib/payments/conductor/types.ts`). Посилання на замовлення: `task_{escrowId}_{timestamp}`.

|| Подія | Поведінка |
||-------|----------|
|| Фінансування під час створення (кредит) | `creditBalanceService.spendCredits` → документ ескроу `held` + метадані повідомлення |
|| Фінансування під час створення (фіат/нативні кошти) | Документ ескроу в стані очікування → `needsCheckout` → `POST /api/tasks/escrow/[id]/checkout` |
|| Вебхук WFP/Stripe / вбудований нативний платіж | `markHeldFromPayment` → `held` + сповіщення автора |
|| Скасування до вивільнення | Кредит: `addFiatUsd` desk_refund + стабільний `reference_id`; WFP: `refundStorePayment`; нативні кошти: treasury→автор **або резервний варіант — еквівалент у кредитах** (як під час вивільнення) |
|| Прийняття після виконання | Кредит/фіат: `addFiatUsd` + `reference_id`; нативні кошти: treasury→виконавець або резервний варіант — еквівалент у кредитах |
|| Паралельне отримання | **Справжній CAS**: `db().transaction` + `FOR UPDATE` — лише одна з дій accept/cancel може отримати `held` |
|| Адміністраторське оскарження | `/admin/crm/task-escrows` — вивільнення / повернення / скасування через `TaskEscrowService.adminResolve` |

Колекція: `task_escrows` (документ JSONB через DatabaseService). Стабільні посилання реєстру: `task_escrow_release_${id}`, `task_escrow_refund_${id}` (зберігаються під час отримання до переміщення коштів).

### Контрольний список операцій нативного ескроу

1. Спочатку перевірте стан купівлі desk (`SOLANA_TREASURY_PRIVATE_KEY`, `SOLANA_FEE_PAYER_PRIVATE_KEY`, RPC).
2. Виконавець/автор повинні мати кастодіальний нативний гаманець для виведення в блокчейн; інакше спрацьовує резервний варіант із кредитами.
3. Блокування hot-key у mainnet запускає резервний варіант із кредитами — це очікувана поведінка, а не завислий ескроу `held`.
4. Див. `features/wallet/ops/TREASURY-SWAP-OPS.md` § Task escrow.

## Перетворення → можливість request

`convertTaskToOpportunity` (лише автор, не скасоване) викликає `createOpportunity` з `type: 'request'`, `isPrivate: true`, `visibility: 'subscriber'`, `tags: []` і джерелом у **`metadata.source`**:

`{ kind: 'chat_task', messageId, conversationId }`.

Після цього завдання зберігає `opportunityId` у метаданих. Картки стрічки приховують застарілі теги `sourceMessageId:` / `sourceConversationId:` / `chat_task` через `splitOpportunityTags` (`features/opportunities/lib/opportunity-tags.ts`). **Власник** бачить чип **From chat** → `/messages?c=` (той самий запит, що й глибоке посилання на чат на цій сторінці). Немає зворотного заповнення, яке копіює старі закодовані теги в `metadata.source`.

## Кроки інтеграції

  
Створіть завдання з композитора чату (ListTodo) — необов'язкові виконавець, кінцевий термін, кредитний бюджет і ескроу.

  
Співрозмовник натискає Start / Request → Done → автор натискає Accept (або Dispute, коли ескроу утримується).

  
Переглядайте `/tasks` або `/tasks/[chatId]`; відкривайте чат через `/messages?c=`.

  
За потреби перетворіть завдання на можливість → список запитів `/opportunities`.

## Беклог

- [features/messaging](/docs/features/messaging.md) — Передумова: завдання — це віджети повідомлень чату в тому самому списку розмов.

- [features/opportunities](/docs/features/opportunities.md) — Наступний крок: перетворені запити потрапляють у стрічку перегляду з джерелом From chat, а не машинними тегами.

- [features/wallet](/docs/features/wallet.md) — Залежить від: ескроу завдань v1 утримує та вивільняє бали кредитного балансу.

- [features/payment-conductor](/docs/features/payment-conductor.md) — Поглиблено: PaymentPurpose task_escrow повторно використовує рейки Conductor.
