feat(pipeline): full-page lead detail with Move Stage picker

Opens from the drawer's "More Details", replacing the placeholder toast.
The board swaps for a full-width lead record; "← Pipeline" goes back.

- Detail page: breadcrumb, hero (avatar, stage/type/storm/age pills,
  Call + Email), stage progress bar + 7-step stepper, and an overview
  grid of Contact / Property / Job Details / Insurance / Assignment.
- Move Stage dropdown so a lead can be re-staged from the detail page,
  not only by dragging on the kanban board. Stage changes now route
  through one setStage() used by both paths, so "reached" advancement
  and the toast behave identically.
- Detail is keyed by lead id rather than a snapshot, so a stage move
  updates the pills, progress, stepper and page accent live.
- pipeline-data: PLead gains phones, propertyType, source, tradeType,
  urgency, insurance (null for Retail jobs) and assignment. All derived
  deterministically from the existing rows — no Date.now, so SSR and
  client render identically.
- Dropdown is a bottom sheet under 720px and an anchored menu above it;
  dropped overflow:hidden on .pld-hero, which was clipping it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mayur Shinde
2026-07-27 18:27:55 +05:30
parent 190ef5f58e
commit c63bced08b
3 changed files with 419 additions and 13 deletions
+115
View File
@@ -1647,7 +1647,122 @@
.dash-root .pl-more { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; height: 44px; border: 0; border-radius: 12px; background: var(--blue); color: #fff; font-family: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer; transition: filter .14s; }
.dash-root .pl-more:hover { filter: brightness(1.08); }
/* =======================================================================
Pipeline — full-page lead detail (from the drawer's "More Details")
======================================================================= */
.dash-root .pld { display: flex; flex-direction: column; gap: 16px; }
/* ---- breadcrumb ---- */
.dash-root .pld-crumbs { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--faint); }
.dash-root .pld-back { display: inline-flex; align-items: center; gap: 6px; border: 0; background: none; padding: 0; font-family: inherit; font-size: 12.5px; font-weight: 600; color: var(--muted); cursor: pointer; transition: color .14s; }
.dash-root .pld-back:hover { color: var(--orange); }
.dash-root .pld-back-ic { transform: rotate(180deg); }
.dash-root .pld-crumb-now { color: var(--text); font-weight: 600; }
.dash-root .pld-crumb-id { margin-left: 2px; padding: 2px 7px; border-radius: 6px; background: var(--panel-2); border: 1px solid var(--border); font-size: 11px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }
/* ---- hero ---- */
/* No `overflow: hidden` here — it would clip the Move Stage dropdown. The
accent bar rounds its own top corners instead. */
.dash-root .pld-hero { display: flex; align-items: flex-start; gap: 16px; padding: 18px 20px; border-radius: 16px; border: 1px solid var(--border); background: var(--panel); position: relative; }
.dash-root .pld-hero::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; border-radius: 16px 16px 0 0; background: linear-gradient(90deg, var(--pl-accent), transparent); }
.dash-root .pld-hero-id { flex: 1; min-width: 0; }
.dash-root .pld-hero-name { font-size: 22px; font-weight: 700; letter-spacing: -0.01em; color: var(--text); }
.dash-root .pld-hero-addr { display: flex; align-items: center; gap: 6px; margin-top: 4px; font-size: 13px; color: var(--muted); }
.dash-root .pld-hero-addr svg { flex: 0 0 auto; color: var(--faint); }
.dash-root .pld-hero-pills { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-top: 12px; }
.dash-root .pld-hero-actions { display: flex; gap: 8px; flex: 0 0 auto; }
/* ---- move stage dropdown ---- */
.dash-root .pld-move { position: relative; }
.dash-root .pld-move-scrim { position: fixed; inset: 0; z-index: 40; }
.dash-root .pld-move-menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 41; width: 232px; max-height: min(60vh, 420px); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; padding: 6px; border-radius: 14px; border: 1px solid var(--border-2); background: var(--panel); box-shadow: var(--shadow-lg, 0 18px 44px -12px rgba(0,0,0,0.55)); animation: ds-fade .14s ease; }
.dash-root .pld-move-menu::-webkit-scrollbar { width: 6px; }
.dash-root .pld-move-menu::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 99px; }
.dash-root .pld-move-group + .pld-move-group { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--border); }
.dash-root .pld-move-grouphead { padding: 7px 10px 5px; font-size: 10px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: var(--faint); }
.dash-root .pld-move-item { display: flex; align-items: center; gap: 9px; width: 100%; padding: 8px 10px; border: 0; border-radius: 9px; background: none; font-family: inherit; font-size: 13px; font-weight: 600; color: var(--text-2); text-align: left; cursor: pointer; transition: background .12s, color .12s; }
.dash-root .pld-move-item:hover:not(:disabled) { background: color-mix(in srgb, var(--pl-accent) 12%, transparent); color: var(--text); }
.dash-root .pld-move-item:disabled { cursor: default; color: var(--text); }
.dash-root .pld-move-item.is-current { background: var(--panel-2); }
.dash-root .pld-move-item svg { margin-left: auto; color: var(--pl-accent); flex: 0 0 auto; }
.dash-root .pld-move-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pl-accent); flex: 0 0 auto; box-shadow: 0 0 8px color-mix(in srgb, var(--pl-accent) 55%, transparent); }
.dash-root .pld-move-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ---- stage progress ---- */
.dash-root .pld-progress { padding: 16px 20px; border-radius: 16px; border: 1px solid var(--border); background: var(--panel); }
.dash-root .pld-progress-row { display: flex; align-items: baseline; justify-content: space-between; }
.dash-root .pld-progress-label { font-size: 14px; font-weight: 700; color: var(--text); }
.dash-root .pld-progress-pct { font-size: 13px; font-weight: 700; color: var(--pl-accent); font-variant-numeric: tabular-nums; }
.dash-root .pld-progress-bar { height: 6px; border-radius: 99px; background: var(--track); overflow: hidden; margin: 10px 0 16px; }
.dash-root .pld-progress-bar > span { display: block; height: 100%; background: linear-gradient(90deg, color-mix(in srgb, var(--pl-accent) 55%, transparent), var(--pl-accent)); border-radius: inherit; transition: width .3s ease; }
.dash-root .pld-steps { display: flex; flex-wrap: wrap; gap: 10px 6px; list-style: none; margin: 0; padding: 0; }
.dash-root .pld-step { display: flex; align-items: center; gap: 7px; flex: 1 1 120px; min-width: 0; position: relative; }
.dash-root .pld-step::after { content: ""; position: absolute; left: 22px; right: 4px; top: 11px; height: 2px; background: var(--border); z-index: 0; }
.dash-root .pld-step:last-child::after { display: none; }
.dash-root .pld-step.done::after { background: var(--pl-accent); }
.dash-root .pld-step.now::after, .dash-root .pld-step.now ~ .pld-step::after { background: var(--border); }
.dash-root .pld-step-dot { position: relative; z-index: 1; display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; flex: 0 0 auto; background: var(--panel-2); border: 1px solid var(--border-2); color: var(--faint); font-size: 10.5px; font-weight: 700; }
.dash-root .pld-step.done .pld-step-dot { background: var(--pl-accent); border-color: var(--pl-accent); color: #fff; }
.dash-root .pld-step.now .pld-step-dot { box-shadow: 0 0 0 4px color-mix(in srgb, var(--pl-accent) 20%, transparent); }
.dash-root .pld-step-label { font-size: 11.5px; font-weight: 600; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; background: var(--panel); padding-right: 6px; position: relative; z-index: 1; }
.dash-root .pld-step.done .pld-step-label { color: var(--text-2); }
.dash-root .pld-step.now .pld-step-label { color: var(--text); }
/* ---- overview grid ---- */
.dash-root .pld-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.dash-root .pld-sec { border: 1px solid var(--border); border-radius: 14px; background: var(--panel); overflow: hidden; }
.dash-root .pld-sec.wide { grid-column: 1 / -1; }
.dash-root .pld-sec-head { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-bottom: 1px solid var(--border); background: var(--panel-2); font-size: 11px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: var(--text-2); }
.dash-root .pld-sec-head svg { color: var(--pl-accent); }
.dash-root .pld-sec-body { padding: 8px 16px 14px; }
.dash-root .pld-kv { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 9px 0; border-bottom: 1px dashed var(--border); }
.dash-root .pld-kv:last-child { border-bottom: 0; }
.dash-root .pld-kv-k { font-size: 12px; color: var(--muted); flex: 0 0 auto; }
.dash-root .pld-kv-v { font-size: 13px; font-weight: 600; color: var(--text); text-align: right; min-width: 0; overflow-wrap: anywhere; }
.dash-root .pld-sublabel { font-size: 10.5px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: var(--faint); margin: 12px 0 6px; }
.dash-root .pld-sec-body > .pld-sublabel:first-child { margin-top: 4px; }
.dash-root .pld-contact { display: flex; align-items: center; gap: 9px; padding: 7px 0; font-size: 13px; }
.dash-root .pld-contact svg { color: var(--muted); flex: 0 0 auto; }
.dash-root .pld-contact-val { font-weight: 600; color: var(--text); min-width: 0; overflow-wrap: anywhere; }
.dash-root .pld-contact-tag { font-size: 11px; color: var(--muted); padding: 2px 7px; border-radius: 6px; background: var(--panel-2); border: 1px solid var(--border); }
.dash-root .pld-notes { font-size: 13px; line-height: 1.55; color: var(--text-2); margin: 0; padding: 11px 13px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel-2); }
.dash-root .pld-assign { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0 24px; }
.dash-root .pld-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 26px 12px; color: var(--faint); text-align: center; }
.dash-root .pld-empty p { margin: 0; font-size: 12.5px; }
@media (max-width: 980px) {
.dash-root .pld-grid { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
.dash-root .pl-search { width: 100%; max-width: none; }
.dash-root .pl-col { flex-basis: 82vw; width: 82vw; }
.dash-root .pld-hero { flex-wrap: wrap; }
.dash-root .pld-hero-actions { width: 100%; }
.dash-root .pld-hero-actions > * { flex: 1; }
.dash-root .pld-move > .ds-btn { width: 100%; }
.dash-root .pld-step { flex: 1 1 100%; }
.dash-root .pld-step::after { display: none; }
/* Phones: a bottom sheet can't be clipped or pushed off-screen the way an
anchored dropdown can, and every stage stays thumb-reachable. */
.dash-root .pld-move-scrim { background: rgba(0,0,0,0.5); z-index: 90; animation: ds-fade .16s ease; }
.dash-root .pld-move-menu {
position: fixed; z-index: 91;
top: auto; right: 12px; left: 12px; bottom: max(12px, env(safe-area-inset-bottom));
width: auto; max-height: 72vh; padding: 8px;
border-radius: 18px; box-shadow: 0 -10px 44px -12px rgba(0,0,0,0.6);
animation: pld-sheet .2s cubic-bezier(.2,.7,.3,1);
}
.dash-root .pld-move-item { padding: 11px 12px; font-size: 14px; }
}
@keyframes pld-sheet { from { transform: translateY(14px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
/* Short viewports (landscape phones, small laptops): keep the anchored menu
inside the screen rather than letting it run past the fold. */
@media (min-width: 721px) and (max-height: 620px) {
.dash-root .pld-move-menu { max-height: 48vh; }
}