diff --git a/src/app/dashboard/dashboard.css b/src/app/dashboard/dashboard.css index c292889..59ba5cf 100644 --- a/src/app/dashboard/dashboard.css +++ b/src/app/dashboard/dashboard.css @@ -29,6 +29,7 @@ /* ---- dark (default) ---- */ .dash-root, .dash-root[data-theme="dark"] { + color-scheme: dark; --bg: #060608; --panel: #0e0e13; --panel-2: #060608; @@ -46,6 +47,7 @@ /* ---- light ---- */ .dash-root[data-theme="light"] { + color-scheme: light; --bg: #eef1f7; --panel: #ffffff; --panel-2: #f5f7fb; @@ -363,6 +365,7 @@ } .dash-root .ds-textarea { height: auto; padding: 11px 13px; resize: vertical; line-height: 1.5; } .dash-root .ds-select { appearance: none; cursor: pointer; background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%); background-position: calc(100% - 18px) 18px, calc(100% - 13px) 18px; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 34px; } + .dash-root .ds-select option { background: var(--panel); color: var(--text); } .dash-root .ds-input:focus, .dash-root .ds-select:focus, .dash-root .ds-textarea:focus { border-color: var(--orange); box-shadow: 0 0 0 3px var(--ring); } .dash-root .ds-input.flush { border: 0; background: none; box-shadow: none; padding: 0 4px; height: 38px; } .dash-root .ds-input.flush:focus { box-shadow: none; } @@ -977,7 +980,21 @@ .dash-root[data-theme="dark"] .tm-ring-in { background: #14110d; } .dash-root .tm-rolecard-name { font-size: 15px; font-weight: 800; display: flex; align-items: center; } .dash-root .tm-rolecard-sub { font-size: 12px; color: var(--muted); margin-top: 2px; } - .dash-root .tm-rolecard-desc { font-size: 12.5px; color: var(--muted); line-height: 1.5; margin: 0 0 16px; } + .dash-root .tm-rolecard-desc { font-size: 12.5px; color: var(--muted); line-height: 1.5; margin: 0 0 14px; } + + /* Attire & appearance — persona-guide cues */ + .dash-root .tm-attire { border-top: 1px solid var(--border); padding-top: 13px; margin-bottom: 13px; } + .dash-root .tm-attire-h { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--rc); margin-bottom: 9px; } + .dash-root .tm-attire-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; } + .dash-root .tm-attire-list li { display: flex; align-items: flex-start; gap: 9px; font-size: 12.5px; color: var(--text-2); line-height: 1.45; } + .dash-root .tm-attire-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--rc); flex: 0 0 auto; margin-top: 7px; } + + /* Common overlap — how the role blends with others */ + .dash-root .tm-overlap { display: flex; gap: 10px; padding: 11px 12px; margin-bottom: 16px; border-radius: 12px; background: color-mix(in srgb, var(--rc) 7%, transparent); border: 1px solid color-mix(in srgb, var(--rc) 18%, transparent); } + .dash-root .tm-overlap-ic { color: var(--rc); flex: 0 0 auto; margin-top: 1px; } + .dash-root .tm-overlap b { display: block; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-2); margin-bottom: 3px; } + .dash-root .tm-overlap p { font-size: 12px; color: var(--muted); line-height: 1.5; margin: 0; } + .dash-root .tm-permgroup { margin-bottom: 14px; } .dash-root .tm-permgroup:last-child { margin-bottom: 0; } .dash-root .tm-permgroup-h { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--faint); margin-bottom: 8px; } diff --git a/src/components/dashboard/team-data.ts b/src/components/dashboard/team-data.ts index 3a7c076..a223497 100644 --- a/src/components/dashboard/team-data.ts +++ b/src/components/dashboard/team-data.ts @@ -66,64 +66,174 @@ export type Role = { description: string; system: boolean; // system roles can't be deleted perms: string[]; // granted permission ids + attire: string[]; // "attire & appearance" notes from the persona guide + overlap: string; // "common overlap" note — how the role blends with others }; +// Roles mirror the Construction Industry Personas & Attire reference guide: +// each role carries a plain-language description, the attire/appearance cues +// a person in that role typically shows, and the way the role commonly +// overlaps with others in a small contractor. export const roles: Role[] = [ { id: "owner", name: "Owner", color: "var(--orange)", - description: "Full, unrestricted access. There is exactly one account owner.", + description: "Sets the company standard, drives the vision, builds relationships and stays close to sales, operations, production, client care and financial performance.", system: true, perms: [...allPermissionIds], + attire: [ + "Tucked-in button-down or refined work shirt", + "Fitted jeans or chinos with a belt", + "Nice boots or leather shoes", + "Quality watch — Rolex-style or similar", + "Polished, confident, hands-on presence", + ], + overlap: "In smaller companies, owners are often involved in a little bit of everything: sales, estimating, production, payroll, hiring and customer issues.", }, { id: "admin", name: "Admin", color: "var(--purple)", - description: "Runs the workspace — manages people, roles and settings.", + description: "Keeps the office running — phones, calendars, customer updates, documents, onboarding paperwork, billing support and general organization.", system: true, perms: [...allPermissionIds], + attire: [ + "Business casual blouse, sweater or cardigan", + "Clean, comfortable office attire", + "Simple jewelry and neat grooming", + "Organized desk presence", + "Approachable, calm and detail-oriented", + ], + overlap: "In small teams, admins may also support HR, accounting, collections, dispatching, permit tracking and customer service.", }, { - id: "manager", + id: "sales-manager", name: "Sales Manager", color: "var(--blue)", - description: "Leads the sales floor and oversees the crew's pipeline.", + description: "Leads the sales team — manages pipeline, trains reps, reviews numbers, sets expectations, supports closes and holds the team accountable.", system: false, perms: ["leads.manage", "pipeline.manage", "estimates.create", "dispatch.manage", "billing.view", "reports.view", "team.manage"], + attire: [ + "Button-down shirt or polished business-casual top", + "Chinos or clean slacks", + "Leather shoes or clean boots", + "Folder, tablet or sales materials", + "More refined than a field rep, still jobsite-aware", + ], + overlap: "Often supports recruiting, coaching, ride-alongs, lead routing, team meetings and performance reviews.", }, { - id: "rep", + id: "sales-rep", name: "Sales Rep", color: "var(--green)", - description: "Works leads and moves deals through the pipeline.", + description: "Builds trust with homeowners, performs inspections, explains options, creates estimates, follows up and turns leads into signed projects.", system: false, perms: ["leads.manage", "pipeline.manage", "estimates.create"], + attire: [ + "Branded polo or clean casual button-down", + "Well-fitted pants or jeans", + "Casual sneakers, boots or clean field shoes", + "Tablet, phone, folder or inspection tools", + "Polished, friendly and trustworthy look", + ], + overlap: "In many smaller contractors, a sales rep may also act as project manager after the sale and stay responsible for the customer experience.", }, { - id: "dispatcher", - name: "Dispatcher", + id: "project-manager", + name: "Project Manager", color: "var(--cyan)", - description: "Schedules crews and coordinates site visits.", + description: "Owns the job after approval — scope review, material coordination, scheduling, customer updates, trade coordination, documentation and closeout.", + system: false, + perms: ["leads.manage", "pipeline.manage", "dispatch.manage", "billing.view", "reports.view"], + attire: [ + "Button-down, polo or clean field shirt", + "Jeans or durable pants", + "Work boots or field-ready shoes", + "Tablet, plans, folder or measuring tools", + "Professional but ready to walk a jobsite", + ], + overlap: "Commonly overlaps with sales, estimating, scheduling, collections and quality control.", + }, + { + id: "site-supervisor", + name: "Site Supervisor", + color: "var(--red)", + description: "Runs daily field execution — watches quality and safety, keeps crews moving, verifies details and helps projects stay on schedule.", system: false, perms: ["dispatch.manage", "reports.view"], + attire: [ + "Company t-shirt or work shirt", + "Ball cap or jobsite hat", + "Durable jeans or work pants", + "Work boots and jobsite watch", + "Practical, experienced, hands-on look", + ], + overlap: "May coordinate subs, verify deliveries, answer field questions, document progress and report back to the project manager or owner.", }, { - id: "estimator", - name: "Estimator", + id: "canvasser", + name: "Canvasser", color: "var(--yellow)", - description: "Prepares estimates and proposals for approved leads.", + description: "Front-line lead generator — walks neighborhoods, knocks doors, qualifies homes, starts conversations and books inspections for sales reps.", system: false, - perms: ["leads.manage", "estimates.create"], + perms: ["leads.manage"], + attire: [ + "Young, clean, energetic appearance", + "Branded polo shirt", + "Lanyard ID badge", + "Comfortable pants or khakis", + "Clean sneakers, phone and shoulder bag", + ], + overlap: "A strong canvasser may grow into a sales rep or team lead once they learn scripts, objections, territory discipline and appointment quality.", }, { - id: "viewer", - name: "Viewer", - color: "var(--muted)", - description: "Read-only access to reports and billing — no edits.", + id: "team-lead", + name: "Team Lead", + color: "#ec4899", + description: "Leads a small group of reps or canvassers — a sales team lead or a canvassing team lead depending on the company structure.", system: false, - perms: ["reports.view", "billing.view"], + perms: ["leads.manage", "pipeline.manage", "reports.view"], + attire: [ + "Branded polo or clean field-casual shirt", + "Well-fitted pants with belt", + "Boots or clean sneakers", + "Tablet, phone, clipboard or route list", + "Confident, coachable, team-first presence", + ], + overlap: "May run morning huddles, assign territory, coach scripts, inspect appointment quality and help new people ramp faster.", + }, + { + id: "subcontractor", + name: "Subcontractor", + color: "var(--orange-2)", + description: "Specialized trade partner performing the actual skilled work — roofing, framing, electrical, HVAC, masonry, gutters, painting, drywall or flooring.", + system: false, + perms: ["dispatch.manage", "billing.view"], + attire: [ + "Trade-specific workwear", + "Hard hat, high-vis vest, gloves and PPE when needed", + "Durable pants and work boots", + "Tools, belt or equipment as required", + "Rugged, skilled and dependable presence", + ], + overlap: "Subs may have their own crews, foremen, schedules, invoices, insurance documents and safety requirements.", + }, + { + id: "other", + name: "Other", + color: "var(--muted)", + description: "Utility roles — estimator, dispatcher, bookkeeper, HR coordinator, accounting support, warehouse support, permit coordinator, marketing assistant or specialist.", + system: false, + perms: ["reports.view"], + attire: [ + "Dress for the function of the day", + "Office casual for desk work", + "Polo, jeans, boots or safety gear for field visits", + "Clean, reliable, company-aligned appearance", + "Flexible enough to support multiple departments", + ], + overlap: "In smaller companies, other roles often become utility players covering gaps across operations, accounting, HR, dispatch, estimating and customer service.", }, ]; @@ -148,13 +258,16 @@ export type Member = { export const members: Member[] = [ { id: "u1", name: "James Carter", initials: "JC", email: "james.carter@lynkeduppro.com", title: "Founder & CEO", roleId: "owner", gradient: "linear-gradient(135deg,#fda913,#fd6d13)", status: "active", lastActive: "Active now", deals: 12, joined: "Mar 2021" }, - { id: "u2", name: "Emma Wilson", initials: "EW", email: "emma.wilson@lynkeduppro.com", title: "Head of Sales", roleId: "manager", gradient: "linear-gradient(135deg,#285ef0,#09b9c6)", status: "active", lastActive: "Active now", deals: 21, joined: "Aug 2021" }, - { id: "u3", name: "Liam Foster", initials: "LF", email: "liam.foster@lynkeduppro.com", title: "Senior Sales Rep", roleId: "rep", gradient: "linear-gradient(135deg,#9036e9,#285ef0)", status: "active", lastActive: "5 min ago", deals: 17, joined: "Jan 2022" }, - { id: "u4", name: "Sophie Turner", initials: "ST", email: "sophie.turner@lynkeduppro.com", title: "Account Executive", roleId: "rep", gradient: "linear-gradient(135deg,#14bc83,#09b9c6)", status: "away", lastActive: "32 min ago", deals: 9, joined: "May 2022" }, - { id: "u5", name: "Noah Mitchell", initials: "NM", email: "noah.mitchell@lynkeduppro.com", title: "Dispatch Coordinator", roleId: "dispatcher", gradient: "linear-gradient(135deg,#f0563f,#fda913)", status: "active", lastActive: "Active now", deals: 0, joined: "Sep 2022" }, - { id: "u6", name: "Olivia Bennett", initials: "OB", email: "olivia.bennett@lynkeduppro.com", title: "Operations Lead", roleId: "admin", gradient: "linear-gradient(135deg,#09b9c6,#285ef0)", status: "away", lastActive: "1 hour ago", deals: 3, joined: "Nov 2021" }, - { id: "u7", name: "Ethan Brooks", initials: "EB", email: "ethan.brooks@lynkeduppro.com", title: "Lead Estimator", roleId: "estimator", gradient: "linear-gradient(135deg,#ffd60a,#fda913)", status: "offline", lastActive: "Yesterday", deals: 6, joined: "Feb 2023" }, - { id: "u8", name: "Ava Reynolds", initials: "AR", email: "ava.reynolds@lynkeduppro.com", title: "Finance Analyst", roleId: "viewer", gradient: "linear-gradient(135deg,#9036e9,#f0563f)", status: "offline", lastActive: "2 days ago", deals: 0, joined: "Apr 2023" }, + { id: "u2", name: "Emma Wilson", initials: "EW", email: "emma.wilson@lynkeduppro.com", title: "Head of Sales", roleId: "sales-manager", gradient: "linear-gradient(135deg,#285ef0,#09b9c6)", status: "active", lastActive: "Active now", deals: 21, joined: "Aug 2021" }, + { id: "u3", name: "Liam Foster", initials: "LF", email: "liam.foster@lynkeduppro.com", title: "Senior Sales Rep", roleId: "sales-rep", gradient: "linear-gradient(135deg,#9036e9,#285ef0)", status: "active", lastActive: "5 min ago", deals: 17, joined: "Jan 2022" }, + { id: "u4", name: "Sophie Turner", initials: "ST", email: "sophie.turner@lynkeduppro.com", title: "Account Executive", roleId: "sales-rep", gradient: "linear-gradient(135deg,#14bc83,#09b9c6)", status: "away", lastActive: "32 min ago", deals: 9, joined: "May 2022" }, + { id: "u5", name: "Noah Mitchell", initials: "NM", email: "noah.mitchell@lynkeduppro.com", title: "Site Supervisor", roleId: "site-supervisor", gradient: "linear-gradient(135deg,#f0563f,#fda913)", status: "active", lastActive: "Active now", deals: 0, joined: "Sep 2022" }, + { id: "u6", name: "Olivia Bennett", initials: "OB", email: "olivia.bennett@lynkeduppro.com", title: "Office Admin", roleId: "admin", gradient: "linear-gradient(135deg,#09b9c6,#285ef0)", status: "away", lastActive: "1 hour ago", deals: 3, joined: "Nov 2021" }, + { id: "u7", name: "Ethan Brooks", initials: "EB", email: "ethan.brooks@lynkeduppro.com", title: "Project Manager", roleId: "project-manager", gradient: "linear-gradient(135deg,#ffd60a,#fda913)", status: "offline", lastActive: "Yesterday", deals: 6, joined: "Feb 2023" }, + { id: "u8", name: "Ava Reynolds", initials: "AR", email: "ava.reynolds@lynkeduppro.com", title: "Bookkeeper", roleId: "other", gradient: "linear-gradient(135deg,#9036e9,#f0563f)", status: "offline", lastActive: "2 days ago", deals: 0, joined: "Apr 2023" }, + { id: "u9", name: "Mason Reed", initials: "MR", email: "mason.reed@lynkeduppro.com", title: "Field Canvasser", roleId: "canvasser", gradient: "linear-gradient(135deg,#ffd60a,#14bc83)", status: "active", lastActive: "Active now", deals: 4, joined: "Jun 2023" }, + { id: "u10", name: "Chloe Adams", initials: "CA", email: "chloe.adams@lynkeduppro.com", title: "Sales Team Lead", roleId: "team-lead", gradient: "linear-gradient(135deg,#ec4899,#9036e9)", status: "away", lastActive: "18 min ago", deals: 11, joined: "Oct 2022" }, + { id: "u11", name: "Diego Ramirez", initials: "DR", email: "diego.ramirez@lynkeduppro.com", title: "Roofing Subcontractor", roleId: "subcontractor", gradient: "linear-gradient(135deg,#fd6d13,#f0563f)", status: "offline", lastActive: "3 days ago", deals: 0, joined: "Jan 2023" }, ]; /* ---------------------------------------------------------- */ @@ -164,6 +277,6 @@ export const members: Member[] = [ export type Invite = { id: string; email: string; roleId: string; invitedBy: string; sentAt: string }; export const pendingInvites: Invite[] = [ - { id: "inv1", email: "daniel.hughes@gmail.com", roleId: "rep", invitedBy: "Emma Wilson", sentAt: "2 days ago" }, - { id: "inv2", email: "grace.murphy@outlook.com", roleId: "estimator", invitedBy: "James Carter", sentAt: "5 days ago" }, + { id: "inv1", email: "daniel.hughes@gmail.com", roleId: "sales-rep", invitedBy: "Emma Wilson", sentAt: "2 days ago" }, + { id: "inv2", email: "grace.murphy@outlook.com", roleId: "canvasser", invitedBy: "James Carter", sentAt: "5 days ago" }, ]; diff --git a/src/components/dashboard/team-management.tsx b/src/components/dashboard/team-management.tsx index d3b1a16..7c96e87 100644 --- a/src/components/dashboard/team-management.tsx +++ b/src/components/dashboard/team-management.tsx @@ -266,6 +266,23 @@ export function TeamManagement() {
{r.description}
+{r.overlap}
+