diff --git a/src/app/plan/plan.css b/src/app/plan/plan.css index 9f41bc6..031ec34 100644 --- a/src/app/plan/plan.css +++ b/src/app/plan/plan.css @@ -86,38 +86,41 @@ .plan-axis-days { position: absolute; top: 34px; right: 0; left: 0; height: 33px; } .plan-axis-days span { position: absolute; top: 0; bottom: 0; display: grid; place-items: center; border-right: 1px solid color-mix(in srgb, var(--border-subtle) 55%, transparent); color: var(--text-muted); font-size: 10px; font-variant-numeric: tabular-nums; } .plan-axis-days span.is-today { background: var(--brand-soft); color: var(--brand); font-weight: 800; } -.plan-timeline-row { height: 61px; border-bottom: 1px solid var(--border-subtle); } +.plan-timeline-row { min-height: 61px; border-bottom: 1px solid var(--border-subtle); } .plan-timeline-row:last-child { border-bottom: 0; } .plan-timeline-row:hover .plan-card-label { background: var(--surface); } -.plan-card-label { display: flex; flex-direction: column; justify-content: center; padding: 0 37px 0 17px; transition: background .15s; } -.plan-card-name { display: flex; align-items: center; gap: 8px; min-width: 0; } -.plan-priority-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: var(--plan-cyan); } +.plan-card-label { display: flex; flex-direction: column; justify-content: center; min-height: 61px; padding: 10px 37px 10px 17px; transition: background .15s; } +.plan-card-details-button { display: block; width: 100%; min-width: 0; border-radius: 5px; color: inherit; text-align: left; } +.plan-card-details-button:hover .plan-card-title { color: var(--brand); } +.plan-card-details-button:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; } +.plan-card-name { display: flex; align-items: flex-start; gap: 8px; min-width: 0; } +.plan-priority-dot { width: 7px; height: 7px; flex: none; margin-top: 5px; border-radius: 50%; background: var(--plan-cyan); } .plan-priority-dot.is-critical { background: var(--plan-coral); } .plan-priority-dot.is-high { background: var(--plan-amber); } .plan-priority-dot.is-low { background: var(--plan-olive); } -.plan-card-title { overflow: hidden; font-size: 12px; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; } -.plan-card-subline { display: flex; gap: 8px; margin: 5px 0 0 15px; color: var(--text-muted); font-size: 10px; white-space: nowrap; } +.plan-card-title { min-width: 0; overflow-wrap: anywhere; font-size: 12px; font-weight: 700; line-height: 1.35; } +.plan-card-subline { display: flex; flex-wrap: wrap; gap: 4px 8px; margin: 5px 0 0 15px; color: var(--text-muted); font-size: 10px; } .plan-card-subline span + span::before { content: "·"; margin-right: 8px; color: var(--border-strong); } .plan-card-subline .plan-needs-change { color: var(--accent); font-weight: 800; } -.plan-row-edit { position: absolute; right: 11px; top: 19px; display: grid; width: 24px; height: 24px; place-items: center; border-radius: 6px; color: var(--text-muted); opacity: .55; } +.plan-row-edit { position: absolute; right: 11px; top: 50%; display: grid; width: 24px; height: 24px; place-items: center; border-radius: 6px; color: var(--text-muted); opacity: .55; transform: translateY(-50%); } .plan-row-edit:hover { background: var(--surface-raised); color: var(--brand); opacity: 1; } -.plan-row-track { position: relative; flex: none; background: color-mix(in srgb, var(--surface) 38%, var(--panel)); } +.plan-row-track { position: relative; min-height: 61px; flex: none; background: color-mix(in srgb, var(--surface) 38%, var(--panel)); } .plan-row-track.is-weeks { background-image: repeating-linear-gradient(to right, transparent 0, transparent 30px, color-mix(in srgb, var(--border-subtle) 52%, transparent) 30px, color-mix(in srgb, var(--border-subtle) 52%, transparent) 31px); } .plan-track-line { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--border-subtle); } .plan-today-line { position: absolute; z-index: 1; top: 0; bottom: 0; width: 2px; background: var(--brand); opacity: .65; pointer-events: none; } -.plan-bar { position: absolute; z-index: 2; top: 18px; height: 25px; overflow: hidden; border-radius: 99px; background: var(--plan-cyan); box-shadow: inset 0 1px 1px #ffffff9c, 0 2px 5px #051a2d1d; color: #082938; cursor: pointer; } +.plan-bar { position: absolute; z-index: 2; top: calc(50% - 12px); height: 25px; overflow: hidden; border-radius: 99px; background: var(--plan-cyan); box-shadow: inset 0 1px 1px #ffffff9c, 0 2px 5px #051a2d1d; color: #082938; cursor: pointer; } .plan-bar.is-critical { background: var(--plan-coral); } .plan-bar.is-high { background: var(--plan-amber); } .plan-bar.is-low { background: var(--plan-olive); } .plan-bar:disabled { cursor: default; } .plan-bar:hover:not(:disabled) { filter: brightness(1.06); transform: translateY(-1px); } -.plan-bar.is-approved-ghost { z-index: 1; top: 13px; height: 35px; border: 1px dashed var(--brand); background: var(--brand-soft); box-shadow: none; opacity: .9; pointer-events: none; } +.plan-bar.is-approved-ghost { z-index: 1; top: calc(50% - 17px); height: 35px; border: 1px dashed var(--brand); background: var(--brand-soft); box-shadow: none; opacity: .9; pointer-events: none; } .plan-bar-progress { position: absolute; top: 2px; bottom: 2px; left: 2px; max-width: calc(100% - 4px); border-radius: 99px; background: #ffffff6e; pointer-events: none; } .plan-bar-title { position: relative; display: block; overflow: hidden; padding: 0 11px; font-size: 10px; font-weight: 800; line-height: 25px; text-align: left; text-overflow: ellipsis; white-space: nowrap; } -.plan-set-dates, .plan-awaiting-dates { position: absolute; top: 17px; left: 18px; display: inline-flex; align-items: center; gap: 6px; height: 27px; padding: 0 10px; border: 1px dashed var(--border-strong); border-radius: 99px; background: var(--panel); color: var(--text-muted); font-size: 10px; font-weight: 700; white-space: nowrap; } +.plan-set-dates, .plan-awaiting-dates { position: absolute; top: calc(50% - 13px); left: 18px; display: inline-flex; align-items: center; gap: 6px; height: 27px; padding: 0 10px; border: 1px dashed var(--border-strong); border-radius: 99px; background: var(--panel); color: var(--text-muted); font-size: 10px; font-weight: 700; white-space: nowrap; } .plan-set-dates:hover { border-color: var(--brand); color: var(--brand); } .plan-awaiting-dates { border: 0; background: transparent; font-weight: 500; } -.plan-outside-range { position: absolute; top: 17px; left: 18px; display: inline-flex; align-items: center; gap: 6px; height: 27px; padding: 0 10px; border-radius: 99px; background: var(--brand-soft); color: var(--brand); font-size: 10px; font-weight: 800; white-space: nowrap; } +.plan-outside-range { position: absolute; top: calc(50% - 13px); left: 18px; display: inline-flex; align-items: center; gap: 6px; height: 27px; padding: 0 10px; border-radius: 99px; background: var(--brand-soft); color: var(--brand); font-size: 10px; font-weight: 800; white-space: nowrap; } .plan-outside-range:hover { filter: brightness(1.08); } .plan-project-footer { gap: 12px; min-height: 61px; padding: 11px 19px; border-top: 1px solid var(--border-subtle); } @@ -169,6 +172,26 @@ .plan-calendar-day.is-between { border-radius: 0; background: var(--brand-soft); } .plan-calendar-day.is-selected { background: var(--brand); color: var(--brand-fg); font-weight: 800; } .plan-dialog-bottom { align-items: flex-end; gap: 14px; padding: 20px 25px 24px; } +.plan-item-dialog { width: min(100%, 560px); max-height: calc(100vh - 40px); overflow-y: auto; border: 1px solid var(--border-subtle); border-radius: 17px; background: var(--panel); box-shadow: 0 22px 70px #0017285c; } +.plan-item-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 24px 27px 18px; border-bottom: 1px solid var(--border-subtle); } +.plan-item-top p { margin-top: 6px; color: var(--text-muted); font-size: 12px; } +.plan-item-body { padding: 22px 27px 24px; } +.plan-item-body h2 { overflow-wrap: anywhere; font-family: Georgia, "Times New Roman", serif; font-size: clamp(22px, 4vw, 29px); font-weight: 600; line-height: 1.15; } +.plan-item-tags { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 17px; } +.plan-item-tags > span { display: inline-flex; align-items: center; gap: 7px; padding: 6px 10px; border: 1px solid var(--border-subtle); border-radius: 99px; background: var(--surface); color: var(--text-secondary); font-size: 11px; font-weight: 700; } +.plan-item-priority i { width: 7px; height: 7px; border-radius: 50%; background: var(--plan-cyan); } +.plan-item-priority.is-critical i { background: var(--plan-coral); } +.plan-item-priority.is-high i { background: var(--plan-amber); } +.plan-item-priority.is-low i { background: var(--plan-olive); } +.plan-item-section { margin-top: 25px; } +.plan-item-section h3 { margin-bottom: 9px; color: var(--text-muted); font-size: 10px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; } +.plan-item-section p { overflow-wrap: anywhere; color: var(--text-secondary); font-size: 13px; line-height: 1.65; white-space: pre-wrap; } +.plan-item-section p.is-muted { color: var(--text-muted); } +.plan-item-facts { display: grid; gap: 9px; } +.plan-item-facts div { display: flex; justify-content: space-between; gap: 15px; padding: 10px 12px; border: 1px solid var(--border-subtle); border-radius: 8px; background: var(--surface); font-size: 12px; } +.plan-item-facts dt { color: var(--text-muted); } +.plan-item-facts dd { color: var(--text-primary); font-weight: 700; text-align: right; } +.plan-item-footer { display: flex; justify-content: space-between; gap: 9px; padding: 18px 27px 23px; border-top: 1px solid var(--border-subtle); } .plan-quick-actions { display: flex; flex-wrap: wrap; gap: 6px 11px; } .plan-quick-actions button { color: var(--brand); font-size: 10px; font-weight: 750; } .plan-quick-actions button:hover { text-decoration: underline; } @@ -188,6 +211,10 @@ } @media (max-width: 530px) { + .plan-item-top { padding: 20px 20px 16px; } + .plan-item-body { padding: 20px; } + .plan-item-footer { flex-direction: column-reverse; padding: 16px 20px 20px; } + .plan-item-footer > * { width: 100%; } .plan-toolbar-filters { align-items: stretch; flex-direction: column; } .plan-project-filter select { min-width: 0; width: 100%; } .plan-project-filter { width: 100%; } diff --git a/src/components/planning/plan-board.tsx b/src/components/planning/plan-board.tsx index cc1822b..4e7c0dd 100644 --- a/src/components/planning/plan-board.tsx +++ b/src/components/planning/plan-board.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { useRouter } from "next/navigation"; import { ArrowRight, @@ -21,6 +21,7 @@ import { submitProjectPlanAction, } from "@/app/actions/project-plan"; import { DateRangeDialog } from "@/components/planning/date-range-dialog"; +import { WorkItemDialog } from "@/components/planning/work-item-dialog"; import { useToast } from "@/components/ui/toast"; import { calendarAxis, @@ -70,6 +71,7 @@ export function PlanBoard({ projects, role }: { projects: PlanningProject[]; rol const [zoom, setZoom] = useState("weeks"); const [anchor, setAnchor] = useState(todayLocalIso()); const [editing, setEditing] = useState(null); + const [details, setDetails] = useState(null); const [pending, setPending] = useState(false); const [feedbackFor, setFeedbackFor] = useState(null); const [feedback, setFeedback] = useState(""); @@ -89,6 +91,8 @@ export function PlanBoard({ projects, role }: { projects: PlanningProject[]; rol const waiting = board.filter((project) => project.plan.status === "submitted").length; const editProject = board.find((project) => project.id === editing?.projectId); const editCard = editProject?.cards.find((card) => card.id === editing?.cardId); + const detailsProject = board.find((project) => project.id === details?.projectId); + const detailsCard = detailsProject?.cards.find((card) => card.id === details?.cardId); function applyPlan(projectId: string, plan: ProjectPlan) { setBoard((current) => current.map((project) => { @@ -264,7 +268,11 @@ export function PlanBoard({ projects, role }: { projects: PlanningProject[]; rol sortOrder={sortOrder} canEdit={canEdit} onJump={setAnchor} - onEdit={(cardId) => setEditing({ projectId: project.id, cardId })} + onDetails={(cardId) => setDetails({ projectId: project.id, cardId })} + onEdit={(cardId) => { + setDetails(null); + setEditing({ projectId: project.id, cardId }); + }} />