Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: scaffold exercise history with chart #180

Merged
merged 1 commit into from
Aug 19, 2022
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"release:alpha": "npm run release -- --prerelease alpha",
"rmds": "find . -name '.DS_Store' -delete",
"prepare": "husky install",
"commit": "cz"
"commit": "git add -A && cz"
},
"bin": {
"gains": "./src/bin/gains.js"
Expand Down
20 changes: 19 additions & 1 deletion src/apps/api/v1/exercises/exercises.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ import * as ExercisesQueries from './exercises.queries.js';
import * as ExerciseCategoriesQueries from '../exercise-categories/exercise-categories.queries.js';
import { omit } from 'lodash-es';

/**
* It gets the exercise history for a given exercise id
* @param req - The request object.
* @param res - The response object.
* @returns The exercise history for a specific exercise.
*/
export async function getExerciseHistory(req, res) {
const exercise_id = req.params.exercise_id;
const exercise = await ExercisesQueries.getExerciseHistoryByExerciseId(exercise_id);

return res.status(StatusCodes.OK).json({
status: 'success',
request_url: req.originalUrl,
message: 'The resource was returned successfully!',
data: exercise,
});
}

/**
* It gets an exercise by its id
* @param req - The request object.
Expand All @@ -18,7 +36,7 @@ export async function getExercise(req, res) {

const exercise = await ExercisesQueries.getExerciseById(eid);

if (!exercise.length) throw new CustomError.BadRequestError(`There are no exercise available for exercise id ${eid}!`); // prettier-ignore
// if (!exercise.length) throw new CustomError.BadRequestError(`There are no exercise available for exercise id ${eid}!`); // prettier-ignore

return res.status(StatusCodes.OK).json({
status: 'success',
Expand Down
37 changes: 37 additions & 0 deletions src/apps/api/v1/exercises/exercises.queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,43 @@ export function getExerciseById(id) {
return db.select('*').from('exercises').where({ id }).andWhere({ deleted: false });
}

/**
* Get all the sets for a given exercise, ordered by the date they were created.
* @param id - the id of the exercise you want to get the history for
* @returns An array of objects
*/
export async function getExerciseHistoryByExerciseId(id) {
const { rows } = await db.raw(
`
select
s.reps,
s.weight,
s.rpe as "rpe",
s.notes as "notes",
s.created_at as "created_at",
e."name" as "exercise_name",
ec."name" as "category_name",
e.id as "exercise_id",
ec.id as "category_id",
s.id as "set_id",
s.session_id as "session_id",
s.log_id as "log_id",
e.user_id as "user_id"
from
exercises e
inner join sets s on s.exercise_id = e.id
inner join exercise_categories ec on ec.id = e.exercise_category_id
where
e.deleted = false
and e.id = ?
order by
s.created_at desc
`,
[id],
);
return rows;
}

/**
* Get all exercises from the database where the user_id matches the uid passed in and where the
* deleted column is false.
Expand Down
12 changes: 12 additions & 0 deletions src/apps/api/v1/exercises/exercises.router.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ exercises.get(
catchAsyncErrors(ExercisesController.getExercise),
);

/**
* GET /api/v1/exercises/{exercise_id}/history
* @tags exercises
* @summary get history of a exercises
* @param {number} exercise_id.path.required - the exercise id - application/x-www-form-urlencoded
*/
exercises.get(
'/:exercise_id/history',
validator(ExercisesValidation.getExerciseHistory),
catchAsyncErrors(ExercisesController.getExerciseHistory),
);

/**
* POST /api/v1/exercises
* @tags exercises
Expand Down
27 changes: 26 additions & 1 deletion src/apps/api/v1/exercises/exercises.validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export const getExercise = [
.isNumeric()
.withMessage('exercise id must be an number!')
.bail()
.toInt()
.custom(async (eid) => {
const exercise = await ExercisesQueries.getExerciseById(eid);
if (exercise.length === 0) throw new Error('exercise does not exist!');
return true;
})
.toInt(),
];

Expand Down Expand Up @@ -47,7 +53,8 @@ export const getExercises = [
const user = await ExerciseCategoriesQueries.getExerciseCategoriesById(ecid);
if (user.length === 0) throw new Error('category_id does not exist!');
return true;
}),
})
.toInt(),
];

export const postExercise = [
Expand Down Expand Up @@ -147,3 +154,21 @@ export const patchExerciseNote = [
return true;
}),
];

export const getExerciseHistory = [
param('exercise_id')
.trim()
.notEmpty()
.withMessage('exercise id must not be empty!')
.bail()
.isNumeric()
.withMessage('exercise id must be an number!')
.bail()
.toInt()
.custom(async (exercise_id) => {
const exercise = await ExercisesQueries.getExerciseById(exercise_id);
if (exercise.length === 0) throw new Error('exercise does not exist!');
return true;
})
.toInt(),
];
18 changes: 15 additions & 3 deletions src/apps/ui/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<template>
<component :is="layout"
:class="{ 'dashboard-layout-style': layout === 'DashboardLayout' || layout === 'EmptyDashboardLayout' }" />
<component
:is="layout"
:class="{
'dashboard-layout-style': layout === 'DashboardLayout' || layout === 'EmptyDashboardLayout',
}"
/>
</template>

<script>
Expand All @@ -25,7 +29,7 @@ export default {
if (to.meta.layout === 'DashboardLayout' || to.meta.layout === 'EmptyDashboardLayout') {
document.body.style.backgroundColor = 'black';
} else {
document.body.style.backgroundColor = ''
document.body.style.backgroundColor = '';
}

// set layout by route meta
Expand All @@ -40,6 +44,14 @@ export default {
</script>

<style>
.btn-group-xs > .btn,
.btn-xs {
padding: 0.25rem 0.4rem;
font-size: 0.875rem;
line-height: 0.5;
border-radius: 0.2rem;
}

.dashboard-layout-style {
max-width: 540px;
margin: 0 auto;
Expand Down
81 changes: 48 additions & 33 deletions src/apps/ui/components/dashboard/SessionDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1026,56 +1026,64 @@ function clearAndDismissDeleteALogModal() {
<!-- block -->
<span v-if="currentSessionDetails.block_name">
<i class="bi bi-clipboard2-data-fill me-1"></i>Block:
<span class="fw-light">{{ currentSessionDetails.block_name }}</span>
<router-link
:to="`/dashboard/blocks/${currentSessionDetails.block_id}`"
class="fw-light fst-italic link-dark"
>{{ currentSessionDetails.block_name }}</router-link
>
</span>
</small>
</div>
</div>
</div>

<!-- footer -->
<small class="card-footer text-muted d-flex justify-content-between align-items-center">
<!-- date -->
<!-- left -->
<small>
<font-awesome-icon icon="fa-calendar " class="me-1" /><span class="fw-light">{{
gainsDateDisplay(currentSessionDetails.created_at)
}}</span>
</small>

<!-- right -->
<span class="d-flex justify-content-between align-items-center gap-2">
<!-- incomplete or progress -->
<small
v-if="!currentSessionDetails.end_date"
class="fst-italic d-flex align-items-center"
>
<!-- danger -->
<!-- prettier-ignore -->
<div class="card-footer">
<span class="d-flex justify-content-between text-muted">
<!-- left -->
<span class="d-flex justify-content-between align-items-center gap-2">
<!-- date -->
<small>
<i class="bi bi-calendar-check me-1"></i
>{{ dayjs(currentSessionDetails.created_at).format('YYYY/MM/DD') }}
</small>
</span>

<!-- right -->
<span class="d-flex justify-content-between align-items-center gap-2">
<!-- incomplete or progress -->
<span
v-if="!currentSessionDetails.end_date"
class="fst-italic d-flex align-items-center"
>
<!-- danger -->
<!-- prettier-ignore -->
<small
v-if="currentSessionDetails.end_date === null && dayjs(currentSessionDetails.start_date).format('YYYY/MM/DD') === today"
class="text-warning">
<font-awesome-icon icon="fa-refresh" class="me-1" /> in progress
</span>
</small>

<!-- danger -->
<!-- prettier-ignore -->
<span
<!-- danger -->
<!-- prettier-ignore -->
<small
v-if="currentSessionDetails.end_date === null && dayjs(currentSessionDetails.start_date).format('YYYY/MM/DD') < today"
class="text-danger">
<i class="bi bi-exclamation-triangle-fill text-danger"></i> incomplete
</span>
</small>
</span>

<!-- videos view -->
<router-link
class="fw-light link-secondary text-decoration-none"
:to="`/dashboard/videos/${currentSessionDetails.id}`"
>
<small> <i class="bi bi-play-circle-fill"></i> Video view </small>
</router-link>
<!-- videos view -->
<small>
<router-link
class="link-secondary text-decoration-none"
:to="`/dashboard/videos/${currentSessionDetails.id}`"
><i class="bi bi-play-circle-fill me-1"></i>Video view
</router-link>
</small>
</span>
</span>
</small>
</div>
</div>

<!-- exercise logs -->
Expand Down Expand Up @@ -1383,10 +1391,17 @@ function clearAndDismissDeleteALogModal() {

<!-- right -->
<span class="d-flex justify-content-between gap-2">
<!-- chart -->
<button class="btn btn-sm btn-outline-dark" type="button" disabled>
<i class="bi bi-bar-chart"></i>
</button>
<button class="btn btn-sm btn-outline-dark" type="button" disabled>

<!-- history -->
<button
@click="router.push(`/dashboard/exercises/${log.exercise_id}`)"
class="btn btn-sm btn-outline-dark"
type="button"
>
<i class="bi bi-journal-text"></i>
</button>
</span>
Expand Down
49 changes: 27 additions & 22 deletions src/apps/ui/components/dashboard/VideoDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -294,38 +294,43 @@ async function postAComment() {

<!-- footer -->
<div class="card-footer">
<small class="d-flex justify-content-between text-muted">
<span class="d-flex justify-content-between text-muted">
<!-- left -->
<span class="d-flex gap-3">
<!-- block -->
<span v-if="currentSessionDetails.block_name">
<i class="bi bi-clipboard2-data me-1"></i>Block:
<span class="fw-light">{{ currentSessionDetails.block_name }}</span>
</span>

<span class="d-flex align-items-center gap-2">
<!-- date -->
<span><i class="bi bi-calendar-check me-1"></i> 2020-01-01</span>
<small
><i class="bi bi-calendar-check me-1"></i
>{{ dayjs(currentSessionDetails.created_at).format('YYYY/MM/DD') }}</small
>

<!-- block -->
<small v-if="currentSessionDetails.block_name">
<router-link
:to="`/dashboard/blocks/${currentSessionDetails.block_id}`"
class="link-secondary text-decoration-none"
>
<i class="bi bi-journal-text me-1"></i>{{ currentSessionDetails.block_name }}
</router-link>
</small>
</span>

<!-- right -->
<div class="d-flex gap-2">
<div class="d-flex align-items-center gap-2">
<!-- session -->
<router-link
v-if="userStore.user.id === currentSessionDetails.user_id"
class="link-secondary text-decoration-none"
:to="`/dashboard/sessions/${currentSessionDetails.id}`"
><i class="bi bi-journal-text me-1"> </i>
<span>
<small>
<router-link
v-if="userStore.user.id === currentSessionDetails.user_id"
class="link-secondary text-decoration-none"
:to="`/dashboard/sessions/${currentSessionDetails.id}`"
><i class="bi bi-journal-text"></i>
{{ currentSessionDetails.id }}
</span>
</router-link>
</router-link>
</small>

<!-- comment -->
<span
><i class="bi bi-chat me-1"></i><span>{{ comments.length || 0 }}</span></span
>
<small><i class="bi bi-chat me-1"></i>{{ comments.length || 0 }}</small>
</div>
</small>
</span>
</div>
</div>

Expand Down
2 changes: 1 addition & 1 deletion src/apps/ui/components/shared/Signup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
and our
<a
href="#"
class="btn btn-sm p-0 m-0"
class="btn btn-xs p-0 m-0"
style="text-decoration: underline"
:class="{ disabled: loading === true }"
@click="$router.push('/privacy')"
Expand Down
20 changes: 20 additions & 0 deletions src/apps/ui/pages/dashboard/Block.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup>
import Backheader from '../../components/dashboard/headers/Backheader.vue';
</script>

<template>
<!-- header -->
<Backheader />

<div class="container px-3 animate__animated animate__fadeIn animate__faster">
<div class="my-3 d-flex flex-column gap-3">
<div class="card">
<div class="card-body">
<h5 class="card-title">Block</h5>
</div>
</div>
</div>
</div>
</template>

<style scoped></style>
Loading