Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/src/components/History.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ export default function History({ onShowHome, onShowLogger, onShowHistory, onSho
/>

{loading ? (
<div style={{ background: "var(--cds-layer-01)", border: "1px solid var(--cds-border-subtle-01)", padding: "16px 12px", marginBottom: 24 }}>
<div aria-live="polite" aria-busy="true" style={{ background: "var(--cds-layer-01)", border: "1px solid var(--cds-border-subtle-01)", padding: "16px 12px", marginBottom: 24 }}>
<SkeletonPlaceholder style={{ width: "100%", height: 280 }} />
</div>
) : (
Expand Down Expand Up @@ -388,7 +388,7 @@ export default function History({ onShowHome, onShowLogger, onShowHistory, onSho
)}

{loadingSession && (
<div style={{ marginBottom: 24 }}>
<div aria-live="polite" aria-busy="true" style={{ marginBottom: 24 }}>
<AccordionSkeleton count={2} />
</div>
)}
Expand Down
71 changes: 52 additions & 19 deletions app/src/components/Home.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import { format, parseISO, startOfISOWeek, addDays } from "date-fns";
import { nb } from "date-fns/locale";
import { InlineLoading } from "@carbon/react";
Expand Down Expand Up @@ -45,7 +45,8 @@ export default function Home({
}) {
const [lastSession, setLastSession] = useState(undefined);
const [weekSessions, setWeekSessions] = useState(undefined);
const [hoveredDay, setHoveredDay] = useState(null);
const [tooltip, setTooltip] = useState(null);
const weekStripRef = useRef();
const [syncState, setSyncState] = useState(null);
const [syncMsg, setSyncMsg] = useState('');

Expand Down Expand Up @@ -129,7 +130,7 @@ export default function Home({
<SectionLabel>{isToday ? "DAGENS ØKT" : "SISTE ØKT"}</SectionLabel>

{lastSession === undefined && (
<div style={{ display: "flex", justifyContent: "center", padding: "16px 0 32px" }}>
<div aria-live="polite" aria-busy="true" style={{ display: "flex", justifyContent: "center", padding: "16px 0 32px" }}>
<InlineLoading description="Laster siste økt…" />
</div>
)}
Expand Down Expand Up @@ -222,26 +223,58 @@ export default function Home({
{/* Weekly strip */}
<SectionLabel>UKEN SÅ LANGT</SectionLabel>
<div style={{ padding: "0 16px" }}>
<div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 2 }}>
{weekDays.map(({ label, count, date }, i) => (
<div key={i} style={{ textAlign: "center" }}>
<div style={{ fontFamily: "var(--cds-font-mono)", fontSize: 10, color: "var(--cds-text-secondary)", marginBottom: 4, letterSpacing: "0.1em" }}>
{label}
<div ref={weekStripRef} style={{ position: "relative" }}>
<div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 2 }}>
{weekDays.map(({ label, count, date, names }, i) => (
<div key={i} style={{ textAlign: "center" }}>
<div style={{ fontFamily: "var(--cds-font-mono)", fontSize: 10, color: "var(--cds-text-secondary)", marginBottom: 4, letterSpacing: "0.1em" }}>
{label}
</div>
<div
onClick={count > 0 ? () => onShowHistoryWithDate(date) : undefined}
onMouseEnter={count > 0 ? (e) => {
const rect = weekStripRef.current?.getBoundingClientRect();
if (!rect) return;
setTooltip({ names, x: e.clientX - rect.left, y: e.clientY - rect.top });
} : undefined}
onMouseMove={count > 0 ? (e) => {
const rect = weekStripRef.current?.getBoundingClientRect();
if (!rect) return;
setTooltip(prev => prev ? { ...prev, x: e.clientX - rect.left, y: e.clientY - rect.top } : prev);
} : undefined}
onMouseLeave={count > 0 ? () => setTooltip(null) : undefined}
style={{ height: 36, background: heatColor(count), border: "1px solid var(--cds-border-subtle-01)", cursor: count > 0 ? "pointer" : "default" }}
/>
</div>
<div
onClick={count > 0 ? () => onShowHistoryWithDate(date) : undefined}
onMouseEnter={count > 0 ? () => setHoveredDay(i) : undefined}
onMouseLeave={count > 0 ? () => setHoveredDay(null) : undefined}
style={{ height: 36, background: heatColor(count), border: "1px solid var(--cds-border-subtle-01)", cursor: count > 0 ? "pointer" : "default" }}
/>
))}
</div>
{tooltip && tooltip.names.length > 0 && (
<div style={{
position: "absolute",
left: Math.min(tooltip.x + 10, (weekStripRef.current?.offsetWidth || 300) - 160),
top: Math.max(tooltip.y - 10, 4),
background: "var(--cds-layer-02)",
border: "1px solid var(--cds-border-subtle-01)",
padding: "8px 10px",
zIndex: 10,
pointerEvents: "none",
maxWidth: 200,
}}>
{tooltip.names.map((name, i) => (
<div key={i} style={{
fontFamily: "var(--cds-font-mono)", fontSize: 12,
color: "var(--cds-text-primary)",
padding: i > 0 ? "4px 0 0" : 0,
}}>
{name}
</div>
))}
</div>
))}
)}
</div>
{weekSessions !== undefined && (
<div style={{ fontFamily: "var(--cds-font-mono)", fontSize: 11, color: "var(--cds-text-secondary)", marginTop: 8, letterSpacing: "0.06em", minHeight: 16 }}>
{hoveredDay !== null && weekDays[hoveredDay]?.names.length > 0
? weekDays[hoveredDay].names.join(" · ")
: `${weekSessionCount} ØKTE${weekSessionCount !== 1 ? "R" : ""} · ${weekMuscleCount} MUSKELGRUPPE${weekMuscleCount !== 1 ? "R" : ""}`}
<div style={{ fontFamily: "var(--cds-font-mono)", fontSize: 11, color: "var(--cds-text-secondary)", marginTop: 8, letterSpacing: "0.06em" }}>
{`${weekSessionCount} ØKTE${weekSessionCount !== 1 ? "R" : ""} · ${weekMuscleCount} MUSKELGRUPPE${weekMuscleCount !== 1 ? "R" : ""}`}
</div>
)}
</div>
Expand Down
34 changes: 20 additions & 14 deletions app/src/components/MuscleMap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
const STEP_HEADINGS = { upload: "Last opp bilde", analyzing: "Analyserer…", confirm: "Bekreft øvelser", muscles: "Analyse av økt" };
const STEP_LABELS = ["Last opp bilde", "Bekreft øvelser", "Analyse av økt"];

export const initialState = {

Check warning on line 25 in app/src/components/MuscleMap.jsx

View workflow job for this annotation

GitHub Actions / Lint, test and audit

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
step: "upload",
images: [],
exercises: [],
Expand All @@ -42,7 +42,7 @@
sessionDate: localDateStr(),
};

export function reducer(state, action) {

Check warning on line 45 in app/src/components/MuscleMap.jsx

View workflow job for this annotation

GitHub Actions / Lint, test and audit

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
switch (action.type) {
case "RESET":
return { ...initialState, sessionDate: localDateStr() };
Expand Down Expand Up @@ -120,9 +120,11 @@
if (!templatePreload) return;
dispatch({ type: "LOAD_TEMPLATE", exercises: templatePreload.map((e, i) => ({ ...e, id: e.id || i })) });
onTemplatePreloadConsumed();
}, [templatePreload]);

Check warning on line 123 in app/src/components/MuscleMap.jsx

View workflow job for this annotation

GitHub Actions / Lint, test and audit

React Hook useEffect has a missing dependency: 'onTemplatePreloadConsumed'. Either include it or remove the dependency array. If 'onTemplatePreloadConsumed' changes too often, find the parent component that defines it and wrap that definition in useCallback

const stepIndex = { upload: 0, analyzing: 0, confirm: 1, muscles: 2 }[step] ?? 0;
const headingRef = useRef();
useEffect(() => { headingRef.current?.focus(); }, [step]);
const exerciseMuscleMap = useMemo(() => buildMuscleMapFromExercises(exercises), [exercises]);

const addImage = useCallback(async (file) => {
Expand Down Expand Up @@ -214,14 +216,16 @@
currentView={currentView}
>
<div style={{ paddingBottom: 32 }}>
<SectionLabel>LOGG ØKT</SectionLabel>
<PageHeading style={{ marginBottom: 20 }}>{STEP_HEADINGS[step]}</PageHeading>
<div style={{ display: "flex", marginBottom: 28 }}>
<div ref={headingRef} tabIndex={-1} style={{ outline: "none" }}>
<SectionLabel>LOGG ØKT</SectionLabel>
<PageHeading style={{ marginBottom: 20 }}>{STEP_HEADINGS[step]}</PageHeading>
</div>
<div role="list" aria-label="Fremgang" style={{ display: "flex", marginBottom: 28 }}>
{STEP_LABELS.map((label, idx) => {
const isComplete = stepIndex > idx;
const isActive = stepIndex === idx;
return (
<div key={idx} style={{
<div key={idx} role="listitem" aria-current={isActive ? "step" : undefined} style={{
flex: 1,
borderTop: (isActive || isComplete) ? "2px solid #0f62fe" : "1px solid #393939",
paddingTop: 8,
Expand Down Expand Up @@ -336,16 +340,18 @@
style={{ display: "none" }}
onChange={(e) => handleFiles(e.target.files)} />

{error && (
<InlineNotification
kind="error"
title="Feil:"
subtitle={error}
hideCloseButton

style={{ marginBottom: 14 }}
/>
)}
<div aria-live="polite" aria-atomic="true">
{error && (
<InlineNotification
kind="error"
title="Feil:"
subtitle={error}
hideCloseButton

style={{ marginBottom: 14 }}
/>
)}
</div>

<Button
kind="secondary"
Expand Down
8 changes: 6 additions & 2 deletions app/src/components/Report.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,11 @@ export default function Report({ onShowHome, onShowLogger, onShowHistory, onShow
</div>
)}

<div aria-live="polite" aria-atomic="true">
{loading ? (
<InlineLoading description="Laster rapport…" status="active" style={{ marginTop: 24 }} />
) : error ? (
<p style={{ color: "var(--cds-support-error)", fontSize: 14 }}>{error}</p>
<p role="alert" style={{ color: "var(--cds-support-error)", fontSize: 14 }}>{error}</p>
) : (
<>
<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 1, marginBottom: 20 }}>
Expand Down Expand Up @@ -443,6 +444,7 @@ export default function Report({ onShowHome, onShowLogger, onShowHistory, onShow
{loadingRecs ? "Henter anbefalinger…" : "Få anbefaling"}
</Button>

<div aria-live="polite" aria-atomic="true">
{loadingRecs && (
<InlineLoading description="Analyserer treningsdata…" status="active" style={{ marginTop: 12 }} />
)}
Expand All @@ -453,10 +455,11 @@ export default function Report({ onShowHome, onShowLogger, onShowHistory, onShow
title="Feil:"
subtitle={recsError}
hideCloseButton

style={{ marginTop: 12 }}
/>
)}
</div>

{recs && recs.length > 0 && (() => {
const recPrimary = [...new Set(recs.flatMap(r => r.primary || []))];
Expand Down Expand Up @@ -531,6 +534,7 @@ export default function Report({ onShowHome, onShowLogger, onShowHistory, onShow
)}
</>
)}
</div>
</div>
</PageShell>
);
Expand Down
Loading