Skip to content

Commit

Permalink
feat: added the ability to import and export exercise splits
Browse files Browse the repository at this point in the history
  • Loading branch information
WhyAsh5114 committed Jan 23, 2025
1 parent 5e554ec commit e4d1a40
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/routes/exercise-splits/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<DropdownMenu.Group>
<DropdownMenu.Item onclick={createNewExerciseSplit}>Start from scratch</DropdownMenu.Item>
<DropdownMenu.Item href="/exercise-splits/templates">Use template</DropdownMenu.Item>
<DropdownMenu.Item href="/exercise-splits/import">Import</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Root>
Expand Down
31 changes: 31 additions & 0 deletions src/routes/exercise-splits/[exerciseSplitId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import DeleteIcon from 'virtual:icons/lucide/trash';
import EditIcon from 'virtual:icons/lucide/pencil';
import LoaderCircle from 'virtual:icons/lucide/loader-circle';
import FileUpIcon from 'virtual:icons/lucide/file-up';
import { onMount } from 'svelte';
import { toast } from 'svelte-sonner';
import ExerciseSplitSkeleton from './(components)/ExerciseSplitSkeleton.svelte';
Expand Down Expand Up @@ -82,6 +83,33 @@
exerciseSplitRunes.loadExerciseSplit(getExerciseSplitWithoutIds(exerciseSplit), exerciseSplit.id);
goto(`/exercise-splits/manage/structure`);
}
function exportSplit() {
if (exerciseSplit === 'loading') return;
const { userId, id, ...rest } = exerciseSplit;
(rest.exerciseSplitDays as unknown) = rest.exerciseSplitDays.map((day) => {
const { id, exerciseSplitId, ...rest } = day;
(rest.exercises as unknown) = rest.exercises.map((ex) => {
const { id, exerciseSplitDayId, ...rest } = ex;
return rest;
});
return rest;
});
const jsonData = JSON.stringify(rest);
const blob = new Blob([jsonData], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${exerciseSplit.name}.json`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
</script>

<H2 showChartIcon={selectedTabValue === 'exercises'} bind:chartMode>View exercise split</H2>
Expand Down Expand Up @@ -111,6 +139,9 @@
<DropdownMenu.Item class="gap-2" onclick={() => loadExerciseSplit('clone')}>
<CloneIcon /> Clone
</DropdownMenu.Item>
<DropdownMenu.Item class="gap-2" onclick={() => exportSplit()}>
<FileUpIcon /> Export
</DropdownMenu.Item>
<DropdownMenu.Item class="gap-2 text-red-500" onclick={() => (deleteConfirmDrawerOpen = true)}>
<DeleteIcon /> Delete
</DropdownMenu.Item>
Expand Down
62 changes: 62 additions & 0 deletions src/routes/exercise-splits/import/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import H2 from '$lib/components/ui/typography/H2.svelte';
import H3 from '$lib/components/ui/typography/H3.svelte';
import {
ExerciseSplitCreateWithoutUserInputSchema,
ExerciseSplitDayCreateWithoutExerciseSplitInputSchema,
ExerciseTemplateCreateWithoutExerciseSplitDayInputSchema
} from '$lib/zodSchemas';
import { toast } from 'svelte-sonner';
import { exerciseSplitRunes } from '../manage/exerciseSplitRunes.svelte';
import { goto } from '$app/navigation';
let splitFile = $state<File>();
async function validateAndImportSplit() {
if (!splitFile) return;
try {
const splitData = JSON.parse(await splitFile.text());
const { exerciseSplitDays, ...split } = splitData;
ExerciseSplitCreateWithoutUserInputSchema.parse(split);
exerciseSplitDays.forEach((data: { [x: string]: unknown; exercises: unknown[] }) => {
const { exercises, ...splitDay } = data;
ExerciseSplitDayCreateWithoutExerciseSplitInputSchema.parse(splitDay);
exercises.forEach((data) => {
ExerciseTemplateCreateWithoutExerciseSplitDayInputSchema.parse(data);
});
});
exerciseSplitRunes.loadExerciseSplit(splitData);
goto('/exercise-splits/manage/structure');
} catch (error) {
if (error instanceof Error) {
toast.error(error.message);
}
}
}
</script>

<H2>Exercise splits</H2>
<H3>Import</H3>

<div class="grid w-full items-center gap-1.5">
<Label for="picture">Exercise split JSON file</Label>
<Input
id="picture"
type="file"
accept=".json"
onchange={(e) => {
const file = e.currentTarget.files?.item(0);
if (file) splitFile = file;
}}
/>
</div>

<Button class="mt-auto" onclick={validateAndImportSplit}>Import</Button>
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,11 @@
deleteExercise={mesocycleExerciseSplitRunes.deleteExercise}
{reordering}
setEditingExercise={mesocycleExerciseSplitRunes.setEditingExercise}
bind:itemList={mesocycleExerciseSplitRunes.splitExercises[
mesocycleExerciseSplitRunes.selectedSplitDayIndex
] as MesocycleExerciseTemplateWithoutIdsOrIndex[]}
bind:itemList={
mesocycleExerciseSplitRunes.splitExercises[
mesocycleExerciseSplitRunes.selectedSplitDayIndex
] as MesocycleExerciseTemplateWithoutIdsOrIndex[]
}
/>
</div>
</div>
Expand Down

0 comments on commit e4d1a40

Please sign in to comment.