export default defineComponent({ async run({ steps }) { const dashboard = steps.get_current_dashboard.$return_value; const slackEvent = steps.handle_slack_event.$return_value || {}; const userId = slackEvent.user_id || slackEvent.raw_body?.event?.user || slackEvent.raw_body?.user || slackEvent.raw_body?.authorizations?.[0]?.user_id; if (!dashboard || Object.keys(dashboard).length === 0) { throw new Error("No dashboard data found in Data Store."); } if (!userId) { throw new Error("No Slack user ID found in the event payload."); } const payments = Array.isArray(dashboard.payments) ? dashboard.payments : []; const formatCurrency = (value) => new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(Number(value || 0)); const parseMDYLocal = (value) => { if (!value) return null; const parts = String(value).split("/"); if (parts.length !== 3) return null; const [mm, dd, yyyy] = parts.map((p) => parseInt(p, 10)); if (!mm || !dd || !yyyy) return null; return new Date(yyyy, mm - 1, dd); }; const formatDateKey = (date) => { const y = date.getFullYear(); const m = String(date.getMonth() + 1).padStart(2, "0"); const d = String(date.getDate()).padStart(2, "0"); return `${y}-${m}-${d}`; }; const formatDisplayDate = (dateKey) => { const [y, m, d] = dateKey.split("-").map(Number); const dt = new Date(y, m - 1, d); return dt.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric", }); }; const today = new Date(); today.setHours(0, 0, 0, 0); const futurePayments = payments.filter((p) => { const dt = parseMDYLocal(p.send_payment_on); return dt && dt >= today && (p.method === "ACH" || p.method === "Check"); }); const groupByMethodAndDay = (methodName) => { const grouped = {}; for (const payment of futurePayments) { if (payment.method !== methodName) continue; const dt = parseMDYLocal(payment.send_payment_on); if (!dt) continue; const key = formatDateKey(dt); if (!grouped[key]) { grouped[key] = { count: 0, total: 0, }; } grouped[key].count += 1; grouped[key].total += payment.amount_usd; } return Object.keys(grouped) .sort() .map((dateKey) => ({ date_key: dateKey, display_date: formatDisplayDate(dateKey), count: grouped[dateKey].count, total: Number(grouped[dateKey].total.toFixed(2)), })); }; const achDays = groupByMethodAndDay("ACH"); const checkDays = groupByMethodAndDay("Check"); const buildDayBlocks = (title, days) => { const total = days.reduce((sum, d) => sum + d.total, 0); const count = days.reduce((sum, d) => sum + d.count, 0); const blocks = [ { type: "section", text: { type: "mrkdwn", text: `*${title}*\n${count} payments • ${formatCurrency(total)}`, }, }, { type: "divider", }, ]; if (!days.length) { blocks.push({ type: "section", text: { type: "mrkdwn", text: "_No upcoming payments found._", }, }); blocks.push({ type: "divider" }); return blocks; } for (const day of days) { blocks.push({ type: "section", fields: [ { type: "mrkdwn", text: `*${day.display_date}*`, }, { type: "mrkdwn", text: `${day.count} payments • ${formatCurrency(day.total)}`, }, ], }); } blocks.push({ type: "divider" }); return blocks; }; return { user_id: userId, view: { type: "home", blocks: [ { type: "header", text: { type: "plain_text", text: "Payments Dashboard", }, }, { type: "context", elements: [ { type: "mrkdwn", text: `*Last updated:* ${dashboard.last_updated || "N/A"}`, }, { type: "mrkdwn", text: `*Source:* ${dashboard.file_name || "N/A"}`, }, ], }, { type: "divider", }, ...buildDayBlocks("ACH", achDays), ...buildDayBlocks("Check", checkDays), ], }, }; }, });