Skip to content

Commit

Permalink
Merge pull request #163 from WhyAsh5114/156-workout-feedback-edit-bug
Browse files Browse the repository at this point in the history
Workout feedback edit bug
  • Loading branch information
WhyAsh5114 authored Dec 28, 2024
2 parents 56ad77b + 5b99d99 commit 15f8ae7
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 19 deletions.
3 changes: 2 additions & 1 deletion src/lib/trpc/routes/workouts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,8 @@ export const workouts = t.router({
userId: ctx.userId,
startedAt: input.data.workoutData.startedAt!,
endedAt: input.endedAt,
userBodyweight: input.data.workoutData.userBodyweight
userBodyweight: input.data.workoutData.userBodyweight,
note: input.data.workoutData.note
};

const workoutOfMesocycle = await prisma.workoutOfMesocycle.findFirst({
Expand Down
16 changes: 8 additions & 8 deletions src/routes/(components)/layout/ChangelogDialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@
import { checkForUpdates, needRefresh, updateDataLossDialog } from './PWAFunctions.svelte';
let open = $state(false);
let checkingForUpdate = $state(false);
let checkedForUpdate = $state(false);
let dialogText = $state<string>();
let releases = $state<{ tag_name: string; body: string }[]>([]);
onMount(async () => {
checkingForUpdate = true;
const response = await fetch('https://api.github.com/repos/WhyAsh5114/MyFit/releases');
releases = await response.json();
const latestRelease = releases[0];
Expand All @@ -27,26 +26,27 @@
changelogShownOf.localeCompare(latestRelease!.tag_name, undefined, { numeric: true }) === -1
) {
open = true;
loadChangelog(changelogShownOf);
await loadChangelog(changelogShownOf);
while (checkForUpdates === null) {
await new Promise((resolve) => setTimeout(resolve, 500));
}
await checkForUpdates();
}
ls.setItem('changelogShownOf', latestRelease!.tag_name);
checkingForUpdate = false;
checkedForUpdate = true;
});
async function loadChangelog(lastRelease: string) {
const notShownReleases = releases.filter(
(release) => release.tag_name.localeCompare(lastRelease, undefined, { numeric: true }) === 1
);
dialogText = '';
let text = '';
for (let i = 0; i < notShownReleases.length; i++) {
dialogText += DOMPurify.sanitize(await marked.parse(releases[i].body));
text += DOMPurify.sanitize(await marked.parse(releases[i].body));
if (i !== notShownReleases.length - 1) dialogText += '<hr>';
}
dialogText = text;
}
</script>

Expand All @@ -58,14 +58,14 @@
</article>
</ScrollArea>
<Button
disabled={checkingForUpdate}
disabled={!checkedForUpdate}
class="gap-2"
onclick={() => {
if ($needRefresh) updateDataLossDialog.open = true;
else open = false;
}}
>
{#if checkingForUpdate}
{#if !checkedForUpdate}
Fetching update <LoaderCircle class="animate-spin" />
{:else if $needRefresh}
Update & reload <ReloadIcon />
Expand Down
127 changes: 118 additions & 9 deletions src/routes/exercise-stats/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
<script lang="ts">
import { invalidateAll } from '$app/navigation';
import { Button } from '$lib/components/ui/button/index.js';
import * as Command from '$lib/components/ui/command/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import { Label } from '$lib/components/ui/label/index.js';
import * as Popover from '$lib/components/ui/popover/index.js';
import * as Select from '$lib/components/ui/select';
import { RangeCalendar } from '$lib/components/ui/range-calendar/index.js';
import H2 from '$lib/components/ui/typography/H2.svelte';
import { trpc } from '$lib/trpc/client.js';
import type { RouterOutputs } from '$lib/trpc/router';
import { onMount } from 'svelte';
import { dateToCalendarDate } from '$lib/utils';
import { cn } from '$lib/utils.js';
import { DateFormatter, getLocalTimeZone } from '@internationalized/date';
import type { DateRange, Selected } from 'bits-ui';
import { toast } from 'svelte-sonner';
import CalendarIcon from 'virtual:icons/lucide/calendar';
import ChevronsUpDown from 'virtual:icons/lucide/chevrons-up-down';
import FilterIcon from 'virtual:icons/lucide/filter';
import RenameIcon from 'virtual:icons/lucide/folder-pen';
import LoaderCircle from 'virtual:icons/lucide/loader-circle';
import WorkoutExerciseCard from '../workouts/[workoutId]/(components)/WorkoutExerciseCard.svelte';
Expand All @@ -18,9 +26,14 @@
type WorkoutExercise = RouterOutputs['workouts']['getExerciseHistory'][number];
type BasicExerciseData = Pick<WorkoutExercise, 'name' | 'targetMuscleGroup' | 'customMuscleGroup'>;
const df = new DateFormatter('en-US', {
dateStyle: 'medium'
});
let { data } = $props();
let exercisesByMuscleGroup = $state<{ group: string; exercises: BasicExerciseData[] }[]>();
let renameExerciseOpen = $state(false);
let newExerciseName = $state<string>();
let renamingExercise = $state(false);
Expand All @@ -38,21 +51,58 @@
})) ?? []
);
onMount(async () => {
let dateRange: DateRange = $state({
start: dateToCalendarDate(new Date()),
end: dateToCalendarDate(new Date())
});
let mesocycleNames = $derived.by(() => {
if (!exerciseInstances) return [];
return Array.from(new Set(exerciseInstances.map((ex) => ex.workout.workoutOfMesocycle?.mesocycle.name ?? null)));
});
let selectedMesocycleNames: Selected<string | null>[] = $state([]);
let filteredExerciseInstances = $derived.by(() => {
if (!exerciseInstances) return [];
return exerciseInstances.filter((ex) => {
const date = dateToCalendarDate(ex.workout.startedAt);
if (dateRange.start && dateRange.start > date) return false;
if (dateRange.end && dateRange.end < date) return false;
if (
selectedMesocycleNames.length > 0 &&
!selectedMesocycleNames.some((s) => s.value === (ex.workout.workoutOfMesocycle?.mesocycle.name ?? null))
)
return false;
return true;
});
});
$effect(() => {
selectedExercise = undefined;
exercisesByMuscleGroup = undefined;
searchText = '';
searchOpen = true;
exerciseInstances = [];
renameExerciseOpen = false;
loadExercises();
});
async function loadExercises() {
const exerciseList = await data.exerciseList;
exercisesByMuscleGroup = Object.entries(
Object.groupBy(exerciseList, (ex) => ex.customMuscleGroup ?? ex.targetMuscleGroup)
).map(([group, exercises]) => ({
group,
exercises: exercises!.filter((ex) => ex !== undefined)
}));
});
}
async function selectExercise(name: string) {
searchText = name;
searchOpen = false;
selectedExercise = name;
exerciseInstances = await trpc().workouts.getExerciseHistory.query({ exerciseName: name });
dateRange.start = dateToCalendarDate(exerciseInstances[exerciseInstances.length - 1].workout.startedAt);
dateRange.end = dateToCalendarDate(exerciseInstances[0].workout.startedAt);
}
async function renameExercise(e: SubmitEvent) {
Expand All @@ -62,7 +112,8 @@
oldName: selectedExercise!,
newName: newExerciseName!
});
toast.success(`Renamed ${count} exercises`, { description: 'Reload the page to see the changes' });
toast.success(`Renamed ${count} exercises`);
await invalidateAll();
renamingExercise = false;
}
</script>
Expand All @@ -72,8 +123,8 @@
<div class="flex gap-1">
<Popover.Root bind:open={searchOpen}>
<Popover.Trigger asChild let:builder>
<Button builders={[builder]} variant="outline" role="combobox" class="mb-2 w-full justify-between">
{selectedExercise ?? 'Search for an exercise'}
<Button builders={[builder]} variant="outline" role="combobox" class="mb-2 grow justify-between truncate">
<span class="truncate">{selectedExercise ?? 'Search for an exercise'}</span>
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</Popover.Trigger>
Expand Down Expand Up @@ -104,7 +155,7 @@
</Command.Root>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Root bind:open={renameExerciseOpen}>
<Popover.Trigger asChild let:builder>
<Button
builders={[builder]}
Expand Down Expand Up @@ -132,13 +183,71 @@
</form>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger asChild let:builder>
<Button
builders={[builder]}
size="icon"
aria-label="Filter exercises"
class="shrink-0"
disabled={(exerciseInstances?.length ?? 0) === 0}
>
<FilterIcon />
</Button>
</Popover.Trigger>
<Popover.Content class="w-fit">
<span class="font-semibold">Filter by date</span>
<div class="my-2 flex w-full max-w-sm flex-col gap-1.5">
<Popover.Root openFocus>
<Popover.Trigger asChild let:builder>
<Button
variant="outline"
class={cn('w-[300px] justify-start text-left font-normal', !dateRange && 'text-muted-foreground')}
builders={[builder]}
>
<CalendarIcon class="mr-2 h-4 w-4" />
{#if dateRange && dateRange.start}
{#if dateRange.end}
{df.format(dateRange.start.toDate(getLocalTimeZone()))} - {df.format(
dateRange.end.toDate(getLocalTimeZone())
)}
{:else}
{df.format(dateRange.start.toDate(getLocalTimeZone()))}
{/if}
{:else if dateRange.start}
{df.format(dateRange.start.toDate(getLocalTimeZone()))}
{:else}
Pick a date
{/if}
</Button>
</Popover.Trigger>
<Popover.Content class="w-auto p-0" align="start">
<RangeCalendar bind:value={dateRange} initialFocus placeholder={dateRange?.start} />
</Popover.Content>
</Popover.Root>
<span class="font-semibold">Filter by mesocycles</span>
<Select.Root multiple bind:selected={selectedMesocycleNames}>
<Select.Trigger class="w-[300px]">
<Select.Value placeholder="All mesocycles" />
</Select.Trigger>
<Select.Content>
{#each mesocycleNames as mesocycleName}
<Select.Item class={cn({ italic: mesocycleName === null })} value={mesocycleName}>
{mesocycleName === null ? 'Non-mesocycle' : mesocycleName}
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
</Popover.Content>
</Popover.Root>
</div>

<div class="flex flex-col gap-2">
{#if selectedExercise}
<ExerciseStatsChart {selectedExercise} exercises={exerciseInstances} />
<ExerciseStatsChart {selectedExercise} exercises={filteredExerciseInstances} />
{#if exerciseInstances}
{#each exerciseInstances as instance}
{#each filteredExerciseInstances as instance}
<WorkoutExerciseCard exercise={instance} date={new Date(instance.workout.startedAt)} />
{/each}
{/if}
Expand Down
2 changes: 1 addition & 1 deletion src/routes/workouts/manage/overview/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
</Tabs.Content>
</Tabs.Root>

<div class="mt-4 flex w-full max-w-sm flex-col gap-1.5">
<div class="mt-4 flex w-full flex-col gap-1.5">
<Label for="workout-note">Workout note</Label>
<Textarea id="workout-note" placeholder="Type here (optional)" bind:value={workoutNote}></Textarea>
</div>
Expand Down

0 comments on commit 15f8ae7

Please sign in to comment.