feat(v2)(ui): operator-side LP UI matches the new dca_lp authority split
Operator no longer chooses the LP's wallet / DCA mode / autoforward — those belong to the LP, written via satmachineclient. The Add LP / Edit LP dialogs reduce to (machine, user_id, optional username, status). The clients table loses the wallet / mode / autoforward columns and gains an "Onboarded" column showing whether the LP has a `dca_lp` row yet (server-side LEFT JOIN; `DcaClient.lp_onboarded`). Deposit creation gate (the structural enforcement of "must onboard first"): - Picker annotates each LP option with "— pending onboarding" and disables un-onboarded LP rows. - Selecting an un-onboarded LP shows an inline deep-orange banner explaining the LP needs to open satmachineclient once. - The Record button is `:disable`d in that state. The backend refuses with HTTP 422 anyway (see previous commit) — UI is just the first line of feedback. Backend wiring: - `DcaClient` model gains `lp_onboarded: bool = False`, populated at SELECT time via a shared `_CLIENT_SELECT` / `_CLIENT_FROM` fragment that LEFT JOINs `dca_lp` on `user_id`. All four list/ single-row read paths use it: by-id, by-(machine,user), by-machine, by-operator, by-user. No extra round-trip per row. - CSV export drops the removed columns; adds `lp_onboarded`. All 86 unit tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
80b5a6d785
commit
cfad4e341c
4 changed files with 93 additions and 103 deletions
44
crud.py
44
crud.py
|
|
@ -201,9 +201,23 @@ async def create_dca_client(data: CreateDcaClientData) -> DcaClient:
|
|||
return client
|
||||
|
||||
|
||||
# Shared SELECT fragment: client columns plus the LP-onboarded flag
|
||||
# computed via LEFT JOIN on dca_lp. Returned as `lp_onboarded` (boolean
|
||||
# 0/1 in SQLite, which Pydantic coerces to bool on the DcaClient model).
|
||||
_CLIENT_SELECT = """
|
||||
c.id, c.machine_id, c.user_id, c.username, c.status,
|
||||
c.created_at, c.updated_at,
|
||||
(lp.user_id IS NOT NULL) AS lp_onboarded
|
||||
"""
|
||||
_CLIENT_FROM = (
|
||||
"satoshimachine.dca_clients c "
|
||||
"LEFT JOIN satoshimachine.dca_lp lp ON lp.user_id = c.user_id"
|
||||
)
|
||||
|
||||
|
||||
async def get_dca_client(client_id: str) -> Optional[DcaClient]:
|
||||
return await db.fetchone(
|
||||
"SELECT * FROM satoshimachine.dca_clients WHERE id = :id",
|
||||
f"SELECT {_CLIENT_SELECT} FROM {_CLIENT_FROM} WHERE c.id = :id",
|
||||
{"id": client_id},
|
||||
DcaClient,
|
||||
)
|
||||
|
|
@ -213,9 +227,9 @@ async def get_dca_client_for_machine_user(
|
|||
machine_id: str, user_id: str
|
||||
) -> Optional[DcaClient]:
|
||||
return await db.fetchone(
|
||||
"""
|
||||
SELECT * FROM satoshimachine.dca_clients
|
||||
WHERE machine_id = :machine_id AND user_id = :user_id
|
||||
f"""
|
||||
SELECT {_CLIENT_SELECT} FROM {_CLIENT_FROM}
|
||||
WHERE c.machine_id = :machine_id AND c.user_id = :user_id
|
||||
""",
|
||||
{"machine_id": machine_id, "user_id": user_id},
|
||||
DcaClient,
|
||||
|
|
@ -224,10 +238,10 @@ async def get_dca_client_for_machine_user(
|
|||
|
||||
async def get_dca_clients_for_machine(machine_id: str) -> List[DcaClient]:
|
||||
return await db.fetchall(
|
||||
"""
|
||||
SELECT * FROM satoshimachine.dca_clients
|
||||
WHERE machine_id = :machine_id
|
||||
ORDER BY created_at DESC
|
||||
f"""
|
||||
SELECT {_CLIENT_SELECT} FROM {_CLIENT_FROM}
|
||||
WHERE c.machine_id = :machine_id
|
||||
ORDER BY c.created_at DESC
|
||||
""",
|
||||
{"machine_id": machine_id},
|
||||
DcaClient,
|
||||
|
|
@ -237,9 +251,9 @@ async def get_dca_clients_for_machine(machine_id: str) -> List[DcaClient]:
|
|||
async def get_dca_clients_for_operator(operator_user_id: str) -> List[DcaClient]:
|
||||
"""All clients across every machine this operator owns."""
|
||||
return await db.fetchall(
|
||||
"""
|
||||
SELECT c.*
|
||||
FROM satoshimachine.dca_clients c
|
||||
f"""
|
||||
SELECT {_CLIENT_SELECT}
|
||||
FROM {_CLIENT_FROM}
|
||||
JOIN satoshimachine.dca_machines m ON m.id = c.machine_id
|
||||
WHERE m.operator_user_id = :uid
|
||||
ORDER BY c.created_at DESC
|
||||
|
|
@ -252,10 +266,10 @@ async def get_dca_clients_for_operator(operator_user_id: str) -> List[DcaClient]
|
|||
async def get_dca_clients_for_user(user_id: str) -> List[DcaClient]:
|
||||
"""LP cross-operator view — every machine this LP is registered at."""
|
||||
return await db.fetchall(
|
||||
"""
|
||||
SELECT * FROM satoshimachine.dca_clients
|
||||
WHERE user_id = :user_id
|
||||
ORDER BY created_at DESC
|
||||
f"""
|
||||
SELECT {_CLIENT_SELECT} FROM {_CLIENT_FROM}
|
||||
WHERE c.user_id = :user_id
|
||||
ORDER BY c.created_at DESC
|
||||
""",
|
||||
{"user_id": user_id},
|
||||
DcaClient,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue