{"app":{"id":29,"summary":"Financial Recovery Dashboard","versions":[8867],"created_by":"tristan795","created_at":"2026-03-30T17:21:48.636Z","votes":0,"approved":true,"apps":[],"app_type":"raw","external_embed_url":"","value":{"runnables":{"a":{"name":"a","fields":{},"type":"inline","inlineScript":{"content":"// import * as wmill from \"windmill-client\"\n\nexport async function main(x: string) {\n  return x\n}\n","language":"bun","schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"x":{"default":null,"description":"","originalType":"string","type":"string"}},"required":["x"],"type":"object"}}}},"files":{"/index.tsx":"\nimport React from 'react'\n\nimport { createRoot } from 'react-dom/client'\nimport App from './App'\n\nconst root = createRoot(document.getElementById('root')!);\nroot.render(<App/>);\n","/App.tsx":"import React, { useState } from 'react'\nimport './index.css'\n\n// Mock data for high-value failures\nconst mockFailures = [\n  {\n    id: 1,\n    customer: \"Acme Corporation\",\n    amount: 24500,\n    date: \"2024-01-15\",\n    status: \"escalated\",\n    owner: \"Sarah Chen\"\n  },\n  {\n    id: 2,\n    customer: \"TechStart Industries\",\n    amount: 18750,\n    date: \"2024-01-14\",\n    status: \"sent\",\n    owner: \"Michael Torres\"\n  },\n  {\n    id: 3,\n    customer: \"Global Dynamics LLC\",\n    amount: 15200,\n    date: \"2024-01-14\",\n    status: \"escalated\",\n    owner: \"Sarah Chen\"\n  },\n  {\n    id: 4,\n    customer: \"Innovate Solutions\",\n    amount: 12800,\n    date: \"2024-01-13\",\n    status: \"pending\",\n    owner: \"James Wilson\"\n  },\n  {\n    id: 5,\n    customer: \"Quantum Systems\",\n    amount: 11500,\n    date: \"2024-01-13\",\n    status: \"sent\",\n    owner: \"Michael Torres\"\n  },\n  {\n    id: 6,\n    customer: \"Nexus Enterprises\",\n    amount: 9800,\n    date: \"2024-01-12\",\n    status: \"resolved\",\n    owner: \"Sarah Chen\"\n  },\n  {\n    id: 7,\n    customer: \"Vertex Technologies\",\n    amount: 8900,\n    date: \"2024-01-12\",\n    status: \"escalated\",\n    owner: \"James Wilson\"\n  },\n  {\n    id: 8,\n    customer: \"Pinnacle Group\",\n    amount: 7650,\n    date: \"2024-01-11\",\n    status: \"sent\",\n    owner: \"Michael Torres\"\n  },\n  {\n    id: 9,\n    customer: \"Horizon Ventures\",\n    amount: 6200,\n    date: \"2024-01-11\",\n    status: \"pending\",\n    owner: \"Sarah Chen\"\n  },\n  {\n    id: 10,\n    customer: \"Catalyst Partners\",\n    amount: 5400,\n    date: \"2024-01-10\",\n    status: \"escalated\",\n    owner: \"James Wilson\"\n  }\n]\n\n// Mock activity feed data\nconst mockActivities = [\n  {\n    id: 1,\n    type: \"success\",\n    text: \"Payment retry successful for Acme Corporation\",\n    time: \"2 minutes ago\",\n    icon: \"✓\"\n  },\n  {\n    id: 2,\n    type: \"warning\",\n    text: \"Escalation created for Global Dynamics LLC\",\n    time: \"15 minutes ago\",\n    icon: \"⚠\"\n  },\n  {\n    id: 3,\n    type: \"info\",\n    text: \"SMS sent to TechStart Industries billing contact\",\n    time: \"32 minutes ago\",\n    icon: \"✉\"\n  },\n  {\n    id: 4,\n    type: \"success\",\n    text: \"Payment received from Nexus Enterprises\",\n    time: \"1 hour ago\",\n    icon: \"✓\"\n  },\n  {\n    id: 5,\n    type: \"info\",\n    text: \"Automated follow-up email sent to Quantum Systems\",\n    time: \"2 hours ago\",\n    icon: \"✉\"\n  },\n  {\n    id: 6,\n    type: \"warning\",\n    text: \"Payment method expired for Vertex Technologies\",\n    time: \"3 hours ago\",\n    icon: \"⚠\"\n  },\n  {\n    id: 7,\n    type: \"success\",\n    text: \"Dunning workflow completed for Pinnacle Group\",\n    time: \"4 hours ago\",\n    icon: \"✓\"\n  }\n]\n\nconst generateDraftEmail = (failure: typeof mockFailures[0]) => {\n  const firstName = failure.customer.split(' ')[0]\n  const amount = new Intl.NumberFormat('en-US', {\n    style: 'currency',\n    currency: 'USD',\n    minimumFractionDigits: 0,\n    maximumFractionDigits: 0\n  }).format(failure.amount)\n\n  return {\n    to: `billing@${failure.customer.toLowerCase().replace(/\\s+/g, '')}.com`,\n    subject: `Action Required: Outstanding Payment of ${amount}`,\n    body: `Dear ${firstName} Billing Team,\n\nI hope this message finds you well. I'm reaching out regarding an outstanding payment of ${amount} that was due on ${new Date(failure.date).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })}.\n\nOur records indicate that the most recent payment attempt was unsuccessful. We understand that payment issues can arise for a variety of reasons, and we'd like to help resolve this as quickly as possible.\n\nCould you please:\n1. Verify the payment method on file is up to date\n2. Confirm whether there were any issues processing the payment\n3. Let us know if you'd like to arrange an alternative payment schedule\n\nWe value our partnership with ${failure.customer} and want to ensure uninterrupted service. Please don't hesitate to reach out if you have any questions.\n\nBest regards,\n${failure.owner}\nRecovery Team`\n  }\n}\n\nconst App = () => {\n  const [searchQuery, setSearchQuery] = useState('')\n  const [selectedFailure, setSelectedFailure] = useState<typeof mockFailures[0] | null>(null)\n  const [sent, setSent] = useState(false)\n\n  // Filter failures based on search query\n  const filteredFailures = mockFailures.filter(failure =>\n    failure.customer.toLowerCase().includes(searchQuery.toLowerCase()) ||\n    failure.owner.toLowerCase().includes(searchQuery.toLowerCase())\n  )\n\n  const draftEmail = selectedFailure ? generateDraftEmail(selectedFailure) : null\n\n  const getStatusLabel = (status: string) => {\n    switch (status) {\n      case 'escalated':\n        return 'Escalated'\n      case 'sent':\n        return 'SMS Sent'\n      case 'pending':\n        return 'Pending'\n      case 'resolved':\n        return 'Resolved'\n      default:\n        return status\n    }\n  }\n\n  const formatCurrency = (amount: number) => {\n    return new Intl.NumberFormat('en-US', {\n      style: 'currency',\n      currency: 'USD',\n      minimumFractionDigits: 0,\n      maximumFractionDigits: 0\n    }).format(amount)\n  }\n\n  const formatDate = (dateString: string) => {\n    const date = new Date(dateString)\n    return new Intl.DateTimeFormat('en-US', {\n      month: 'short',\n      day: 'numeric',\n      year: 'numeric'\n    }).format(date)\n  }\n\n  return (\n    <div className=\"dashboard\">\n      <main className=\"main-content\">\n        <header className=\"header\">\n          <h1 className=\"header-title\">Financial Recovery Dashboard</h1>\n          <p className=\"header-subtitle\">Real-time payment recovery monitoring and management</p>\n        </header>\n\n        <div className=\"content\">\n          <div className=\"content-main\">\n            {/* Metrics Row */}\n            <div className=\"metrics-row\">\n              <div className=\"metric-card\">\n                <div className=\"metric-info\">\n                  <div className=\"metric-label\">Total Recovered</div>\n                  <div className=\"metric-change positive\">↑ 12% from last month</div>\n                </div>\n                <div className=\"metric-value-container\">\n                  <div className=\"metric-value\">$124k</div>\n                </div>\n              </div>\n              <div className=\"metric-card\">\n                <div className=\"metric-info\">\n                  <div className=\"metric-label\">Recovery Rate</div>\n                  <div className=\"metric-change positive\">↑ 5% from last month</div>\n                </div>\n                <div className=\"metric-value-container\">\n                  <div className=\"metric-value\">68%</div>\n                </div>\n              </div>\n              <div className=\"metric-card\">\n                <div className=\"metric-info\">\n                  <div className=\"metric-label\">Active Escalations</div>\n                  <div className=\"metric-change negative\">↑ 3 from yesterday</div>\n                </div>\n                <div className=\"metric-value-container\">\n                  <div className=\"metric-value\">12</div>\n                </div>\n              </div>\n            </div>\n\n            {/* Data Table */}\n            <div className=\"table-section\">\n              <div className=\"table-header\">\n                <h2 className=\"table-title\">High-Value Failures</h2>\n                <div className=\"search-box\">\n                  <svg className=\"search-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                    <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n                  </svg>\n                  <input\n                    type=\"text\"\n                    className=\"search-input\"\n                    placeholder=\"Search customers or owners...\"\n                    value={searchQuery}\n                    onChange={(e) => setSearchQuery(e.target.value)}\n                  />\n                </div>\n              </div>\n              <div className=\"table-container\">\n                <table className=\"data-table\">\n                  <thead>\n                    <tr>\n                      <th>Customer Name</th>\n                      <th>Amount</th>\n                      <th>Date</th>\n                      <th>Status</th>\n                      <th>Account Owner</th>\n                      <th></th>\n                    </tr>\n                  </thead>\n                  <tbody>\n                    {filteredFailures.map((failure) => (\n                      <tr key={failure.id}>\n                        <td className=\"customer-name\">{failure.customer}</td>\n                        <td className=\"amount\">{formatCurrency(failure.amount)}</td>\n                        <td className=\"date\">{formatDate(failure.date)}</td>\n                        <td>\n                          <span className={`status-badge status-${failure.status}`}>\n                            {getStatusLabel(failure.status)}\n                          </span>\n                        </td>\n                        <td className=\"owner\">{failure.owner}</td>\n                        <td><button className=\"contact-btn\" onClick={() => setSelectedFailure(failure)}>Contact</button></td>\n                      </tr>\n                    ))}\n                  </tbody>\n                </table>\n              </div>\n            </div>\n          </div>\n\n          {/* Activity Feed Sidebar */}\n          <aside className=\"activity-feed\">\n            <h3 className=\"activity-title\">Live Activity</h3>\n            <div className=\"activity-list\">\n              {mockActivities.slice(0, 7).map((activity) => (\n                <div key={activity.id} className=\"activity-item\">\n                  <div className={`activity-dot ${activity.type}`} />\n                  <div className=\"activity-content\">\n                    <div className=\"activity-text\">{activity.text}</div>\n                    <div className=\"activity-time\">{activity.time}</div>\n                  </div>\n                </div>\n              ))}\n            </div>\n          </aside>\n        </div>\n      </main>\n\n      {selectedFailure && draftEmail && (\n        <div className=\"modal-overlay\" onClick={() => { setSelectedFailure(null); setSent(false) }}>\n          <div className=\"modal\" onClick={(e) => e.stopPropagation()}>\n            <div className=\"modal-header\">\n              <div>\n                <div className=\"modal-title\">Draft Email</div>\n                <div className=\"modal-badge\">AI Generated</div>\n              </div>\n              <button className=\"modal-close\" onClick={() => { setSelectedFailure(null); setSent(false) }}>×</button>\n            </div>\n            <div className=\"modal-body\">\n              <div className=\"email-field\">\n                <span className=\"email-label\">To</span>\n                <span className=\"email-value\">{draftEmail.to}</span>\n              </div>\n              <div className=\"email-field\">\n                <span className=\"email-label\">Subject</span>\n                <span className=\"email-value\">{draftEmail.subject}</span>\n              </div>\n              <div className=\"email-body\">{draftEmail.body}</div>\n            </div>\n            <div className=\"modal-footer\">\n              {sent ? (\n                <div className=\"send-confirmation\">Email sent successfully</div>\n              ) : (\n                <>\n                  <button className=\"btn-secondary\" onClick={() => setSelectedFailure(null)}>Cancel</button>\n                  <button className=\"btn-primary\" onClick={() => setSent(true)}>Send Email</button>\n                </>\n              )}\n            </div>\n          </div>\n        </div>\n      )}\n    </div>\n  )\n}\n\nexport default App\n","/index.css":"* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;\n  background: #ffffff;\n  color: #1a1a2e;\n  font-size: 14px;\n  line-height: 1.5;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n#root {\n  width: 100%;\n  min-height: 100vh;\n}\n\n/* Dashboard Layout */\n.dashboard {\n  display: flex;\n  min-height: 100vh;\n}\n\n/* Main Content */\n.main-content {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  overflow-x: hidden;\n  min-width: 0;\n}\n\n.header {\n  padding: 32px 48px 28px;\n  background: #ffffff;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.06);\n}\n\n.header-title {\n  font-size: 26px;\n  font-weight: 700;\n  letter-spacing: -0.5px;\n  color: #1a1a2e;\n  margin-bottom: 4px;\n}\n\n.header-subtitle {\n  font-size: 14px;\n  color: #8b8fa3;\n  font-weight: 400;\n}\n\n.content {\n  flex: 1;\n  padding: 32px 48px;\n  background: #ffffff;\n  display: flex;\n  gap: 32px;\n}\n\n.content-main {\n  flex: 1;\n  min-width: 0;\n}\n\n/* Metrics Row */\n.metrics-row {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 20px;\n  margin-bottom: 32px;\n}\n\n.metric-card {\n  background: #ffffff;\n  border: 1px solid rgba(0, 0, 0, 0.06);\n  border-radius: 14px;\n  padding: 22px 24px;\n  transition: all 0.25s ease;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\n.metric-card:hover {\n  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);\n  transform: translateY(-2px);\n}\n\n.metric-label {\n  font-size: 12px;\n  color: #8b8fa3;\n  font-weight: 500;\n  text-transform: uppercase;\n  letter-spacing: 0.6px;\n}\n\n.metric-value {\n  font-size: 34px;\n  font-weight: 700;\n  color: #1a1a2e;\n  letter-spacing: -1.5px;\n  line-height: 1;\n}\n\n.metric-change {\n  font-size: 12px;\n  color: #8b8fa3;\n  margin-top: 6px;\n  font-weight: 500;\n  display: flex;\n  align-items: center;\n  gap: 4px;\n}\n\n.metric-change.positive {\n  color: #0d9668;\n}\n\n.metric-change.negative {\n  color: #e5484d;\n}\n\n.metric-info {\n  flex: 1;\n}\n\n.metric-value-container {\n  text-align: right;\n}\n\n/* Data Table Section */\n.table-section {\n  background: #ffffff;\n  border: 1px solid rgba(0, 0, 0, 0.06);\n  border-radius: 14px;\n  overflow: hidden;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\n.table-header {\n  padding: 20px 24px;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.06);\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.table-title {\n  font-size: 16px;\n  font-weight: 600;\n  color: #1a1a2e;\n  letter-spacing: -0.3px;\n}\n\n.search-box {\n  position: relative;\n}\n\n.search-input {\n  padding: 9px 16px 9px 38px;\n  border: 1px solid rgba(0, 0, 0, 0.1);\n  border-radius: 10px;\n  font-size: 13px;\n  font-family: inherit;\n  width: 280px;\n  outline: none;\n  transition: all 0.2s ease;\n  background: #f8f9fa;\n  color: #1a1a2e;\n}\n\n.search-input::placeholder {\n  color: #a0a4b8;\n}\n\n.search-input:focus {\n  border-color: #6c63ff;\n  background: #ffffff;\n  box-shadow: 0 0 0 3px rgba(108, 99, 255, 0.1);\n}\n\n.search-icon {\n  position: absolute;\n  left: 12px;\n  top: 50%;\n  transform: translateY(-50%);\n  width: 16px;\n  height: 16px;\n  opacity: 0.35;\n}\n\n.table-container {\n  overflow-x: auto;\n}\n\n.data-table {\n  width: 100%;\n  border-collapse: collapse;\n}\n\n.data-table thead {\n  background: #f8f9fa;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.06);\n}\n\n.data-table th {\n  padding: 12px 24px;\n  text-align: left;\n  font-size: 11px;\n  font-weight: 600;\n  color: #8b8fa3;\n  text-transform: uppercase;\n  letter-spacing: 0.6px;\n}\n\n.data-table td {\n  padding: 16px 24px;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.04);\n  font-size: 14px;\n  color: #1a1a2e;\n}\n\n.data-table tbody tr {\n  transition: background 0.15s ease;\n}\n\n.data-table tbody tr:hover {\n  background: #f8f9fb;\n}\n\n.data-table tbody tr:last-child td {\n  border-bottom: none;\n}\n\n.customer-name {\n  font-weight: 600;\n  color: #1a1a2e;\n}\n\n.amount {\n  font-weight: 600;\n  font-variant-numeric: tabular-nums;\n}\n\n.date {\n  color: #8b8fa3;\n  font-variant-numeric: tabular-nums;\n}\n\n.status-badge {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 13px;\n  font-weight: 450;\n  color: #1a1a2e;\n}\n\n.status-badge::before {\n  content: '';\n  display: inline-block;\n  width: 6px;\n  height: 6px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n\n.status-escalated::before {\n  background: #f59e0b;\n}\n\n.status-sent::before {\n  background: #3b82f6;\n}\n\n.status-pending::before {\n  background: #a0a4b8;\n}\n\n.status-resolved::before {\n  background: #10b981;\n}\n\n.owner {\n  color: #8b8fa3;\n  font-size: 13px;\n}\n\n.contact-btn {\n  padding: 6px 16px;\n  font-size: 13px;\n  font-family: inherit;\n  font-weight: 500;\n  color: #1a1a2e;\n  background: transparent;\n  border: 1px solid rgba(0, 0, 0, 0.12);\n  border-radius: 8px;\n  cursor: pointer;\n  transition: all 0.15s ease;\n  white-space: nowrap;\n}\n\n.contact-btn:hover {\n  background: #1a1a2e;\n  color: #ffffff;\n  border-color: #1a1a2e;\n}\n\n/* Activity Feed Sidebar */\n.activity-feed {\n  width: 340px;\n  background: #ffffff;\n  border: 1px solid rgba(0, 0, 0, 0.06);\n  border-radius: 14px;\n  padding: 24px;\n  height: fit-content;\n  position: sticky;\n  top: 32px;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\n.activity-title {\n  font-size: 13px;\n  font-weight: 600;\n  color: #8b8fa3;\n  margin-bottom: 20px;\n  text-transform: uppercase;\n  letter-spacing: 0.6px;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n\n.activity-title::before {\n  content: '';\n  display: inline-block;\n  width: 7px;\n  height: 7px;\n  background: #0d9668;\n  border-radius: 50%;\n  box-shadow: 0 0 8px rgba(13, 150, 104, 0.5);\n  animation: pulse 2s ease-in-out infinite;\n}\n\n@keyframes pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.4; }\n}\n\n.activity-list {\n  display: flex;\n  flex-direction: column;\n  gap: 0;\n}\n\n.activity-item {\n  display: flex;\n  gap: 12px;\n  padding: 14px 0;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n}\n\n.activity-item:first-child {\n  padding-top: 0;\n}\n\n.activity-item:last-child {\n  border-bottom: none;\n  padding-bottom: 0;\n}\n\n.activity-dot {\n  width: 7px;\n  height: 7px;\n  border-radius: 50%;\n  flex-shrink: 0;\n  margin-top: 6px;\n}\n\n.activity-dot.success {\n  background: #10b981;\n}\n\n.activity-dot.warning {\n  background: #f59e0b;\n}\n\n.activity-dot.info {\n  background: #3b82f6;\n}\n\n.activity-content {\n  flex: 1;\n  min-width: 0;\n}\n\n.activity-text {\n  font-size: 13px;\n  color: #1a1a2e;\n  line-height: 1.45;\n  margin-bottom: 3px;\n}\n\n.activity-time {\n  font-size: 11px;\n  color: #a0a4b8;\n  font-variant-numeric: tabular-nums;\n}\n\n/* Email Modal */\n.modal-overlay {\n  position: fixed;\n  inset: 0;\n  background: rgba(0, 0, 0, 0.4);\n  backdrop-filter: blur(4px);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  z-index: 100;\n  animation: fadeIn 0.15s ease;\n}\n\n@keyframes fadeIn {\n  from { opacity: 0; }\n  to { opacity: 1; }\n}\n\n.modal {\n  background: #ffffff;\n  border-radius: 16px;\n  width: 580px;\n  max-width: calc(100vw - 48px);\n  max-height: calc(100vh - 48px);\n  display: flex;\n  flex-direction: column;\n  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.12);\n  animation: slideUp 0.2s ease;\n}\n\n@keyframes slideUp {\n  from { opacity: 0; transform: translateY(12px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n\n.modal-header {\n  padding: 24px 28px 20px;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.06);\n  display: flex;\n  justify-content: space-between;\n  align-items: flex-start;\n}\n\n.modal-title {\n  font-size: 18px;\n  font-weight: 600;\n  color: #1a1a2e;\n  letter-spacing: -0.3px;\n}\n\n.modal-badge {\n  display: inline-block;\n  margin-top: 6px;\n  font-size: 11px;\n  font-weight: 500;\n  color: #6c63ff;\n  letter-spacing: 0.3px;\n}\n\n.modal-close {\n  width: 32px;\n  height: 32px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border: none;\n  background: transparent;\n  color: #8b8fa3;\n  font-size: 22px;\n  cursor: pointer;\n  border-radius: 8px;\n  transition: all 0.15s ease;\n  line-height: 1;\n}\n\n.modal-close:hover {\n  background: rgba(0, 0, 0, 0.05);\n  color: #1a1a2e;\n}\n\n.modal-body {\n  padding: 24px 28px;\n  overflow-y: auto;\n  flex: 1;\n}\n\n.email-field {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 10px 0;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n}\n\n.email-label {\n  font-size: 12px;\n  font-weight: 500;\n  color: #8b8fa3;\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n  width: 60px;\n  flex-shrink: 0;\n}\n\n.email-value {\n  font-size: 14px;\n  color: #1a1a2e;\n}\n\n.email-body {\n  margin-top: 20px;\n  font-size: 14px;\n  color: #3a3a4e;\n  line-height: 1.7;\n  white-space: pre-line;\n}\n\n.modal-footer {\n  padding: 20px 28px;\n  border-top: 1px solid rgba(0, 0, 0, 0.06);\n  display: flex;\n  justify-content: flex-end;\n  gap: 10px;\n}\n\n.btn-secondary {\n  padding: 9px 20px;\n  font-size: 14px;\n  font-family: inherit;\n  font-weight: 500;\n  color: #1a1a2e;\n  background: transparent;\n  border: 1px solid rgba(0, 0, 0, 0.12);\n  border-radius: 10px;\n  cursor: pointer;\n  transition: all 0.15s ease;\n}\n\n.btn-secondary:hover {\n  background: rgba(0, 0, 0, 0.04);\n}\n\n.btn-primary {\n  padding: 9px 24px;\n  font-size: 14px;\n  font-family: inherit;\n  font-weight: 500;\n  color: #ffffff;\n  background: #1a1a2e;\n  border: 1px solid #1a1a2e;\n  border-radius: 10px;\n  cursor: pointer;\n  transition: all 0.15s ease;\n}\n\n.btn-primary:hover {\n  background: #2a2a42;\n}\n\n.send-confirmation {\n  font-size: 14px;\n  font-weight: 500;\n  color: #0d9668;\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n.send-confirmation::before {\n  content: '✓';\n  font-size: 13px;\n}\n\n/* Responsive */\n@media (max-width: 1400px) {\n  .content {\n    flex-direction: column;\n  }\n\n  .activity-feed {\n    width: 100%;\n    position: static;\n  }\n}\n\n@media (max-width: 1024px) {\n  .metrics-row {\n    grid-template-columns: 1fr;\n  }\n\n  .content {\n    padding: 24px 32px;\n  }\n}\n\n@media (max-width: 768px) {\n  .content {\n    padding: 20px;\n  }\n\n  .header {\n    padding: 24px;\n  }\n\n  .search-input {\n    width: 200px;\n  }\n\n  .metric-card {\n    flex-direction: column;\n    align-items: flex-start;\n    gap: 8px;\n  }\n\n  .metric-value-container {\n    text-align: left;\n  }\n\n  .activity-card {\n    padding: 16px;\n  }\n\n  .activity-card-icon {\n    width: 40px;\n    height: 40px;\n    font-size: 18px;\n  }\n}\n","/package.json":"{\n    \"dependencies\": {\n        \"react\": \"19.0.0\",\n        \"react-dom\": \"19.0.0\",\n        \"windmill-client\": \"^1\"\n    },\n    \"devDependencies\": {\n        \"@types/react-dom\": \"^19.0.0\",\n        \"@types/react\": \"^19.0.0\"\n    }\n}"},"data":{"tables":[]}},"description":"Real-time payment recovery monitoring and management (mocked data for demo purpose)","vcreated_at":"2026-03-30T17:21:48.636Z","vcreated_by":"tristan795","comments":[]}}