master
Flycro 2023-06-13 11:39:18 +02:00
commit 872c089e4b
27 changed files with 10246 additions and 0 deletions

10
.eslintrc Normal file
View File

@ -0,0 +1,10 @@
{
"extends": "@antfu",
"ignorePatterns": ["!pages/public"],
"rules": {
"vue/no-restricted-syntax":["error", {
"selector": "VElement[name='a']",
"message": "Use NuxtLink instead."
}]
}
}

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# Nuxt dev/build outputs
.output
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example

1
.npmrc Normal file
View File

@ -0,0 +1 @@
shamefully-hoist=true

7
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"prettier.enable": false,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}

63
README.md Normal file
View File

@ -0,0 +1,63 @@
# Nuxt 3 Minimal Starter
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install the dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm run dev
# yarn
yarn dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm run build
# yarn
yarn build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm run preview
# yarn
yarn preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

5
app.vue Normal file
View File

@ -0,0 +1,5 @@
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>

View File

@ -0,0 +1,129 @@
<script setup>
import Chart from 'chart.js/auto'
const props = defineProps({
muscle: {
type: String,
required: true,
},
exercise: {
type: String,
required: true,
},
fromDate: {
type: String,
required: true,
},
toDate: {
type: String,
required: true,
},
})
const weightInput = useWeightInputStore()
// Convert fromDate and toDate to Date objects
const fromDate = computed(() => new Date(props.fromDate))
const toDate = computed(() => new Date(props.toDate))
// Retrieve exercise data based on the date range
const exerciseData = weightInput.exercises
// Filter the exercise data within the specified date range
const filteredData = computed(() =>
Object.entries(exerciseData)
.filter(([date]) => {
const currentDate = new Date(date)
return currentDate >= fromDate.value && currentDate <= toDate.value
})
.map(([, muscleData]) => muscleData[props.muscle]?.[props.exercise])
.filter(Boolean),
)
// Prepare the chart data
const chartData = computed(() =>
filteredData.value.map((exercise, index) => {
const date = new Date(Object.keys(exerciseData)[index])
const formattedDate = `${date.getMonth() + 1}/${date.getDate()}`
const maxWarmUpSetWeight = Math.max(
...exercise.warmUpSet.map(set => set.warmSetWeight),
)
const maxWorkingSetWeight = Math.max(
...exercise.workingSet.map(set => set.workingSetWeight),
)
return {
date: formattedDate,
warmUpSet: isNaN(maxWarmUpSetWeight) ? 0 : maxWarmUpSetWeight,
workingSet: isNaN(maxWorkingSetWeight) ? 0 : maxWorkingSetWeight,
}
}),
)
const labels = computed(() => chartData.value.map(data => data.date))
const warmUpSetWeights = computed(() => chartData.value.map(data => data.warmUpSet))
const workingSetWeights = computed(() => chartData.value.map(data => data.workingSet))
const chartOptions = computed(() => ({
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
suggestedMax: Math.max(
Math.max(...warmUpSetWeights.value),
Math.max(...workingSetWeights.value),
),
},
},
}))
const chartConfig = computed(() => ({
type: 'line',
data: {
labels: labels.value,
datasets: [
{
label: 'Warm-Up Sets',
data: warmUpSetWeights.value,
fill: false,
borderColor: 'rgba(75, 192, 192, 1)',
},
{
label: 'Working Sets',
data: workingSetWeights.value,
fill: false,
borderColor: 'rgba(192, 75, 192, 1)',
},
],
},
options: chartOptions.value,
}))
const exerciseChart = ref(null)
let chartInstance = null
onMounted(() => {
const ctx = exerciseChart.value.getContext('2d')
chartInstance = new Chart(ctx, chartConfig.value)
})
watch([filteredData, chartOptions], () => {
if (chartInstance) {
chartInstance.data.labels = labels.value
chartInstance.data.datasets[0].data = warmUpSetWeights.value
chartInstance.data.datasets[1].data = workingSetWeights.value
chartInstance.options.scales.y.suggestedMax = Math.max(
Math.max(...warmUpSetWeights.value),
Math.max(...workingSetWeights.value),
)
chartInstance.update()
}
})
</script>
<template>
<div>
<canvas ref="exerciseChart" :style="{ height: '400px', width: '600px' }" />
</div>
</template>

105
components/ExerciseList.vue Normal file
View File

@ -0,0 +1,105 @@
<script setup>
const props = defineProps({
muscle: String,
})
const exerciseStore = useExerciseStore()
const weightInput = useWeightInputStore()
const exercises = ref([])
const selectedExercise = ref(null)
const selectedDate = ref(new Date().toISOString().substr(0, 10))
onMounted(() => {
exercises.value = exerciseStore.getExercisesByMuscle(props.muscle)
},
)
// filter exercises with help from https://blog.logrocket.com/create-search-bar-vue/ last accessed 05.05.2023
const input = ref('')
function filterExercises() {
return exercises.value.filter((exercise) => {
return exercise.name.toLowerCase().includes(input.value.toLowerCase())
})
}
function isActiveExercise(exercise) {
// TODO: Implement (exercise in weightInput.exercises) to show which exercise are edited, or put them in a proper seperate list
return { 'exercise-list-button exerciseItem': selectedExercise.value !== exercise, 'exercise-list-button-active exerciseItem': selectedExercise.value === exercise }
}
function exerciseClick(exercise) {
selectedExercise.value = exercise.name
}
function initSetInput() {
weightInput.initSetsInputs(selectedDate.value, props.muscle, selectedExercise.value)
}
</script>
<template>
<div class="flex flex-col">
<div class="flex">
<div class="w-1/4">
<input
v-model="input"
class="text-black py-2 px-2 mt-5 rounded-md mb-4 w-full"
type="text"
placeholder="Search..."
>
</div>
<div class="w-3/4 flex justify-end">
<input
id="date"
v-model="selectedDate"
type="date"
class="text-black py-2 px-2 mt-5 rounded-md mb-4 w-2/4"
>
</div>
</div>
<div>
<div class="flex flex-col">
<div class="flex flex-row">
<div class="w-1/4">
<button v-for="exercise in filterExercises()" :key="exercise.name" :class="isActiveExercise(exercise.name)" @click="exerciseClick(exercise); initSetInput()">
{{ exercise.name }}
</button>
</div>
<div v-if="selectedExercise" class="w-3/4">
<WeightForm :date="selectedDate" :muscle="muscle" :selected-exercise="selectedExercise" />
</div>
</div>
</div>
</div>
</div>
</template>
<style>
.exercise-list-button {
@apply bg-green-500 hover:bg-green-400 text-white font-bold py-2 px-2 rounded my-1 w-full;
}
.exercise-list-button-active {
@apply bg-green-600 text-white font-bold py-2 px-2 rounded mb-1 w-full;
}
.weights{
display: flex;
flex-direction: row;
gap: 0.25rem;
margin-top: 0.5rem;
margin-left: 5rem;
color: white;
}
.set{
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 0.25rem;
margin-top: 0.5rem;
color: white;
}
</style>

81
components/NavBar.vue Normal file
View File

@ -0,0 +1,81 @@
<script setup lang="ts">
const isNavOpen = ref(false)
const exerciseStore = useExerciseStore()
const route = useRoute()
const muscleGroups = exerciseStore.getAllMuscles()
</script>
<template>
<div class="min-h-full">
<div class="pb-32 bg-gray-800">
<nav class="bg-gray-800">
<div class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
<div class="relative flex h-16 items-center justify-between">
<div class="absolute inset-y-0 left-0 flex items-center sm:hidden">
<!-- Mobile menu button -->
<button type="button" class="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" aria-controls="mobile-menu" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<!--
Icon when menu is closed.
Menu open: "hidden", Menu closed: "block"
-->
<svg v-show="isNavOpen === false" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" @click="isNavOpen = true">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
<!--
Icon when menu is open.
Menu open: "block", Menu closed: "hidden"
-->
<svg v-show="isNavOpen === true" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" @click="isNavOpen = false;">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
<div class="flex flex-shrink-0 items-center">
<img class="block h-8 w-auto lg:hidden" src="#" alt="Logo">
<img class="hidden h-8 w-auto lg:block" src="#" alt="Logo">
</div>
<div class="hidden sm:ml-6 sm:block">
<div class="flex space-x-4">
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
<NuxtLink to="/" active-class="bg-gray-900 text-white" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium" aria-current="page">
Dashboard
</NuxtLink>
<NuxtLink v-for="muscle in muscleGroups" :key="muscle" :to="`/muscles/${muscle.toLowerCase()}`" active-class="bg-gray-900 text-white" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">
{{ muscle }}
</NuxtLink>
</div>
</div>
</div>
</div>
</div>
<!-- Mobile menu, show/hide based on menu state. -->
<div v-if="isNavOpen" id="mobile-menu" class="sm:hidden">
<div class="space-y-1 px-2 pb-3 pt-2">
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
<a href="#" class="bg-gray-900 text-white block rounded-md px-3 py-2 text-base font-medium" aria-current="page">Dashboard</a>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Team</a>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Projects</a>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Calendar</a>
</div>
</div>
</nav>
<header class="py-10 bg-gray-800">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold tracking-tight text-white">
{{ route.meta.name }}
</h1>
</div>
</header>
</div>
</div>
</template>
<style scoped>
</style>

111
components/WeightForm.vue Normal file
View File

@ -0,0 +1,111 @@
<script setup lang="ts">
import { defineProps, ref } from 'vue'
const props = defineProps({
date: String,
muscle: String,
selectedExercise: String,
})
const weightInput = useWeightInputStore()
weightInput.selectedExercise = ref(props.selectedExercise)
const addWorkingSet = () => weightInput.addWorkingSet(props.date, props.muscle, props.selectedExercise)
const removeWorkingSet = () => weightInput.removeWorkingSet(props.date, props.muscle, props.selectedExercise)
const getWorkingSetCount = weightInput.getWorkingSetCount(props.date, props.muscle, props.selectedExercise)
const addWarmUpSet = () => weightInput.addWarmUpSet(props.date, props.muscle, props.selectedExercise)
const removeWarmUpSet = () => weightInput.removeWarmUpSet(props.date, props.muscle, props.selectedExercise)
const getWarmUpSetCount = weightInput.getWarmUpSetCount(props.date, props.muscle, props.selectedExercise)
function restrictToNumbers(event) {
const charCode = event.which ? event.which : event.keyCode
if (charCode > 31 && (charCode < 48 || charCode > 57))
event.preventDefault()
}
</script>
<template>
<div class="w-2/3 mx-auto">
<div class="mt-2">
<label>Warm-Up Sets</label>
<button
class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-2 rounded ml-5"
@click="addWarmUpSet"
>
Add
</button>
<button
:disabled="getWarmUpSetCount <= 1"
class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-2 rounded ml-5 disabled:opacity-25"
@click="removeWarmUpSet"
>
Remove
</button>
<div
v-for="(warmUpSet, warmUpSetCount) in weightInput.exercises[date][muscle][selectedExercise].warmUpSet"
:key="warmUpSetCount"
class="flex ml-2"
>
<label>Set {{ warmUpSetCount + 1 }}</label>
<div class="ml-3">
<input
v-model="warmUpSet.warmSetWeight"
type="number"
class="mt-1 px-3 py-2 border shadow-sm weightInput.exercises[ focus:outline-none block rounded-md sm:text-sm"
placeholder="Weight (kg)"
@keypress="restrictToNumbers"
>
</div>
<div class="ml-3">
<input
v-model="warmUpSet.warmSetReps"
type="number"
class="mt-1 px-3 py-2 border shadow-sm weightInput.exercises[ focus:outline-none block rounded-md sm:text-sm"
placeholder="Reps"
@keypress="restrictToNumbers"
>
</div>
</div>
</div>
<div class="working-set mt-5">
<label>Working Sets</label>
<button
class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-2 rounded ml-7"
@click="addWorkingSet"
>
Add
</button>
<button
:disabled="getWorkingSetCount <= 1"
class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-2 rounded ml-5 disabled:opacity-25"
@click="removeWorkingSet"
>
Remove
</button>
<div v-for="(workingset, workingSetCount) in weightInput.exercises[date][muscle][selectedExercise].workingSet" :key="workingSetCount" class="item flex justify-smart mt-1">
<label class="">{{ workingSetCount + 1 }}. Set</label>
<div class="ml-3">
<input
v-model="workingset.workingSetWeight"
type="number"
class="mt-1 px-3 py-2 border shadow-sm weightInput.exercises[ focus:outline-none block rounded-md sm:text-sm"
placeholder="Weight (Kg)"
@keypress="restrictToNumbers"
>
</div>
<div class="ml-3">
<input
v-model="workingset.workingSetReps"
type="number"
class="mt-1 px-3 py-2 border shadow-sm focus:outline-none block rounded-md sm:text-sm"
placeholder="Reps"
@keypress="restrictToNumbers"
>
</div>
</div>
</div>
</div>
</template>

797
composables/exercise.ts Normal file
View File

@ -0,0 +1,797 @@
import { defineStore } from 'pinia'
export interface Exercise {
name: string
}
export interface MuscleGroup {
name: string
exercises: Exercise[]
}
export interface Workout {
muscle: MuscleGroup[]
}
const workout: Workout = {
muscle: [
{
name: 'Legs',
exercises: [
{
name: 'Barbell Back Squat',
},
{
name: 'Barbell Front Squat',
},
{
name: 'Barbell Split squat',
},
{
name: 'Dumbbell Split Squat',
},
{
name: 'Barbell Lunge',
},
{
name: 'Barbell Bulgarien split Squat',
},
{
name: 'Barbell Reverse Lunge',
},
{
name: 'Dumbbell Bulgarien split squat',
},
{
name: 'Dumbbell Lunges',
},
{
name: 'Dumbbell Walking Lunges',
},
{
name: 'Dumbbell Reverse Lunge',
},
{
name: 'Dumbbell Goblet Squat',
},
{
name: 'Dumbbell Step Ups',
},
{
name: 'Hack Squat',
},
{
name: 'Leg Press',
},
{
name: 'Leg Extension',
},
{
name: 'Leg Curl',
},
{
name: 'Barbell Romanin Deadlift',
},
{
name: 'Barbell Stiff leg Deadlift',
},
{
name: 'Dumbell Romanin Deadlift',
},
{
name: 'Dumbell Stiff leg Deadlift',
},
{
name: 'Decline Dumbbell leg curl',
},
{
name: 'Leg Ham Raises',
},
{
name: 'Seated Leg Curl',
},
{
name: 'Seated Single Leg Curl',
},
{
name: 'Laying Leg Curl',
},
{
name: 'Laying single leg leg Curl',
},
{
name: 'Cable Laying Leg Curl',
},
{
name: 'Cable Single Leg Romanian Deadlift',
},
{
name: 'Back Extension (Ham Focused)',
},
{
name: 'Barbell Hip Thrusts',
},
{
name: 'Dumbbell Hip Thusts',
},
{
name: 'Dumbbell leaning Forward Step up',
},
{
name: 'Dumbbell Single leg Hip thusts',
},
{
name: 'Cable Standing Cuff Kickback',
},
{
name: 'Cable Pullthroughs',
},
{
name: 'Cable Standing Side raises',
},
{
name: 'Hip Abduction',
},
{
name: 'Kettkebelt Swings',
},
{
name: 'Glue hamm Raise',
},
{
name: 'Back Extension (Glute Focused)',
},
{
name: 'Single leg Hip Thusts',
},
{
name: 'Plate Hip Thrusts',
},
{
name: 'Single leg Plate Hip thusts',
},
{
name: 'Hip Adduction',
},
{
name: 'Hip Dumbbell Gobleg Adductor Lunges',
},
{
name: 'Calf Raise',
},
{
name: 'Single Leg Standing Dumbbell Calf Raises',
},
{
name: 'Standing Dumbbell Calf Raises',
},
{
name: 'Seated Dunbbell calrf Extensions',
},
{
name: 'Seated Calf Raises',
},
{
name: 'Staning Calf Raises',
},
{
name: 'Donkey Calf Raises',
},
{
name: 'Leg Press Calf Raises',
},
{
name: 'Smith Maschine Donkey Calf Raises',
},
],
},
{
name: 'Back',
exercises: [
{
name: 'Bentover Barbell Row',
},
{
name: 'Standing T Bar Row',
},
{
name: 'Chest Supported Barbell Row',
},
{
name: 'Landmine 1 Arm Row',
},
{
name: 'Dumbbell Tripod Row',
},
{
name: 'Dumbbell Pullover (Lats)',
},
{
name: 'Chest Supported Dumbbell row',
},
{
name: 'Lat Focused Dumbbell Row',
},
{
name: 'Rocking Pulldown',
},
{
name: 'Close Grip Undergand Pulldown',
},
{
name: 'Lap Pulldown',
},
{
name: 'One Arm High Calbe Row',
},
{
name: 'Seated One Arm High Cable row',
},
{
name: 'Incline Cable Single Arm Stretch',
},
{
name: 'Cable Straight Arm Pushdown Bar',
},
{
name: 'Cable Straight Arm Pusdown Rope',
},
{
name: 'Single Arm Straight Arm Pushdown',
},
{
name: 'Seated Lat Focused Row',
},
{
name: 'Seated Lat Focused One Arm Row',
},
{
name: 'Kneeling one Arm Lat Focused Row',
},
{
name: 'Kneeling Lat Pullin',
},
{
name: 'Seated Chest supported lat row',
},
{
name: 'Pull ups',
},
{
name: 'Asissted Pull ups',
},
{
name: 'Lat Pulldown',
},
{
name: 'Chin ups',
},
{
name: 'Asissted Pull ups',
},
{
name: 'Austrailan Pull ups',
},
{
name: 'Bentover Barbell row',
},
{
name: 'Chest Supported Barbell Row',
},
{
name: 'Medow Row',
},
{
name: 'Chest Supported Dumbbell Row',
},
{
name: 'Dumbbell One Arm Row',
},
{
name: 'Bentover Dumbbell Row',
},
{
name: 'Dumbbell Helms Row',
},
{
name: 'Seated Row (Wide Grip)',
},
{
name: 'Seated Row',
},
{
name: 'Seated chest supported Row',
},
{
name: 'Deadlift',
},
{
name: 'Sumo Deadlift',
},
{
name: 'Dead Row',
},
{
name: 'Barbell Good Morning',
},
{
name: 'Dumbell Good Morning',
},
{
name: 'Dumbell Superman',
},
{
name: 'Back Extensions',
},
{
name: 'Superman',
},
{
name: 'Good Morning',
},
{
name: 'Rack Pull',
},
{
name: 'Barbell Shrugs',
},
{
name: 'Trap bar Shrugs',
},
{
name: 'Trap Bar Carry',
},
{
name: 'Dumbbell Shrugs',
},
{
name: 'Dumbbell Carry',
},
{
name: 'Dumbell Seated Shrug',
},
{
name: 'Cable Shrugs',
},
{
name: 'Plate Shurugs',
},
{
name: 'Plate Carry',
},
{
name: 'Cable Wrap Around Row',
},
{
name: 'Cable Rope Pull',
},
{
name: 'Prone Reverse Fly',
},
{
name: 'Cable FacePull Press',
},
{
name: 'Prone Y Rise',
},
{
name: 'Prone Press',
},
{
name: 'Plate Raise',
},
],
},
{
name: 'Chest',
exercises: [
{
name: 'Incline Barbell Bench Press',
},
{
name: 'Kneeling Landmine Press',
},
{
name: 'Flat Underhand Barbell Bench Press',
},
{
name: 'Landmine Rainbow',
},
{
name: 'Paused Incline Barbell Bench Press',
},
{
name: 'Incline Dumbbell Bench Press',
},
{
name: 'Dumbbell Pullover (Chest)',
},
{
name: 'DB UCV Raise',
},
{
name: 'Incline Dumbbell Squeeze Press',
},
{
name: 'Flat Underhand Dumbbell Bench Press',
},
{
name: 'Paused Incline Dumbbell Bench Press',
},
{
name: 'Sranding Low to High Cable Fly',
},
{
name: 'Seated Low to High Cable Fly',
},
{
name: 'Incline Cable Press',
},
{
name: 'Standing Low to High Crossover',
},
{
name: 'Seated Low to High Crossover',
},
{
name: 'Dual Cable UCV Raise',
},
{
name: 'Decline Pushup',
},
{
name: 'Slight Decline Bench Pess',
},
{
name: 'Barbell Bench Press',
},
{
name: 'Close Grip Barbell Bench Press',
},
{
name: 'Slight Delcline Dumbbell Press',
},
{
name: 'Dumbbell Bench Press',
},
{
name: 'Paused Dumbbell Bench Press',
},
{
name: 'Dumbbel Floor Fly',
},
{
name: 'Seated Cable Fly',
},
{
name: 'Seated Cable Press',
},
{
name: 'Seated Cable Crossover',
},
{
name: 'Standing Calbe Press',
},
{
name: 'Standing Cable Crossover',
},
{
name: 'Staning Cable Fly',
},
{
name: 'Standing Calbe Press',
},
{
name: 'Standing Cable Crossover',
},
{
name: 'Pushup',
},
{
name: 'Push away Pushup',
},
{
name: 'Plate loaded Chest press',
},
{
name: 'Assisted Chest press',
},
{
name: 'Pec Dec (Chest)',
},
{
name: 'Assisted Dip',
},
{
name: 'Dip',
},
{
name: 'Decline Barbell Bench Press',
},
{
name: 'Barbell Dips',
},
{
name: 'Decline Dumbbell Bench Press',
},
{
name: 'Decline Dumbbell Flys Supernated',
},
{
name: 'Decline Dumbbell Pullover',
},
{
name: 'High to low Cable flys',
},
{
name: 'High to low Cable Press',
},
{
name: 'High to low Crossover',
},
{
name: 'Standing Cable LC Press',
},
{
name: 'Kneeling X Press',
},
{
name: 'Decline Cable Dip',
},
{
name: 'Jack Hammer Pushdown',
},
],
},
{
name: 'Shoulders',
exercises: [
{
name: 'Barbell Overhead Press',
},
{
name: 'Seated Barbell Overhead Press',
},
{
name: 'Barbell Front raises',
},
{
name: 'Kneeling Landmine Press',
},
{
name: 'Smithmashine Shoulder Press',
},
{
name: 'Dumbell Overhead Press',
},
{
name: 'Seated Dumbell Overhead Press',
},
{
name: 'Dumbell Arnold Press',
},
{
name: 'Seated Arnold Press',
},
{
name: 'Dumbbell Front Raises (pronated Grip)',
},
{
name: 'Dumbbell Front Raises (Hammer Grip)',
},
{
name: 'Dumbbell Front Raises (Supernated Grip BEST)',
},
{
name: 'Seated Dumbell Press (Supernated Grip',
},
{
name: 'Staning Dumbell Press (Supernated Grip)',
},
{
name: 'Dumbbel Scoop Press',
},
{
name: 'Cable Front Riase (Pronated Grip)',
},
{
name: 'Cable Front Riase (Supernated Grip BEST)',
},
{
name: 'Cable Strch Front Raises',
},
{
name: 'Plate Bus Drivers',
},
{
name: 'Handstand Pushup',
},
{
name: 'Pike Pushup',
},
{
name: 'Seated Dumbbell Lateral Raise',
},
{
name: 'Standing Dumbbell Lateral Raise',
},
{
name: 'Dumbbell Cheat Lateral Raise',
},
{
name: 'Lying Incline Lateral Raise',
},
{
name: 'Incline Dumbbell Parsel Side Lateral Raise',
},
{
name: 'Cable Behind Body Lateral Raise ',
},
{
name: 'Cable Lateral Raise',
},
{
name: 'Cable Lean-Away Lateral Raise',
},
{
name: 'Egyptian Lateral Raise',
},
{
name: 'Standing Cable Y Raise',
},
{
name: 'Maschine Side Lateral Raises',
},
{
name: 'Standing Barbell Rear Delt Row',
},
{
name: 'Chest Supported Barbell Rear Delt Row',
},
{
name: 'Behinde the Back Barbell Raise',
},
{
name: 'Dumbbell Rear Delt Row',
},
{
name: 'Dumbbell Chest Supported Rear Delt Swing',
},
{
name: 'Dumbbell Chest Supported Rear Delt Row',
},
{
name: 'Dumbbell Reverse Flys',
},
{
name: 'Dumbbll Hip Hugger',
},
{
name: 'Abdduction Row',
},
{
name: 'Incline Behind the Back Dumbbell Raise',
},
{
name: 'Incline Dumbbell Rear Delt Fly',
},
{
name: 'Seated Cable Rear Delt Row',
},
{
name: 'Rear Delt Cable Pull',
},
{
name: 'Double Arm Reverse Cabel Fly',
},
{
name: 'Singke Arm Reverse Cable Fly',
},
{
name: 'Standing Face Pull',
},
{
name: 'Kneeling Face Pull',
},
{
name: 'Laying Face Pull',
},
{
name: 'Reverse Pec Deck',
},
{
name: 'Maschine Side Lateral Rear Delt Fly',
},
],
},
{
name: 'Biceps',
exercises: [
{
name: 'Dumbbell Concentration Curl (Pronated Grip)',
},
{
name: 'Crossbody Cable Curl',
},
{
name: 'Dumbbell Concentration Curl (Hammer Grip)',
},
{
name: 'Barbell Reverse Grip Curl',
},
{
name: 'Ez Bar Reverse Grip Curl',
},
{
name: 'Dumbbell Reverse Grip',
},
{
name: 'Barbell Curl',
},
{
name: 'Ez bar Curl',
},
{
name: 'Standing Dumbbell Curl',
},
{
name: 'Seated Cable Curl',
},
{
name: 'High Cable Curl',
},
],
},
{
name: 'Triceps',
exercises: [
{
name: 'Rope Cable Pushdown (infront Body)',
},
{
name: 'Straight Bart Cable Pushdown (underhand Grip)',
},
{
name: 'Dumbbell Overhead Extension',
},
{
name: 'Dip',
},
{
name: 'Rope Cable Pushdown (behind body)',
},
{
name: 'Cable Straight Bar Pushdown',
},
{
name: 'Barbell Dip',
},
{
name: 'Close Grip Pushup',
},
{
name: 'Close Grip Barbell Bench Press',
},
],
},
],
}
export const useExerciseStore = defineStore('exercise', () => {
const exerciseList = ref<Workout>(workout)
function getAllMuscles(): string[] {
return exerciseList.value.muscle.map(muscle => muscle.name)
}
function getExercisesByMuscle(muscle: string): Exercise[] {
return exerciseList.value.muscle.find(m => m.name === muscle)?.exercises ?? []
}
return {
exerciseList,
getAllMuscles,
getExercisesByMuscle,
}
},
)

119
composables/weightinput.ts Normal file
View File

@ -0,0 +1,119 @@
import { defineStore } from 'pinia'
import { useLocalStorage } from '@vueuse/core'
interface ExerciseData {
workingSet: { workingSetReps: number[]; workingSetWeight: number[] }[]
warmUpSet: { warmSetReps: number[]; warmSetWeight: number[] }[]
}
interface MuscleExercises {
[muscle: string]: {
[selectedExercise: string]: ExerciseData
}
}
interface DateExercises {
[date: string]: MuscleExercises
}
export const useWeightInputStore = defineStore('weightInput', () => {
const exercises = useLocalStorage<DateExercises>('exercises', {})
function addWorkingSet(date: string, muscle: string, selectedExercise: string) {
if (!exercises.value[date])
exercises.value[date] = {}
if (!exercises.value[date][muscle])
exercises.value[date][muscle] = {}
if (!exercises.value[date][muscle][selectedExercise]) {
exercises.value[date][muscle][selectedExercise] = {
workingSet: [],
warmUpSet: [],
}
}
exercises.value[date][muscle][selectedExercise].workingSet.push({
workingSetReps: [],
workingSetWeight: [],
})
console.log('Added working set:', exercises.value[date][muscle][selectedExercise])
}
function addWarmUpSet(date: string, muscle: string, selectedExercise: string) {
if (!exercises.value[date])
exercises.value[date] = {}
if (!exercises.value[date][muscle])
exercises.value[date][muscle] = {}
if (!exercises.value[date][muscle][selectedExercise]) {
exercises.value[date][muscle][selectedExercise] = {
workingSet: [],
warmUpSet: [],
}
}
exercises.value[date][muscle][selectedExercise].warmUpSet.push({
warmSetReps: [],
warmSetWeight: [],
})
}
function removeWorkingSet(date: string, muscle: string, selectedExercise: string) {
if (!exercises.value[date]?.[muscle]?.[selectedExercise])
return
exercises.value[date][muscle][selectedExercise].workingSet.pop()
}
function removeWarmUpSet(date: string, muscle: string, selectedExercise: string) {
if (!exercises.value[date]?.[muscle]?.[selectedExercise])
return
exercises.value[date][muscle][selectedExercise].warmUpSet.pop()
}
function getWorkingSetCount(date: string, muscle: string, selectedExercise: string) {
if (exercises.value[date]?.[muscle]?.[selectedExercise])
return exercises.value[date][muscle][selectedExercise].workingSet.length
return 1
}
function getWarmUpSetCount(date: string, muscle: string, selectedExercise: string) {
if (exercises.value[date]?.[muscle]?.[selectedExercise])
return exercises.value[date][muscle][selectedExercise].warmUpSet.length
return 1
}
function initSetsInputs(date: string, muscle: string, selectedExercise: string) {
if (!exercises.value[date])
exercises.value[date] = {}
if (!exercises.value[date][muscle])
exercises.value[date][muscle] = {}
if (!exercises.value[date][muscle][selectedExercise]) {
exercises.value[date][muscle][selectedExercise] = {
workingSet: [],
warmUpSet: [],
}
}
console.log('Init sets inputs:', exercises.value[date][muscle][selectedExercise], selectedExercise)
}
return {
exercises,
addWorkingSet,
removeWorkingSet,
addWarmUpSet,
removeWarmUpSet,
getWorkingSetCount,
getWarmUpSetCount,
initSetsInputs,
}
})

8
layouts/default.vue Normal file
View File

@ -0,0 +1,8 @@
<template>
<NavBar />
<div class="min-h-screen bg-white-500 mt-4">
<main class="-mt-32 mx-auto max-w-7xl px-4 pb-12 sm:px-6 lg:px-8 bg-white min-h-full rounded-md shadow-md p-4">
<slot />
</main>
</div>
</template>

11
nuxt.config.ts Normal file
View File

@ -0,0 +1,11 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
ssr: false,
modules: [
'@nuxtjs/tailwindcss',
'@vueuse/nuxt',
'@pinia/nuxt',
'@nuxtjs/eslint-module',
],
devtools: { enabled: true },
})

27
package.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "nuxt-app",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"devDependencies": {
"@antfu/eslint-config": "^0.39.5",
"@nuxt/devtools": "latest",
"@nuxtjs/eslint-module": "^4.1.0",
"@nuxtjs/tailwindcss": "^6.7.0",
"@tailwindcss/forms": "^0.5.3",
"@types/node": "^18",
"eslint": "^8.42.0",
"nuxt": "^3.5.2"
},
"dependencies": {
"@pinia/nuxt": "^0.4.11",
"@vueuse/nuxt": "^10.1.2",
"chart.js": "^4.3.0",
"pinia": "^2.1.3"
}
}

29
pages/index.vue Normal file
View File

@ -0,0 +1,29 @@
<script setup lang="ts">
definePageMeta({
title: 'Dashboard',
name: 'Dashboard',
})
const fromDate = ref(new Date(new Date().setDate(new Date().getDate() - 1)).toISOString().substr(0, 10))
const toDate = ref(new Date().toISOString().substr(0, 10))
</script>
<template>
<div class="w-full flex justify-end">
<input
v-model="fromDate"
type="date"
class="text-black py-2 px-2 mt-5 rounded-md mb-4 w-2/4 mr-4"
>
<input
v-model="toDate"
type="date"
class="text-black py-2 px-2 mt-5 rounded-md mb-4 w-2/4"
>
</div>
<ExerciseChart :from-date="fromDate" :to-date="toDate" muscle="Legs" exercise="Barbell Back Squat" />
</template>
<style scoped>
</style>

16
pages/muscles/back.vue Normal file
View File

@ -0,0 +1,16 @@
<script setup lang="ts">
definePageMeta({
title: 'Back',
name: 'Back',
})
</script>
<template>
<div class="">
asdf
</div>
</template>
<style scoped>
</style>

16
pages/muscles/biceps.vue Normal file
View File

@ -0,0 +1,16 @@
<script setup lang="ts">
definePageMeta({
title: 'Biceps',
name: 'Biceps',
})
</script>
<template>
<div class="">
asdf
</div>
</template>
<style scoped>
</style>

16
pages/muscles/chest.vue Normal file
View File

@ -0,0 +1,16 @@
<script setup lang="ts">
definePageMeta({
title: 'Chest',
name: 'Chest',
})
</script>
<template>
<div class="">
asdf
</div>
</template>
<style scoped>
</style>

14
pages/muscles/legs.vue Normal file
View File

@ -0,0 +1,14 @@
<script setup lang="ts">
definePageMeta({
title: 'Legs',
name: 'Legs',
})
</script>
<template>
<ExerciseList muscle="Legs" />
</template>
<style scoped>
</style>

View File

@ -0,0 +1,16 @@
<script setup lang="ts">
definePageMeta({
title: 'Shoulders',
name: 'Shoulders',
})
</script>
<template>
<div class="">
asdf
</div>
</template>
<style scoped>
</style>

16
pages/muscles/triceps.vue Normal file
View File

@ -0,0 +1,16 @@
<script setup lang="ts">
definePageMeta({
title: 'Triceps',
name: 'Triceps',
})
</script>
<template>
<div class="">
asdf
</div>
</template>
<style scoped>
</style>

8599
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

3
server/tsconfig.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}

20
tailwind.config.js Normal file
View File

@ -0,0 +1,20 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./components/**/*.{js,vue,ts}',
'./composables/**/*.{js,vue,ts}',
'./layouts/**/*.vue',
'./pages/**/*.vue',
'./plugins/**/*.{js,ts}',
'./nuxt.config.{js,ts}',
'./App.{js,ts,vue}',
'./app.{js,ts,vue}',
],
darkMode: 'class',
theme: {
extend: {},
},
plugins: [
require('@tailwindcss/forms'),
],
}

4
tsconfig.json Normal file
View File

@ -0,0 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}