mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-09 15:57:59 -05:00
[backend] added support for Yoga [frontend] added support for Yoga [frontend] added "Me" badge if user is logged user in to user list in user settings page [frontend] added missing activity types "Rowing" and "Yoga" to edit activity modal [frontend] Gear now only shows inactive state in gears list [docs]
299 lines
14 KiB
Vue
299 lines
14 KiB
Vue
<template>
|
|
<h1>{{ $t("gearsView.title") }}</h1>
|
|
<div class="row row-gap-3">
|
|
<div class="col-lg-4 col-md-12">
|
|
<!-- Add gear zone -->
|
|
<p>{{ $t("gearsView.buttonAddGear") }}:</p>
|
|
<a class="w-100 btn btn-primary" href="#" role="button" data-bs-toggle="modal" data-bs-target="#addGearModal">
|
|
{{ $t("gearsView.buttonAddGear") }}
|
|
</a>
|
|
|
|
<!-- Add gear modal -->
|
|
<div class="modal fade" id="addGearModal" tabindex="-1" aria-labelledby="addGearModal" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h1 class="modal-title fs-5" id="addGearModal">{{ $t("gearsView.buttonAddGear") }}</h1>
|
|
<!--<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>-->
|
|
</div>
|
|
<form @submit.prevent="submitAddGearForm">
|
|
<div class="modal-body">
|
|
<!-- brand fields -->
|
|
<label for="gearBrandAdd"><b>{{ $t("gearsView.modalBrand") }}:</b></label>
|
|
<input class="form-control" type="text" name="gearBrandAdd" :placeholder='$t("gearsView.modalBrand")' v-model="brand" maxlength="250">
|
|
<!-- model fields -->
|
|
<label for="gearModelAdd"><b>{{ $t("gearsView.modalModel") }}:</b></label>
|
|
<input class="form-control" type="text" name="gearModelAdd" :placeholder='$t("gearsView.modalModel")' v-model="model" maxlength="250">
|
|
<!-- nickname fields -->
|
|
<label for="gearNicknameAdd"><b>* {{ $t("gearsView.modalNickname") }}:</b></label>
|
|
<input class="form-control" type="text" name="gearNicknameAdd" :placeholder='$t("gearsView.modalNickname")' v-model="nickname" maxlength="250" required>
|
|
<!-- gear type fields -->
|
|
<label for="gearTypeAdd"><b>* {{ $t("gearsView.modalGearTypeLabel") }}:</b></label>
|
|
<select class="form-control" name="gearTypeAdd" v-model="gearType" required>
|
|
<option value="1">{{ $t("gearsView.modalGearTypeOption1Bike") }}</option>
|
|
<option value="2">{{ $t("gearsView.modalGearTypeOption2Shoes") }}</option>
|
|
<option value="3">{{ $t("gearsView.modalGearTypeOption3Wetsuit") }}</option>
|
|
</select>
|
|
<!-- date fields -->
|
|
<label for="gearDateAdd"><b>* {{ $t("gearsView.modalDateLabel") }}:</b></label>
|
|
<input class="form-control" type="date" name="gearDateAdd" :placeholder='$t("gearsView.modalDatePlaceholder")' v-model="date" required>
|
|
<p>* {{ $t("generalItems.requiredField") }}</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ $t("generalItems.buttonClose") }}</button>
|
|
<button type="submit" class="btn btn-success" name="addGear" data-bs-dismiss="modal">{{ $t("gearsView.buttonAddGear") }}</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search gear by nickname zone -->
|
|
<br>
|
|
<p class="mt-2">{{ $t("gearsView.subTitleSearchGearByNickname") }}:</p>
|
|
<form>
|
|
<div class="mb-3">
|
|
<input class="form-control" type="text" name="gearNickname" :placeholder='$t("gearsView.placeholderSearchGearByNickname")' v-model="searchNickname" required>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="col">
|
|
<div v-if="isLoading">
|
|
<LoadingComponent />
|
|
</div>
|
|
<div v-else>
|
|
<!-- Checking if userGears is loaded and has length -->
|
|
<div v-if="userGears && userGears.length">
|
|
<!-- Iterating over userGears to display them -->
|
|
<p>{{ $t("gearsView.displayUserNumberOfGears1") }}{{ userGearsNumber }}{{ $t("gearsView.displayUserNumberOfGears2") }}{{ userGears.length }}{{ $t("gearsView.displayUserNumberOfGears3") }}</p>
|
|
|
|
<!-- Displaying loading new gear if applicable -->
|
|
<ul class="list-group list-group-flush" v-if="isLoadingNewGear">
|
|
<li class="list-group-item rounded">
|
|
<LoadingComponent />
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Displaying loading if gears are updating -->
|
|
<LoadingComponent v-if="isGearsUpdatingLoading"/>
|
|
|
|
<!-- List gears -->
|
|
<ul class="list-group list-group-flush" v-for="gear in userGears" :key="gear.id" :gear="gear" v-else>
|
|
<li class="list-group-item d-flex justify-content-between">
|
|
<div class="d-flex align-items-center">
|
|
<img src="/src/assets/avatar/bicycle1.png" alt="Bycicle avatar" width="55" height="55" v-if="gear.gear_type == 1">
|
|
<img src="/src/assets/avatar/running_shoe1.png" alt="Bycicle avatar" width="55" height="55" v-else-if="gear.gear_type == 2">
|
|
<img src="/src/assets/avatar/wetsuit1.png" alt="Bycicle avatar" width="55" height="55" v-else>
|
|
<div class="ms-3">
|
|
<div class="fw-bold">
|
|
<router-link :to="{ name: 'gear', params: { id: gear.id }}" class="link-body-emphasis link-underline-opacity-0 link-underline-opacity-100-hover">
|
|
{{ gear.nickname }}
|
|
</router-link>
|
|
</div>
|
|
<b>{{ $t("gearsView.gearTypeLabel") }}</b>
|
|
<span v-if="gear.gear_type == 1">{{ $t("gearsView.gearTypeOption1") }}</span>
|
|
<span v-else-if="gear.gear_type == 2">{{ $t("gearsView.gearTypeOption2") }}</span>
|
|
<span v-else>{{ $t("gearsView.gearTypeOption3") }}</span>
|
|
<br>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<!--<span class="badge bg-success-subtle border border-success-subtle text-success-emphasis align-middle" v-if="gear.is_active == 1">{{ $t("gearsView.activeState") }}</span>-->
|
|
<span class="badge bg-danger-subtle border border-danger-subtle text-danger-emphasis align-middle" v-if="gear.is_active == 0">{{ $t("gearsView.inactiveState") }}</span>
|
|
<span class="badge bg-primary-subtle border border-primary-subtle text-primary-emphasis align-middle ms-2" v-if="gear.strava_gear_id"><font-awesome-icon :icon="['fab', 'fa-strava']" /> {{ $t("gearsView.gearFromStrava") }}</span>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- pagination area -->
|
|
<PaginationComponent :totalPages="totalPages" :pageNumber="pageNumber" @pageNumberChanged="setPageNumber" v-if="!searchNickname"/>
|
|
</div>
|
|
<!-- Displaying a message or component when there are no activities -->
|
|
<NoItemsFoundComponent v-else />
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
<!-- back button -->
|
|
<BackButtonComponent />
|
|
</template>
|
|
|
|
<script>
|
|
// Importing the vue composition API
|
|
import { ref, onMounted, watch } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useRoute } from 'vue-router';
|
|
// import lodash
|
|
import { debounce } from 'lodash';
|
|
// Import Notivue push
|
|
import { push } from "notivue";
|
|
// Importing the components
|
|
import NoItemsFoundComponent from '@/components/GeneralComponents/NoItemsFoundComponents.vue';
|
|
import LoadingComponent from '@/components/GeneralComponents/LoadingComponent.vue';
|
|
import BackButtonComponent from '@/components/GeneralComponents/BackButtonComponent.vue';
|
|
import PaginationComponent from '@/components/GeneralComponents/PaginationComponent.vue';
|
|
// Importing the services
|
|
import { gears } from '@/services/gearsService';
|
|
|
|
//import { Modal } from 'bootstrap';
|
|
|
|
export default {
|
|
components: {
|
|
NoItemsFoundComponent,
|
|
LoadingComponent,
|
|
PaginationComponent,
|
|
BackButtonComponent,
|
|
},
|
|
setup() {
|
|
const { t } = useI18n();
|
|
const route = useRoute();
|
|
const brand = ref('');
|
|
const model = ref('');
|
|
const nickname = ref('');
|
|
const gearType = ref(1);
|
|
const date = ref(null);
|
|
const isLoading = ref(true);
|
|
const isGearsUpdatingLoading = ref(true);
|
|
const isLoadingNewGear = ref(false)
|
|
const userGears = ref([]);
|
|
const userGearsNumber = ref(0);
|
|
const pageNumber = ref(1);
|
|
const totalPages = ref(1);
|
|
const numRecords = 5;
|
|
const searchNickname = ref('');
|
|
|
|
const performSearch = debounce(async () => {
|
|
// If the search nickname is empty, reset the list to initial state.
|
|
if (!searchNickname.value) {
|
|
// Reset the list to the initial state when search text is cleared
|
|
pageNumber.value = 1;
|
|
// Fetch gears
|
|
await fetchGears();
|
|
// Exit the function
|
|
return;
|
|
}
|
|
try {
|
|
// Fetch the users based on the search nickname.
|
|
userGears.value = await gears.getGearByNickname(searchNickname.value);
|
|
} catch (error) {
|
|
push.error(`${t("gearsView.errorGearNotFound")} - ${error}`);
|
|
}
|
|
}, 500);
|
|
|
|
async function submitAddGearForm() {
|
|
// Set the isLoadingNewGear variable to true.
|
|
isLoadingNewGear.value = true;
|
|
try {
|
|
// Create the gear data object.
|
|
const data = {
|
|
brand: brand.value,
|
|
model: model.value,
|
|
nickname: nickname.value,
|
|
gear_type: gearType.value,
|
|
created_at: date.value,
|
|
};
|
|
|
|
// Create the gear and get the created gear id.
|
|
const createdGearId = await gears.createGear(data);
|
|
|
|
// Get the created gear and add it to the userGears array.
|
|
const newGear = await gears.getGearById(createdGearId);
|
|
userGears.value.unshift(newGear);
|
|
|
|
userGearsNumber.value++;
|
|
|
|
// Set the success message and show the success alert.
|
|
push.success(t("gearsView.successGearAdded"));
|
|
} catch (error) {
|
|
// If there is an error, set the error message and show the error alert.
|
|
push.error(`${t("generalItems.errorFetchingInfo")} - ${error}`);
|
|
} finally {
|
|
// Set the isLoadingNewGear variable to false.
|
|
isLoadingNewGear.value = false;
|
|
}
|
|
}
|
|
|
|
function setPageNumber(page) {
|
|
// Set the page number.
|
|
pageNumber.value = page;
|
|
}
|
|
|
|
async function updateGears() {
|
|
try {
|
|
// Set the loading variable to true.
|
|
isGearsUpdatingLoading.value = true;
|
|
|
|
// Fetch the gears with pagination.
|
|
userGears.value = await gears.getUserGearsWithPagination(pageNumber.value, numRecords);
|
|
|
|
// Set the loading variable to false.
|
|
isGearsUpdatingLoading.value = false;
|
|
} catch (error) {
|
|
// If there is an error, set the error message and show the error alert.
|
|
push.error(`${t("generalItems.errorFetchingInfo")} - ${error}`);
|
|
}
|
|
}
|
|
|
|
async function fetchGears() {
|
|
try {
|
|
// Get the total number of user gears.
|
|
userGearsNumber.value = await gears.getUserGearsNumber();
|
|
|
|
// Fetch the gears with pagination.
|
|
await updateGears();
|
|
|
|
// Update total pages
|
|
totalPages.value = Math.ceil(userGearsNumber.value / numRecords);
|
|
} catch (error) {
|
|
// If there is an error, set the error message and show the error alert.
|
|
push.error(`${t("generalItems.errorFetchingInfo")} - ${error}`);
|
|
}
|
|
}
|
|
|
|
onMounted(async () => {
|
|
if (route.query.gearDeleted === 'true') {
|
|
// Set the gearDeleted value to true and show the success alert.
|
|
push.success(t("gearsView.successGearDeleted"));
|
|
}
|
|
|
|
if (route.query.gearFound === 'false') {
|
|
// Set the gearFound value to false and show the error alert.
|
|
push.error(`${t("gearsView.errorGearNotFound")} - ${error}`);
|
|
}
|
|
|
|
// Fetch gears
|
|
await fetchGears();
|
|
|
|
// Set the isLoading variables to false.
|
|
isGearsUpdatingLoading.value = false;
|
|
isLoading.value = false;
|
|
});
|
|
|
|
// Watch the search nickname variable.
|
|
watch(searchNickname, performSearch, { immediate: false });
|
|
|
|
// Watch the page number variable.
|
|
watch(pageNumber, updateGears, { immediate: false });
|
|
|
|
return {
|
|
brand,
|
|
model,
|
|
nickname,
|
|
totalPages,
|
|
pageNumber,
|
|
numRecords,
|
|
gearType,
|
|
date,
|
|
isLoading,
|
|
isGearsUpdatingLoading,
|
|
isLoadingNewGear,
|
|
userGears,
|
|
userGearsNumber,
|
|
searchNickname,
|
|
t,
|
|
submitAddGearForm,
|
|
setPageNumber,
|
|
};
|
|
},
|
|
};
|
|
</script> |