From 614878f217a95f1471bbbd97d72778c890b6dfa0 Mon Sep 17 00:00:00 2001 From: JOY <5027251+JOY@users.noreply.github.com> Date: Tue, 3 Nov 2026 22:16:39 +0700 Subject: [PATCH 1/2] Editor account section and submit guard for back campaigns (#85) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Back campaigns now need a usable receiving account before review, approval and live (DOS/DOS.Me#1549), because their backers pay into it once the campaign succeeds. The editor shows the receiving account section and blocks "Gửi duyệt" for back campaigns as for give ones, and submitForReview checks the account for both types. Back campaigns have no account swap, so a blocked back campaign whose account already recorded money is told to contact Give instead; swap requests are read for give campaigns only. A back campaign sent back for changes may release an account that saw no transaction, matching the database. Wording no longer limits these rules to charity campaigns, and the moderators' waiting queue shows each campaign's type. Refs #85 Co-Authored-By: Claude Opus 5.5 --- apps/web/src/app/admin/page.tsx | 9 ++- apps/web/src/app/me/accounts/actions.test.ts | 7 +- apps/web/src/app/me/accounts/page.tsx | 8 +- apps/web/src/app/me/campaigns/[id]/page.tsx | 78 +++++++++++-------- apps/web/src/app/me/campaigns/actions.test.ts | 23 +++++- apps/web/src/app/me/campaigns/actions.ts | 18 ++--- apps/web/src/lib/campaign-account.test.ts | 45 +++++++++-- apps/web/src/lib/campaign-account.ts | 64 +++++++++------ apps/web/src/lib/campaigns.ts | 2 +- apps/web/src/lib/errors.ts | 11 +-- apps/web/src/lib/receiving-accounts.test.ts | 2 +- apps/web/src/lib/receiving-accounts.ts | 12 +-- 12 files changed, 184 insertions(+), 95 deletions(-) diff --git a/apps/web/src/app/admin/page.tsx b/apps/web/src/app/admin/page.tsx index 52106a4d..1da16bf7 100644 --- a/apps/web/src/app/admin/page.tsx +++ b/apps/web/src/app/admin/page.tsx @@ -181,9 +181,9 @@ export default async function AdminPage() { Chiến dịch đã duyệt chờ tài khoản nhận{waiting ? ` (${waiting.length})` : ""}

- Chiến dịch từ thiện đã duyệt nhưng tài khoản nhận tiền không còn dùng được, nên hệ thống không tự mở. Chiến - dịch tự mở nếu tài khoản dùng được trở lại; nếu không, hãy yêu cầu chỉnh sửa để người tổ chức nhả tài khoản - cũ, gắn tài khoản khác rồi gửi duyệt lại. + Chiến dịch (từ thiện hoặc dự án có quà tặng) đã duyệt nhưng tài khoản nhận tiền không còn dùng được, nên hệ + thống không tự mở. Chiến dịch tự mở nếu tài khoản dùng được trở lại; nếu không, hãy yêu cầu chỉnh sửa để người + tổ chức nhả tài khoản cũ, gắn tài khoản khác rồi gửi duyệt lại.

{waiting === null ? (

Chưa tải được danh sách này. Hãy tải lại trang sau.

@@ -201,7 +201,8 @@ export default async function AdminPage() {
  • -

    {campaign.title}

    + +

    {campaign.title}

    {accountProblemLabel(campaign.account_problem)}

    diff --git a/apps/web/src/app/me/accounts/actions.test.ts b/apps/web/src/app/me/accounts/actions.test.ts index eb0fc1aa..f94151ea 100644 --- a/apps/web/src/app/me/accounts/actions.test.ts +++ b/apps/web/src/app/me/accounts/actions.test.ts @@ -306,13 +306,14 @@ describe("releaseAccount", () => { expect(result?.message).toContain("Giao dịch đã vào tài khoản là lịch sử tiền") }) - it("does not ask for a back project sent back for changes", async () => { + it("releases the account of a back project sent back for changes too (DOS/Give#85)", async () => { state.userHandler = bindReads({ acc: account({ bindings: [bound] }), camp: campaign({ status: "changes_requested", type: "back" }), }) - expect((await releaseAccount(ACCOUNT, null, new FormData()))?.ok).toBe(false) - expect(state.serviceCalls).toHaveLength(0) + state.serviceHandler = () => ({ data: "b" }) + expect((await releaseAccount(ACCOUNT, null, new FormData()))?.ok).toBe(true) + expect(state.serviceCalls.some((c) => c.target === "rpc:release_account_binding")).toBe(true) }) it("does not ask while the campaign is in review or live", async () => { diff --git a/apps/web/src/app/me/accounts/page.tsx b/apps/web/src/app/me/accounts/page.tsx index 6dee2225..cc8b8b28 100644 --- a/apps/web/src/app/me/accounts/page.tsx +++ b/apps/web/src/app/me/accounts/page.tsx @@ -268,9 +268,9 @@ function AccountItem({ {boundTitle(holder.campaign_id)} {" "} - từ ngày {formatDate(holder.bound_at)}. Khi chiến dịch còn là bản nháp, hoặc là chiến dịch từ thiện được yêu - cầu chỉnh sửa mà tài khoản chưa nhận giao dịch nào, bạn có thể nhả tài khoản để đổi tài khoản khác; ngoài ra, - sau khi gửi duyệt, tài khoản chỉ được nhả khi chiến dịch đó đã xong hẳn phần tiền. + từ ngày {formatDate(holder.bound_at)}. Khi chiến dịch còn là bản nháp, hoặc được yêu cầu chỉnh sửa mà tài + khoản chưa nhận giao dịch nào, bạn có thể nhả tài khoản để đổi tài khoản khác; ngoài ra, sau khi gửi duyệt, + tài khoản chỉ được nhả khi chiến dịch đó đã xong hẳn phần tiền. {(account.verification_status === "disabled" || unusable) && ( {" "} @@ -301,7 +301,7 @@ function AccountItem({ ) : (