diff --git a/src/layout/Shell.css b/src/layout/Shell.css index 413cdda..9f36ab0 100644 --- a/src/layout/Shell.css +++ b/src/layout/Shell.css @@ -195,41 +195,6 @@ animation: zk-rise 0.28s var(--ease) both; } -/* ── Primary action ────────────────────────────────────────── */ -.shell__add { - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - margin: 0 2px 22px; - padding: 11px 14px; - border-radius: var(--r-md); - text-decoration: none; - font-size: 0.86rem; - font-weight: 500; - color: var(--zk-white); - background: var(--grad-blue); - box-shadow: var(--sh-sm); - transition: box-shadow var(--t), transform var(--t-fast), background var(--t); -} - -.shell__add:hover { - background: var(--grad-blue-hover); - box-shadow: var(--sh-md); - transform: translateY(-1px); -} - -.shell__add:active { - transform: none; - box-shadow: var(--sh-xs); -} - -.shell__add svg { - width: 15px; - height: 15px; - flex: none; -} - /* ── Narrow viewports: the rail lies down and scrolls ──────── */ @media (max-width: 900px) { .shell__body { diff --git a/src/layout/Shell.jsx b/src/layout/Shell.jsx index 9952df7..64ef4f7 100644 --- a/src/layout/Shell.jsx +++ b/src/layout/Shell.jsx @@ -1,6 +1,6 @@ import { NavLink, Outlet } from 'react-router-dom' -import { ENTRY, NAV_GROUPS } from '../api/config.js' -import { entryDoorsFor, rolesOf, visibleStages } from '../api/permissions.js' +import { NAV_GROUPS } from '../api/config.js' +import { rolesOf, visibleStages } from '../api/permissions.js' import { useStageCounts } from '../api/portfolio.jsx' import { useZino } from '../api/provider.jsx' import UserMenu from './UserMenu.jsx' @@ -36,7 +36,6 @@ export default function Shell() { // The sidebar shows the queues this role WORKS in. Reporting on the rest // lives on the overview, which counts the whole book for everyone. const stages = visibleStages(roles) - const canFile = entryDoorsFor(roles, ENTRY).length > 0 const { counts, ready } = useStageCounts() // One queue row. Extracted because the live groups and the folded Closed @@ -98,15 +97,11 @@ export default function Shell() { All leads - {canFile ? ( - - - New lead - - ) : null} + {/* "New lead" used to sit here. A sidebar is for moving between + places; filing a lead is an ACTION, and an action in a list of + destinations is the one item that does not behave like its + neighbours. It lives on the screens where leads are looked at — + the overview and the queues — beside the thing it adds to. */} {NAV_GROUPS.filter((g) => g.kind !== 'end').map((group) => { const inGroup = stages.filter((s) => s.kind === group.kind) diff --git a/src/screens/Overview.jsx b/src/screens/Overview.jsx index a5f87e8..856f409 100644 --- a/src/screens/Overview.jsx +++ b/src/screens/Overview.jsx @@ -2,8 +2,8 @@ import { useMemo } from 'react' import { Link, useNavigate } from 'react-router-dom' import { usePortfolio } from '../api/portfolio.jsx' import { useZino } from '../api/provider.jsx' -import { STAGES, phaseOf } from '../api/config.js' -import { rolesOf, visibleStages } from '../api/permissions.js' +import { ENTRY, STAGES, phaseOf } from '../api/config.js' +import { entryDoorsFor, rolesOf, visibleStages } from '../api/permissions.js' import { describeError } from '../api/errors.js' import './screens.css' @@ -58,6 +58,7 @@ export default function Overview() { const { user } = useZino() const navigate = useNavigate() const roles = rolesOf(user) + const canFile = entryDoorsFor(roles, ENTRY).length > 0 const state = usePortfolio() const m = useMemo(() => { @@ -167,11 +168,25 @@ export default function Overview() {

Portfolio overview

Motor and SME renewals · organisation-wide

- {state.at ? ( - - Updated {state.at.toLocaleTimeString('en-IN', { hour: '2-digit', minute: '2-digit' })} - - ) : null} + {/* The action sits with the thing it acts on. It was in the sidebar, + which is for moving between places — an action among destinations + is the one item that does not behave like its neighbours. */} +
+ {state.at ? ( + + Updated {state.at.toLocaleTimeString('en-IN', { hour: '2-digit', minute: '2-digit' })} + + ) : null} + {canFile ? ( + + + New lead + + ) : null} +
diff --git a/src/screens/Pipeline.jsx b/src/screens/Pipeline.jsx index d9fd03b..790b2f8 100644 --- a/src/screens/Pipeline.jsx +++ b/src/screens/Pipeline.jsx @@ -1,7 +1,8 @@ import { useEffect, useState } from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { Link, useNavigate, useParams } from 'react-router-dom' import { useZino } from '../api/provider.jsx' -import { CHANNELS, RV_LEADS, STAGES, phaseOf } from '../api/config.js' +import { CHANNELS, ENTRY, RV_LEADS, STAGES, phaseOf } from '../api/config.js' +import { entryDoorsFor, rolesOf } from '../api/permissions.js' import { describeError } from '../api/errors.js' /** Short absolute date plus how long ago — a queue needs both: the absolute @@ -70,7 +71,8 @@ import './screens.css' export default function Pipeline() { const { stageUid } = useParams() const navigate = useNavigate() - const { client } = useZino() + const { client, user } = useZino() + const canFile = entryDoorsFor(rolesOf(user), ENTRY).length > 0 // "all" is not a stage — it is every lead in one list, so an operator // wanting to find one does not have to guess which queue it is in. // @@ -173,6 +175,15 @@ export default function Pipeline() { {isAll ? shownRows.length : state.total} {(isAll ? shownRows.length : state.total) === 1 ? 'lead' : 'leads'}
+ {canFile ? ( + + + New lead + + ) : null} ) : null} diff --git a/src/screens/screens.css b/src/screens/screens.css index 3560b09..6ada5c7 100644 --- a/src/screens/screens.css +++ b/src/screens/screens.css @@ -1393,3 +1393,25 @@ .grid__row.is-done .grid__name { color: var(--zk-muted); font-weight: 400; } .grid__row.is-done:hover { color: var(--zk-ink); } .grid__row.is-done:hover .grid__name { color: var(--zk-blue-dark); } + +/* ---- file a lead ---- + Moved off the sidebar. A nav is for moving between places; this adds + something, and it now sits beside the lists it adds to. */ +.newlead { + display: inline-flex; + align-items: center; + gap: 8px; + flex: none; + padding: 9px 16px; + border-radius: var(--r-pill); + text-decoration: none; + font-size: 0.84rem; + font-weight: 500; + color: var(--zk-white); + background: var(--grad-blue); + box-shadow: var(--sh-sm); + transition: box-shadow var(--t), transform var(--t-fast), background var(--t); +} +.newlead svg { width: 14px; height: 14px; } +.newlead:hover { background: var(--grad-blue-hover); box-shadow: var(--sh-md); transform: translateY(-1px); } +.newlead:focus-visible { outline: 2px solid var(--zk-blue); outline-offset: 2px; }