# Breadboard — Mobile submit (modal + snackbar) + one-way email reply. # Follows the app's real convention: create surfaces are modals (like # AdminPostFormWidget.show → showDialog), and confirmation is a snackbar + pop # back — NOT a pushed route and NOT a success screen. That kills the dead-end: # the modal dismisses the user back to where they were. # Render: ../../../.claude/skills/breadboarding/render.sh luciq-flow.d2 vars: { d2-config: { layout-engine: dagre } } classes: { place: { style: { fill: "#ffedd5"; stroke: "#f97316"; font-color: "#9a3412"; bold: true } } affordance: { style: { fill: "#ffffff"; stroke: "#94a3b8"; font-color: "#334155"; font-size: 13 } } data: { shape: cylinder; style: { fill: "#eef2f6"; stroke: "#64748b"; font-color: "#334155"; font-size: 13 } } conditional: { shape: diamond; style: { fill: "#f3e8ff"; stroke: "#9333ea"; font-color: "#6b21a8"; font-size: 13 } } note: { style: { fill: "#fef9c3"; stroke: "#eab308"; font-color: "#713f12"; font-size: 12; border-radius: 10; stroke-dash: 2 } } } grid-columns: 2 grid-gap: 90 # ───────────────────────────────────────────────────────────── current: "① CURRENT — Luciq in-app loop" { style: { stroke: "#cbd5e1"; fill: "#ffffff" } trigger: "Profile dropdown ▸ 'Report a Bug'" { class: place } modal: "Luciq native modal" { class: place; grid-columns: 1 desc: "Description" { class: affordance } shot: "Auto screenshot + annotate" { class: affordance } send: "Send" { class: affordance } } trigger -> modal: "showLuciqReporter()" modal.send -> cloud: "to Luciq cloud" cloud: "Luciq cloud" { class: place } inbox: "Luciq in-app inbox (reply thread per user)" { class: place } cloud -> inbox: "admin replies threaded" note: "Full two-way thread — but it's the\npart we pay 599 USD/mo for, and it\nlives inside Luciq's SDK." { class: note } } # ───────────────────────────────────────────────────────────── proposed: "② PROPOSED — modal submit + one-way email" { style: { stroke: "#cbd5e1"; fill: "#ffffff" } direction: down menu: "Profile dropdown ▸ 'Report a Bug'\n(existing item — swap the action)" { class: place } modal: "Report modal (showModalBottomSheet,\nlike AdminPostFormWidget — NOT a route)" { class: place; grid-columns: 1; style.stroke-dash: 4 desc: "What went wrong (notes)" { class: affordance } shot: "Attach screenshot (optional)" { class: affordance } ctx: "Auto context (read-only): appVersion/build\n(PackageInfo) · device/os (DeviceInfo)\n· email + tribeUserId" { class: affordance } submit: "Submit" { class: affordance } } menu -> modal: "opens modal" modal.submit -> node: "POST /api/bug-report (own node)" node: "Origin node — create BugReports row" { class: place; style.stroke-dash: 4 } snack: "Snackbar: 'Thanks — we'll reply by email'\n+ pop back to prior screen (no dead-end)" { class: note } modal.submit -> snack: "on success" triage: "CMS 'Bug Reports' ▸ admin reply" { class: place } node -> triage: "appears in unified inbox" triage -> email: "reply via email (one-way) · status=closed" email: "Email from support@…? (sender TBD — ask Bruce)\nFooter: 'Something new? Open a separate report.'" { class: place; style.stroke-dash: 4 } followup: "user has a\nfollow-up?" { class: conditional } email -> followup newrep: "Opens a NEW report\n(not a reply — loops to top)" { class: note } followup -> newrep: "yes" { style.stroke: "#9333ea" } note: "One-way email dodges reply-parsing.\nNo 'My reports' screen / push loop\nneeded → big scope cut vs. plan of record." { class: note } }