diff --git a/app/src/components/BodyPanel.jsx b/app/src/components/BodyPanel.jsx index f481b5b..adffad0 100644 --- a/app/src/components/BodyPanel.jsx +++ b/app/src/components/BodyPanel.jsx @@ -4,7 +4,7 @@ import { BodySVG, useIsMobile } from "../lib/bodymap.jsx"; // Renders a front+back body map pair: side-by-side on desktop, toggled on mobile. // Manages its own mobile view state so parents don't need to. -export default function BodyPanel({ primary, secondary, muscleMap, marginBottom = 16 }) { +export default function BodyPanel({ primary, secondary, muscleMap, marginBottom = 16, onHover, hovered }) { const isMobile = useIsMobile(); const [mobileView, setMobileView] = useState("front"); @@ -20,7 +20,7 @@ export default function BodyPanel({ primary, secondary, muscleMap, marginBottom ))}
- +
); @@ -30,7 +30,7 @@ export default function BodyPanel({ primary, secondary, muscleMap, marginBottom
{["front", "back"].map(view => (
- +
))}
diff --git a/app/src/components/History.jsx b/app/src/components/History.jsx index 56b1db0..ce30449 100644 --- a/app/src/components/History.jsx +++ b/app/src/components/History.jsx @@ -156,6 +156,7 @@ export default function History({ onShowHome, onShowLogger, onShowHistory, onSho const [analyzeError, setAnalyzeError] = useState(null); const [libraryExercises, setLibraryExercises] = useState([]); const [newExerciseIds, setNewExerciseIds] = useState(new Set()); + const [hoveredMuscle, setHoveredMuscle] = useState(null); const fileRef = useRef(); useEffect(() => { @@ -193,7 +194,8 @@ export default function History({ onShowHome, onShowLogger, onShowHistory, onSho const toggleExpand = (id) => { setExpandedIds(prev => { const next = new Set(prev); - next.has(id) ? next.delete(id) : next.add(id); + if (next.has(id)) { next.delete(id); setHoveredMuscle(null); } + else next.add(id); return next; }); }; @@ -227,6 +229,7 @@ export default function History({ onShowHome, onShowLogger, onShowHistory, onSho setSelectedDate(new Date(dateStr + "T12:00:00")); setEditMode(false); setSelectedSession(null); + setHoveredMuscle(null); loadSession(dateStr); }; @@ -498,8 +501,38 @@ export default function History({ onShowHome, onShowLogger, onShowHistory, onSho primary={sessionMuscles.primary} secondary={sessionMuscles.secondary} muscleMap={sessionMuscleMap} + onHover={setHoveredMuscle} + hovered={hoveredMuscle} + marginBottom={0} /> +
+ {hoveredMuscle ? ( +
+
+ {MUSCLES[hoveredMuscle]?.label} +
+
+
+ + {(sessionMuscleMap[hoveredMuscle] || []).length} + + + {(sessionMuscleMap[hoveredMuscle] || []).length === 1 ? "ØVELSE" : "ØVELSER"} + +
+ + {(sessionMuscleMap[hoveredMuscle] || []).join(" · ")} + +
+
+ ) : ( +
+ Hold musepeker over kroppen for detaljer +
+ )} +
+
Primær ({sessionMuscles.primary.length}) Sekundær ({sessionMuscles.secondary.length}) diff --git a/app/src/components/Home.jsx b/app/src/components/Home.jsx index cfc281d..1491163 100644 --- a/app/src/components/Home.jsx +++ b/app/src/components/Home.jsx @@ -3,7 +3,7 @@ import { format, parseISO, startOfISOWeek, addDays } from "date-fns"; import { nb } from "date-fns/locale"; import { InlineLoading } from "@carbon/react"; import { Camera } from "@carbon/icons-react"; -import { BodySVG, MUSCLES } from "../lib/bodymap.jsx"; +import { BodySVG } from "../lib/bodymap.jsx"; import { fetchLastSession, fetchThisWeekSessions } from "../lib/db"; import { extractMuscles } from "../lib/utils"; import PageShell, { SectionLabel, PageHeading } from "./PageShell"; @@ -76,12 +76,6 @@ export default function Home({ const muscles = lastSession ? extractMuscles(lastSession) : null; const isToday = lastSession?.session_date === format(today, "yyyy-MM-dd"); - const muscleMap = muscles - ? Object.fromEntries( - [...muscles.primary, ...muscles.secondary].map(id => [id, [MUSCLES[id]?.label ?? id]]) - ) - : {}; - const weekStart = startOfISOWeek(today); const weekDays = DAY_LABELS.map((label, i) => { const date = format(addDays(weekStart, i), "yyyy-MM-dd"); @@ -171,13 +165,13 @@ export default function Home({ {/* Body figures + exercise list side by side */}
- {/* Body figures — hover shows muscle name */} + {/* Body figures */}
- +
- +
@@ -261,11 +255,7 @@ export default function Home({ maxWidth: 200, }}> {tooltip.names.map((name, i) => ( -
0 ? "4px 0 0" : 0, - }}> +
0 ? "4px 0 0" : 0 }}> {name}
))} diff --git a/app/src/components/Report.jsx b/app/src/components/Report.jsx index cd5e708..5f011cf 100644 --- a/app/src/components/Report.jsx +++ b/app/src/components/Report.jsx @@ -322,9 +322,9 @@ export default function Report({ onShowHome, onShowLogger, onShowHistory, onShow ))}
-
+
{hoveredMuscle ? ( -
+
{MUSCLES[hoveredMuscle]?.label}
diff --git a/app/src/lib/bodymap.jsx b/app/src/lib/bodymap.jsx index cc8ffea..c6fe681 100644 --- a/app/src/lib/bodymap.jsx +++ b/app/src/lib/bodymap.jsx @@ -252,23 +252,28 @@ export function HeatmapBodySVG({ view, counts = {}, maxCount = 1, exerciseMap = ); } -export function BodySVG({ view, primary, secondary, muscleMap = {} }) { +export function BodySVG({ view, primary, secondary, muscleMap = {}, onHover, hovered }) { const pSet = new Set(primary); const sSet = new Set(secondary); const [tooltip, setTooltip] = React.useState(null); const wrapRef = React.useRef(); const handleEnter = (id, e) => { + if (onHover) { onHover(id); return; } const rect = wrapRef.current?.getBoundingClientRect(); if (!rect) return; setTooltip({ id, x: e.clientX - rect.left, y: e.clientY - rect.top }); }; const handleMove = (id, e) => { + if (onHover) return; const rect = wrapRef.current?.getBoundingClientRect(); if (!rect) return; setTooltip({ id, x: e.clientX - rect.left, y: e.clientY - rect.top }); }; - const handleLeave = () => setTooltip(null); + const handleLeave = () => { + if (onHover) { onHover(null); return; } + setTooltip(null); + }; return (
@@ -295,7 +300,7 @@ export function BodySVG({ view, primary, secondary, muscleMap = {} }) { const isPrimary = pSet.has(id); const isSec = sSet.has(id); if (!isPrimary && !isSec) return null; - const isHovered = tooltip?.id === id; + const isHovered = onHover ? id === hovered : tooltip?.id === id; const fill = isPrimary ? (isHovered ? PRIMARY_HOVER : PRIMARY_FILL) : `url(#sec-stripe-${view})`; @@ -321,7 +326,7 @@ export function BodySVG({ view, primary, secondary, muscleMap = {} }) { - {tooltip && muscleMap[tooltip.id]?.length > 0 && ( + {!onHover && tooltip && muscleMap[tooltip.id]?.length > 0 && (