Wire plan_ready, list_ready, new_suggestions and Send to Google Chat
Connect the features to the notification outbox so real events notify,
each stored by the new notify.EmitTx in the transaction of the change it
is about (Wake after commit):
- plan_ready:{weekStart} (/plan) when a draft job stores the AI's draft;
a redraft shares the key and a manual swap never emits. The title reads
"Next week's plan is ready" for the coming week.
- list_ready:{weekStart} (/plan/shopping) when a week's Supply run is
first created, by generate or after Push to Calendar.
- new_suggestions:{batchId} (/discover) when a scheduled or manual batch
ends done with cards; replenish batches never notify.
- plan.Service.EmitDayOfReminder(date) emits day_of_reminder:{date} for
the Feature 20 scheduler.
Add POST /api/v1/shopping/{weekStart}/send-chat: shopping.SendToChat posts
the dinners with needed items and a link through chat.PostSupplyRun,
records sent_to_chat_at, and is idempotent per week unless
{"resend": true}. The Supply run's Send to Google Chat button calls it
("Send again" once sent) and shows failures in an ErrorBanner; the unused
Unavailable placeholder is removed.
Task: f0cd9d50-9c11-41be-a945-7bae9f5f28a4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -143,6 +143,8 @@ type Querier interface {
|
||||
GetShoppingList(ctx context.Context, weekStart string) (ShoppingList, error)
|
||||
// A suggestion with its dinner. Returns sql.ErrNoRows for an unknown id.
|
||||
GetSuggestion(ctx context.Context, id string) (GetSuggestionRow, error)
|
||||
// Batch id. Returns sql.ErrNoRows when there is none.
|
||||
GetSuggestionBatch(ctx context.Context, id string) (SuggestionBatch, error)
|
||||
// The id of the job that runs batch batch_id.
|
||||
GetSuggestionBatchJobID(ctx context.Context, arg GetSuggestionBatchJobIDParams) (string, error)
|
||||
// The plan of the week starting week_start. Returns sql.ErrNoRows when
|
||||
@@ -233,6 +235,9 @@ type Querier interface {
|
||||
// Records a delivered notification; updated_at is when it was sent. The
|
||||
// lease must still be the claim's.
|
||||
MarkNotificationSent(ctx context.Context, arg MarkNotificationSentParams) (int64, error)
|
||||
// Records list id sent to Google Chat at sent_to_chat_at, unless it is
|
||||
// frozen into the Supply archive.
|
||||
MarkShoppingListSentToChat(ctx context.Context, arg MarkShoppingListSentToChatParams) (int64, error)
|
||||
// Records an edit of plan id, raising its revision: a synced plan becomes
|
||||
// changed, any other keeps its status.
|
||||
MarkWeekPlanEdited(ctx context.Context, arg MarkWeekPlanEditedParams) error
|
||||
|
||||
@@ -362,6 +362,28 @@ func (q *Queries) ListShoppingListsThrough(ctx context.Context, weekStart string
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const markShoppingListSentToChat = `-- name: MarkShoppingListSentToChat :execrows
|
||||
UPDATE shopping_lists
|
||||
SET sent_to_chat_at = ?, updated_at = ?
|
||||
WHERE id = ? AND status = 'current' AND frozen_at IS NULL
|
||||
`
|
||||
|
||||
type MarkShoppingListSentToChatParams struct {
|
||||
SentToChatAt sql.NullString
|
||||
UpdatedAt string
|
||||
ID string
|
||||
}
|
||||
|
||||
// Records list id sent to Google Chat at sent_to_chat_at, unless it is
|
||||
// frozen into the Supply archive.
|
||||
func (q *Queries) MarkShoppingListSentToChat(ctx context.Context, arg MarkShoppingListSentToChatParams) (int64, error) {
|
||||
result, err := q.db.ExecContext(ctx, markShoppingListSentToChat, arg.SentToChatAt, arg.UpdatedAt, arg.ID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
||||
const moveShoppingGroup = `-- name: MoveShoppingGroup :exec
|
||||
UPDATE shopping_groups
|
||||
SET position = ?, updated_at = ?
|
||||
|
||||
@@ -259,6 +259,26 @@ func (q *Queries) GetSuggestion(ctx context.Context, id string) (GetSuggestionRo
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getSuggestionBatch = `-- name: GetSuggestionBatch :one
|
||||
SELECT id, "trigger", status, ai_run_id, created_at, updated_at FROM suggestion_batches
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
// Batch id. Returns sql.ErrNoRows when there is none.
|
||||
func (q *Queries) GetSuggestionBatch(ctx context.Context, id string) (SuggestionBatch, error) {
|
||||
row := q.db.QueryRowContext(ctx, getSuggestionBatch, id)
|
||||
var i SuggestionBatch
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Trigger,
|
||||
&i.Status,
|
||||
&i.AiRunID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getSuggestionBatchJobID = `-- name: GetSuggestionBatchJobID :one
|
||||
SELECT id FROM jobs
|
||||
WHERE kind = ?1
|
||||
|
||||
Reference in New Issue
Block a user