Skip to content

feat(crm): Fase 7 — criar/editar/excluir contato, sincronização com W…#55

Merged
sejaanomalo-web merged 1 commit into
mainfrom
claude/crm-fase7-contatos-crud-whatsapp-sync
Jul 12, 2026
Merged

feat(crm): Fase 7 — criar/editar/excluir contato, sincronização com W…#55
sejaanomalo-web merged 1 commit into
mainfrom
claude/crm-fase7-contatos-crud-whatsapp-sync

Conversation

@sejaanomalo-web

@sejaanomalo-web sejaanomalo-web commented Jul 12, 2026

Copy link
Copy Markdown
Owner

…hatsApp do celular

Permite criar contato manual pelo CRM (nome, telefone, e-mail, escolhendo o número/empresa), editar nome/e-mail e excluir de vez — mensagens, atividades e etiquetas somem junto (cascade já existente no schema).

Sincronização com o WhatsApp do celular, dentro do que o protocolo permite (pesquisado contra a documentação da Evolution API antes de implementar — não existe endpoint de criar/editar/apagar contato remotamente, contato é dado do catálogo do próprio celular):

  • Criar: o contato existe no CRM na hora; vira conversa de verdade no WhatsApp do celular quando a primeira mensagem é enviada (é assim que o WhatsApp funciona — mandar mensagem sincroniza automaticamente pra todos os dispositivos vinculados). Confere best-effort se o número existe no WhatsApp antes de salvar (aviso, não bloqueia).
  • Excluir: opção de também arquivar a conversa no celular (o mais perto de "excluir" que a API expõe — WhatsApp não tem "apagar contato" remoto).
  • Editar: só no CRM mesmo — não existe API pra alterar nome salvo no celular ou perfil de terceiros.
  • Vice-versa (celular → CRM): chat apagado no celular (evento CHATS_DELETE) arquiva o lead automaticamente em vez de sumir sem explicação — fica recuperável em "Ver arquivados" (link novo na sidebar de Conversas).

Revisão adversarial (multi-agente) encontrou e corrigiu 8 bugs antes do commit: etapa padrão de lead novo podia vazar etapa PRIVADA de outro usuário (sem scoping por usuario_id), mensagem de erro de telefone duplicado virava oráculo pra descobrir contato de outro usuário, heurística de DDI sem confirmação visual, edições silenciosamente viravam no-op sem avisar, processamento de CHATS_DELETE sem limite/lote, e o mesmo bug de estado preso no menu (⋮) já corrigido no Kanban reapareceu no menu do contato.

Summary by CodeRabbit

  • Novos Recursos

    • Criação manual de contatos com nome, telefone e e-mail.
    • Edição, exclusão, arquivamento e restauração de contatos.
    • Visualização separada de conversas arquivadas, com contagem total.
    • Verificação automática de números no WhatsApp durante o cadastro.
    • Arquivamento automático de conversas após eventos de exclusão.
  • Melhorias

    • Novas opções de gerenciamento disponíveis diretamente na conversa.
    • Atualização automática das listas após alterações nos contatos.
    • Documentação atualizada sobre os eventos de CRM processados.

…hatsApp do celular

Permite criar contato manual pelo CRM (nome, telefone, e-mail, escolhendo o
número/empresa), editar nome/e-mail e excluir de vez — mensagens, atividades
e etiquetas somem junto (cascade já existente no schema).

Sincronização com o WhatsApp do celular, dentro do que o protocolo permite
(pesquisado contra a documentação da Evolution API antes de implementar —
não existe endpoint de criar/editar/apagar contato remotamente, contato é
dado do catálogo do próprio celular):
- Criar: o contato existe no CRM na hora; vira conversa de verdade no
  WhatsApp do celular quando a primeira mensagem é enviada (é assim que o
  WhatsApp funciona — mandar mensagem sincroniza automaticamente pra todos
  os dispositivos vinculados). Confere best-effort se o número existe no
  WhatsApp antes de salvar (aviso, não bloqueia).
- Excluir: opção de também arquivar a conversa no celular (o mais perto de
  "excluir" que a API expõe — WhatsApp não tem "apagar contato" remoto).
- Editar: só no CRM mesmo — não existe API pra alterar nome salvo no celular
  ou perfil de terceiros.
- Vice-versa (celular → CRM): chat apagado no celular (evento CHATS_DELETE)
  arquiva o lead automaticamente em vez de sumir sem explicação — fica
  recuperável em "Ver arquivados" (link novo na sidebar de Conversas).

Revisão adversarial (multi-agente) encontrou e corrigiu 8 bugs antes do
commit: etapa padrão de lead novo podia vazar etapa PRIVADA de outro usuário
(sem scoping por usuario_id), mensagem de erro de telefone duplicado virava
oráculo pra descobrir contato de outro usuário, heurística de DDI sem
confirmação visual, edições silenciosamente viravam no-op sem avisar,
processamento de CHATS_DELETE sem limite/lote, e o mesmo bug de estado
preso no menu (⋮) já corrigido no Kanban reapareceu no menu do contato.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
metas-anomalo Ready Ready Preview, Comment Jul 12, 2026 12:52am

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

CRM lead lifecycle support now includes manual creation, editing, deletion, restoration, archived-conversation browsing, Evolution chat archiving, and CHATS_DELETE webhook processing.

Changes

CRM lead lifecycle

Layer / File(s) Summary
Evolution API helpers
lib/evolution.ts, lib/crm-leads-actions.ts
Adds WhatsApp number verification and best-effort chat archive operations for connected Evolution instances.
Lead mutation actions
lib/crm-leads-actions.ts
Adds server actions for validated manual creation, editing, deletion, and restoration of user-owned leads.
Chat deletion ingestion and archived queries
lib/crm-inbound.ts, lib/crm-leads.ts, app/api/crm/wa/webhook/.../route.ts
Processes CHATS_DELETE events, archives matching leads, emits realtime pings, supports archived filtering, and counts archived leads.
Archived conversation view
app/dashboard/crm/page.tsx
Adds the arquivados mode, archived count link, and separate conversation list source.
Contact creation and management UI
components/crm/NovoContato.tsx, components/crm/Thread.tsx
Adds manual contact creation and a thread contact menu for editing, deleting, archiving, and restoring leads.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Evolution
  participant Webhook
  participant CRM
  participant Dashboard
  Evolution->>Webhook: Send CHATS_DELETE event
  Webhook->>CRM: Archive matching leads
  CRM->>CRM: Insert realtime ping records
  Dashboard->>CRM: Load archived leads and count
  CRM-->>Dashboard: Return archived conversation data
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main CRM Phase 7 changes: contact creation, editing, deletion, and WhatsApp/Evolution sync.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/crm-fase7-contatos-crud-whatsapp-sync

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
app/dashboard/crm/page.tsx (1)

59-73: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

contarLeadsArquivados() runs on every page load, even on Kanban/Calendário tabs where it's unused.

The "Arquivados (N)" link and leadsConversas are only rendered inside the aba === "conversas" branch, but contarLeadsArquivados() (and conditionally leadsArquivados) is fetched unconditionally in the top-level Promise.all regardless of aba. This adds an unnecessary DB round-trip on every Kanban/Calendário page load.

♻️ Proposed fix
-      listarLeadsInbox(),
-      verArquivados ? listarLeadsInbox({ arquivados: true }) : Promise.resolve([]),
+      listarLeadsInbox(),
+      aba === "conversas" && verArquivados ? listarLeadsInbox({ arquivados: true }) : Promise.resolve([]),
       listarInstancias(),
       listarEtapas(),
       listarEtiquetas(),
       listarAtividadesCalendario(inicioJanela, fimJanela),
       listarProximasAtividades(),
       listarTiposAtividade(),
-      contarLeadsArquivados(),
+      aba === "conversas" ? contarLeadsArquivados() : Promise.resolve(0),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/dashboard/crm/page.tsx` around lines 59 - 73, Update the data-loading
Promise.all around listarLeadsInbox and contarLeadsArquivados so archived leads
and their count are fetched only when aba is "conversas" (and use verArquivados
for the archived list). For Kanban/Calendário, resolve unused values without
invoking the database functions, while preserving leadsConversas behavior in the
Conversas branch.
lib/crm-leads-actions.ts (1)

237-250: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Move the WhatsApp lookup off the response path

This best-effort check still blocks “Criar contato” for up to 10s after the lead is already saved. If the warning can be deferred, run it in after/unstable_after; otherwise shorten the timeout for this call so the action returns faster.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crm-leads-actions.ts` around lines 237 - 250, Update the contact-creation
flow around verificarNumeroWhatsapp so the best-effort WhatsApp lookup no longer
blocks the successful response: defer it using the project’s
after/unstable_after mechanism while preserving the warning behavior, or reduce
this call’s timeout if deferred execution is unavailable. Keep lead persistence,
revalidatePath, and the success return independent of the lookup result.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/crm/NovoContato.tsx`:
- Around line 47-71: Update salvar() to surface the successful response’s
r.aviso before closing the popover and navigating away, using the existing
persistent notification or message mechanism so the WhatsApp warning remains
visible to the user. Preserve the current error handling and successful lead
navigation, and ensure the warning is handled before fechar().

In `@components/crm/Thread.tsx`:
- Around line 643-660: Update the ⋮ trigger button in MenuContato to use the
same pending-state guard as the other action buttons by adding
disabled={pending} to the button using alternar. Preserve the existing click
behavior and styling.

In `@lib/crm-inbound.ts`:
- Around line 347-353: Update processarChatsDelete’s crm_instancias lookup to
select and validate the ativo field, returning the existing unknown-instance
result when the instance is inactive, matching the guard used by
messages.upsert; preserve the existing behavior for active instances.

In `@lib/crm-leads-actions.ts`:
- Line 163: Update the create-contact validation around emailValido and the
related field-processing branches to reject malformed e-mail values instead of
silently omitting them. Match editarLeadAction’s existing behavior by returning
the established "E-mail inválido." error response when validation returns null,
while preserving normal processing for valid or intentionally absent e-mail
values.

---

Nitpick comments:
In `@app/dashboard/crm/page.tsx`:
- Around line 59-73: Update the data-loading Promise.all around listarLeadsInbox
and contarLeadsArquivados so archived leads and their count are fetched only
when aba is "conversas" (and use verArquivados for the archived list). For
Kanban/Calendário, resolve unused values without invoking the database
functions, while preserving leadsConversas behavior in the Conversas branch.

In `@lib/crm-leads-actions.ts`:
- Around line 237-250: Update the contact-creation flow around
verificarNumeroWhatsapp so the best-effort WhatsApp lookup no longer blocks the
successful response: defer it using the project’s after/unstable_after mechanism
while preserving the warning behavior, or reduce this call’s timeout if deferred
execution is unavailable. Keep lead persistence, revalidatePath, and the success
return independent of the lookup result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 854baf75-9c08-4e2c-8e98-3ee38ffc0377

📥 Commits

Reviewing files that changed from the base of the PR and between 3a3d28f and b5edac4.

📒 Files selected for processing (8)
  • app/api/crm/wa/webhook/[segredo]/route.ts
  • app/dashboard/crm/page.tsx
  • components/crm/NovoContato.tsx
  • components/crm/Thread.tsx
  • lib/crm-inbound.ts
  • lib/crm-leads-actions.ts
  • lib/crm-leads.ts
  • lib/evolution.ts

Comment on lines +47 to +71
function salvar() {
if (!instanciaId) {
setErro("Conecte um número em Conexões antes de criar um contato.")
return
}
if (!nome.trim() || !telefone.trim()) {
setErro("Preencha nome e telefone.")
return
}
startTransition(async () => {
const fd = new FormData()
fd.set("instancia_id", instanciaId)
fd.set("nome", nome.trim())
fd.set("telefone", telefone.trim())
fd.set("email", email.trim())
const r = await criarLeadManualAction(fd)
if (!r.ok) {
setErro(r.erro ?? "Erro ao criar contato")
return
}
fechar()
if (r.id) router.push(`/dashboard/crm?view=conversas&lead=${r.id}`)
router.refresh()
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

r.aviso is never surfaced to the user — the WhatsApp-existence warning is silently dropped.

criarLeadManualAction returns an aviso field ("Esse número não parece estar no WhatsApp — confira antes de enviar.") specifically to warn the user best-effort, but salvar() only checks r.ok/r.id and immediately closes the popover and navigates away. The entire verification feature described in the PR (warn before sending to a non-WhatsApp number) has no visible effect for the user.

🐛 Proposed fix
+  const [aviso, setAviso] = useState<string | null>(null)
+
   function salvar() {
     if (!instanciaId) {
       setErro("Conecte um número em Conexões antes de criar um contato.")
       return
     }
     if (!nome.trim() || !telefone.trim()) {
       setErro("Preencha nome e telefone.")
       return
     }
     startTransition(async () => {
       const fd = new FormData()
       fd.set("instancia_id", instanciaId)
       fd.set("nome", nome.trim())
       fd.set("telefone", telefone.trim())
       fd.set("email", email.trim())
       const r = await criarLeadManualAction(fd)
       if (!r.ok) {
         setErro(r.erro ?? "Erro ao criar contato")
         return
       }
+      if (r.aviso) setAviso(r.aviso)
       fechar()
       if (r.id) router.push(`/dashboard/crm?view=conversas&lead=${r.id}`)
       router.refresh()
     })
   }

aviso would then need to be rendered somewhere persistent (e.g. a toast, or held in the thread view via a query param) since the popover closes right after fechar().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/crm/NovoContato.tsx` around lines 47 - 71, Update salvar() to
surface the successful response’s r.aviso before closing the popover and
navigating away, using the existing persistent notification or message mechanism
so the WhatsApp warning remains visible to the user. Preserve the current error
handling and successful lead navigation, and ensure the warning is handled
before fechar().

Comment thread components/crm/Thread.tsx
Comment on lines +643 to +660
return (
<div style={{ position: "relative", flexShrink: 0 }}>
<button
type="button"
onClick={alternar}
title="Opções do contato"
style={{
fontSize: 14,
color: "var(--text-4)",
padding: "6px 8px",
border: "0.5px solid rgba(255,255,255,0.15)",
borderRadius: 8,
lineHeight: 1,
}}
>
</button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Trigger button (⋮) isn't disabled while an action is pending, unlike every other button in this menu.

All action buttons in MenuContato (Editar contato, Salvar, Excluir de vez, Tirar do arquivo, Cancelar) use disabled={pending}, but the "⋮" trigger itself doesn't. If a user re-opens/closes the menu (via alternar()fechar()) while salvarEdicao/excluir/desarquivar is still in flight, fechar() resets modo to "menu" and clears erro before the pending action settles. If that action then fails, setErro(...) is applied to a menu that's already closed and back in "menu" mode — the error message is silently lost and the user has no idea whether the edit/delete/archive actually happened.

🐛 Proposed fix
       <button
         type="button"
         onClick={alternar}
+        disabled={pending}
         title="Opções do contato"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<div style={{ position: "relative", flexShrink: 0 }}>
<button
type="button"
onClick={alternar}
title="Opções do contato"
style={{
fontSize: 14,
color: "var(--text-4)",
padding: "6px 8px",
border: "0.5px solid rgba(255,255,255,0.15)",
borderRadius: 8,
lineHeight: 1,
}}
>
</button>
return (
<div style={{ position: "relative", flexShrink: 0 }}>
<button
type="button"
onClick={alternar}
disabled={pending}
title="Opções do contato"
style={{
fontSize: 14,
color: "var(--text-4)",
padding: "6px 8px",
border: "0.5px solid rgba(255,255,255,0.15)",
borderRadius: 8,
lineHeight: 1,
}}
>
</button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/crm/Thread.tsx` around lines 643 - 660, Update the ⋮ trigger
button in MenuContato to use the same pending-state guard as the other action
buttons by adding disabled={pending} to the button using alternar. Preserve the
existing click behavior and styling.

Comment thread lib/crm-inbound.ts
Comment on lines +347 to +353
const { data: inst } = await db
.from("crm_instancias")
.select("id, empresa_slug")
.eq("instance_name", instanceName)
.maybeSingle()
if (!inst) return { ok: true, info: `instancia desconhecida: ${instanceName}` }
const empresaSlug = inst.empresa_slug as string

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the target file and nearby symbols.
ast-grep outline lib/crm-inbound.ts --view expanded || true

# Show the relevant ranges with line numbers.
sed -n '320,470p' lib/crm-inbound.ts

# Find the sibling handler and any `ativo` checks in this file.
rg -n "processarEventoWebhook|processarChatsDelete|ativo === false|instance_name|crm_instancias" lib/crm-inbound.ts

Repository: sejaanomalo-web/metas-anomalo

Length of output: 7153


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect whether crm_instancias includes an ativo field anywhere in the repo.
rg -n "\bativo\b" .

Repository: sejaanomalo-web/metas-anomalo

Length of output: 17821


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Lightweight read-only probe: compare the two webhook branches in lib/crm-inbound.ts.
python3 - <<'PY'
from pathlib import Path
p = Path("lib/crm-inbound.ts")
text = p.read_text()
for needle in [
    "processarChatsDelete",
    "processarEventoWebhook",
    "if (!inst || inst.ativo === false)",
    "select(\"id, empresa_slug\")",
    "select(\"id, empresa_slug, ativo\")",
]:
    print(f"{needle}: {text.find(needle)}")
PY

Repository: sejaanomalo-web/metas-anomalo

Length of output: 336


🏁 Script executed:

echo hello

Repository: sejaanomalo-web/metas-anomalo

Length of output: 173


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the chat-delete handler to understand the impact of processing inactive-instance events.
sed -n '300,380p' lib/crm-inbound.ts

# Show the message-upsert handler's instance guard for direct comparison.
sed -n '420,470p' lib/crm-inbound.ts

Repository: sejaanomalo-web/metas-anomalo

Length of output: 5012


Ignore inactive instances in processarChatsDelete
CHATS_DELETE still archives leads for deactivated instances, unlike messages.upsert. Add the same ativo guard here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crm-inbound.ts` around lines 347 - 353, Update processarChatsDelete’s
crm_instancias lookup to select and validate the ativo field, returning the
existing unknown-instance result when the instance is inactive, matching the
guard used by messages.upsert; preserve the existing behavior for active
instances.

Comment thread lib/crm-leads-actions.ts
const instanciaId = String(formData.get("instancia_id") ?? "").trim()
const nome = String(formData.get("nome") ?? "").trim().slice(0, 80)
const telefone = normalizarTelefone(String(formData.get("telefone") ?? ""))
const email = emailValido(String(formData.get("email") ?? ""))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Invalid e-mail is silently dropped instead of rejected.

emailValido returns null for a malformed e-mail, and there's no validation branch for it (unlike editarLeadAction, which explicitly returns "E-mail inválido."). A user who mistypes an e-mail while creating a contact gets no feedback — the field is just silently discarded.

🐛 Proposed fix
   const instanciaId = String(formData.get("instancia_id") ?? "").trim()
   const nome = String(formData.get("nome") ?? "").trim().slice(0, 80)
   const telefone = normalizarTelefone(String(formData.get("telefone") ?? ""))
-  const email = emailValido(String(formData.get("email") ?? ""))
+  const emailBruto = String(formData.get("email") ?? "").trim()
+  if (emailBruto && !emailValido(emailBruto)) {
+    return { ok: false, erro: "E-mail inválido." }
+  }
+  const email = emailBruto ? emailValido(emailBruto) : null
   if (!instanciaId) return { ok: false, erro: "Selecione o número/empresa." }

Also applies to: 164-166, 237-250

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crm-leads-actions.ts` at line 163, Update the create-contact validation
around emailValido and the related field-processing branches to reject malformed
e-mail values instead of silently omitting them. Match editarLeadAction’s
existing behavior by returning the established "E-mail inválido." error response
when validation returns null, while preserving normal processing for valid or
intentionally absent e-mail values.

@sejaanomalo-web
sejaanomalo-web merged commit cbb8ba3 into main Jul 12, 2026
3 checks passed
@sejaanomalo-web
sejaanomalo-web deleted the claude/crm-fase7-contatos-crud-whatsapp-sync branch July 12, 2026 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant