Edit user now uses unified modal

[frontend] edit user now uses unified modal
[frontend] fixed imperial<->metric conversions
This commit is contained in:
João Vitória Silva
2025-01-29 11:22:53 +00:00
parent 7bccf0b586
commit 731ae98201
6 changed files with 95 additions and 23 deletions

View File

@@ -139,7 +139,7 @@
</b>
<span v-if="authStore.user.height">
<span v-if="authStore.user.units == 1">{{ authStore.user.height }}{{ $t("generalItems.unitsCm") }}</span>
<span v-else>{{ cmToFeetInches(authStore.user.height) }} </span>
<span v-else>{{ feet }}{{ inches }}</span>
</span>
<span v-else>N/A</span>
</p>
@@ -196,6 +196,7 @@ export default {
const editUserPreferredLanguage = ref(authStore.user.preferred_language);
const editUserAccessType = ref(authStore.user.access_type);
const editUserPhotoPath = ref(authStore.user.photo_path);
const { feet, inches } = cmToFeetInches(authStore.user.height);
async function handleFileChange(event) {
editUserPhotoFile.value = event.target.files?.[0] ?? null;
@@ -287,7 +288,8 @@ export default {
submitEditUserForm,
submitDeleteUserPhoto,
handleFileChange,
cmToFeetInches,
feet,
inches,
};
},
};

View File

@@ -36,7 +36,7 @@
<!-- list zone -->
<ul class="list-group list-group-flush" v-for="user in usersArray" :key="user.id" :user="user" v-else>
<UsersListComponent :user="user" @userDeleted="updateUserList" />
<UsersListComponent :user="user" @userDeleted="updateUserList" @editedUser="editUserList"/>
</ul>
<!-- pagination area -->
@@ -157,11 +157,15 @@ export default {
}
function addUserList(createdUser) {
console.log(createdUser);
usersArray.value.unshift(createdUser);
usersNumber.value++;
}
function editUserList(editedUser) {
const index = usersArray.value.findIndex((user) => user.id === editedUser.id);
usersArray.value[index] = editedUser;
}
function setIsLoadingNewUser(state) {
isLoadingNewUser.value = state;
}
@@ -193,6 +197,7 @@ export default {
searchUsername,
updateUserList,
addUserList,
editUserList,
setIsLoadingNewUser,
};
},

View File

@@ -57,8 +57,25 @@
<option value="2">{{ $t("usersAddEditUserModalComponent.addEditUserModalUnitsOption2") }}</option>
</select>
<!-- height fields -->
<label for="userHeightAddEdit"><b>{{ $t("usersAddEditUserModalComponent.addEditUserModalHeightLabel") }} (cm)</b></label>
<input class="form-control" type="number" name="userHeightAddEdit" :placeholder='$t("usersAddEditUserModalComponent.addEditUserModalHeightPlaceholder") + " (cm)"' v-model="newEditUserHeight">
<div v-if="authStore.user.units == 1">
<label for="userHeightAddEditCms"><b>{{ $t("usersAddEditUserModalComponent.addEditUserModalHeightLabel") }} ({{ $t("generalItems.unitsCm") }})</b></label>
<input class="form-control" type="number" name="userHeightAddEditCms" :placeholder='$t("usersAddEditUserModalComponent.addEditUserModalHeightPlaceholder") + " (" + $t("generalItems.unitsCm") + ")"' v-model="newEditUserHeightCms">
</div>
<div v-else>
<label for="userHeightAddEditFeetInches"><b>{{ $t("usersAddEditUserModalComponent.addEditUserModalHeightLabel") }} ({{ $t("generalItems.unitsFeetInches") }})</b></label>
<div class="input-group">
<input class="form-control" :class="{ 'is-invalid': !isFeetValid }" type="number" aria-describedby="validationFeetFeedback" name="userHeightAddEditFeet" :placeholder='$t("usersAddEditUserModalComponent.addEditUserModalHeightPlaceholder") + " (" + $t("generalItems.unitsFeet") + ")"' v-model="newEditUserHeightFeet" min="0" max="10" step="1">
<span class="input-group-text"></span>
<input class="form-control" :class="{ 'is-invalid': !isInchesValid }" type="number" aria-describedby="validationInchesFeedback" name="userHeightAddEditInches" :placeholder='$t("usersAddEditUserModalComponent.addEditUserModalHeightPlaceholder") + " (" + $t("generalItems.unitsInches") + ")"' v-model="newEditUserHeightInches" min="0" max="11" step="1">
<span class="input-group-text"></span>
<div id="validationFeetFeedback" class="invalid-feedback" v-if="!isFeetValid">
{{ $t("usersAddEditUserModalComponent.addEditUserModalFeetValidationLabel") }}
</div>
<div id="validationInchesFeedback" class="invalid-feedback" v-if="!isInchesValid">
{{ $t("usersAddEditUserModalComponent.addEditUserModalInchesValidationLabel") }}
</div>
</div>
</div>
<!-- preferred language fields -->
<label for="userPreferredLanguageAddEdit"><b>* {{ $t("usersAddEditUserModalComponent.addEditUserModalUserPreferredLanguageLabel") }}</b></label>
<select class="form-control" name="userPreferredLanguageAddEdit" v-model="newEditUserPreferredLanguage" required>
@@ -85,8 +102,8 @@
</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="userAdd" data-bs-dismiss="modal" v-if="action == 'add'">{{ $t("usersAddEditUserModalComponent.addEditUserModalAddTitle") }}</button>
<button type="submit" class="btn btn-success" name="userEdit" data-bs-dismiss="modal" v-else>{{ $t("usersAddEditUserModalComponent.addEditUserModalEditTitle") }}</button>
<button type="submit" class="btn btn-success" name="userAdd" data-bs-dismiss="modal" v-if="action == 'add'" :disabled="!isPasswordValid">{{ $t("usersAddEditUserModalComponent.addEditUserModalAddTitle") }}</button>
<button type="submit" class="btn btn-success" name="userEdit" data-bs-dismiss="modal" v-else :disabled="!isFeetValid || !isInchesValid">{{ $t("usersAddEditUserModalComponent.addEditUserModalEditTitle") }}</button>
</div>
</form>
</div>
@@ -103,8 +120,8 @@ import { useAuthStore } from "@/stores/authStore";
import { push } from "notivue";
// Importing the services
import { users } from "@/services/usersService";
import { formatDateShort } from "@/utils/dateTimeUtils";
// Import units utils
import { cmToFeetInches, feetAndInchesToCm } from "@/utils/unitsUtils";
export default {
props: {
@@ -119,7 +136,8 @@ export default {
},
emits: ["userPhotoDeleted", "isLoadingNewUser", "createdUser", "editedUser"],
setup(props, { emit }) {
const { t } = useI18n();
const authStore = useAuthStore();
const { t, locale } = useI18n();
// edit user specific variables
const editUserId = ref("");
// edit and add user variables
@@ -131,7 +149,15 @@ export default {
const newEditUserBirthDate = ref(null);
const newEditUserGender = ref(1);
const newEditUserUnits = ref(1);
const newEditUserHeight = ref(null);
const newEditUserHeightCms = ref(null);
const newEditUserHeightFeet = ref(null);
const newEditUserHeightInches = ref(null);
const isFeetValid = computed(() => {
return newEditUserHeightFeet.value >= 0 && newEditUserHeightFeet.value <= 10;
});
const isInchesValid = computed(() => {
return newEditUserHeightInches.value >= 0 && newEditUserHeightInches.value <= 11;
});
const newEditUserPreferredLanguage = ref("us");
const newEditUserAccessType = ref(1);
const newEditUserIsActive = ref(1);
@@ -153,11 +179,16 @@ export default {
newEditUserBirthDate.value = props.user.birthdate;
newEditUserGender.value = props.user.gender;
newEditUserUnits.value = props.user.units;
newEditUserHeight.value = props.user.height;
newEditUserHeightCms.value = props.user.height;
newEditUserPreferredLanguage.value = props.user.preferred_language;
newEditUserAccessType.value = props.user.access_type;
newEditUserIsActive.value = props.user.is_active;
newEditUserPhotoPath.value = props.user.photo_path;
if (props.user.height) {
const { feet, inches } = cmToFeetInches(props.user.height);
newEditUserHeightFeet.value = feet;
newEditUserHeightInches.value = inches;
}
}
async function handleFileChange(event) {
@@ -195,7 +226,7 @@ export default {
preferred_language: newEditUserPreferredLanguage.value,
gender: newEditUserGender.value,
units: newEditUserUnits.value,
height: newEditUserHeight.value,
height: newEditUserHeightCms.value,
access_type: newEditUserAccessType.value,
photo_path: null,
is_active: newEditUserIsActive.value,
@@ -244,7 +275,7 @@ export default {
birthdate: newEditUserBirthDate.value,
gender: newEditUserGender.value,
units: newEditUserUnits.value,
height: newEditUserHeight.value,
height: newEditUserHeightCms.value,
preferred_language: newEditUserPreferredLanguage.value,
access_type: newEditUserAccessType.value,
photo_path: newEditUserPhotoPath.value,
@@ -268,6 +299,10 @@ export default {
emit("editedUser", data);
if (data.id === authStore.user.id) {
authStore.setUser(data, authStore.session_id, locale);
}
// Set the success message and show the success alert.
push.success(t("usersListComponent.userEditSuccessMessage"));
} catch (error) {
@@ -279,6 +314,19 @@ export default {
}
function handleSubmit() {
if (authStore.user.units === 1) {
if (newEditUserHeightCms.value !== props.user.height) {
const { feet, inches } = cmToFeetInches(newEditUserHeightCms.value);
newEditUserHeightFeet.value = feet;
newEditUserHeightInches.value = inches;
}
} else {
const { feet, inches } = cmToFeetInches(props.user.height);
if (feet !== newEditUserHeightFeet.value || inches !== newEditUserHeightInches.value) {
newEditUserHeightCms.value = feetAndInchesToCm(newEditUserHeightFeet.value, newEditUserHeightInches.value);
}
}
if (props.action === 'add') {
submitAddUserForm();
} else {
@@ -287,6 +335,7 @@ export default {
}
return {
authStore,
t,
editUserId,
newEditUserPhotoFile,
@@ -297,13 +346,17 @@ export default {
newEditUserBirthDate,
newEditUserGender,
newEditUserUnits,
newEditUserHeight,
newEditUserHeightCms,
newEditUserHeightFeet,
newEditUserHeightInches,
newEditUserPreferredLanguage,
newEditUserAccessType,
newEditUserIsActive,
newEditUserPhotoPath,
newUserPassword,
isPasswordValid,
isFeetValid,
isInchesValid,
submitDeleteUserPhoto,
handleFileChange,
handleSubmit,

View File

@@ -27,7 +27,7 @@
<!-- edit user button -->
<a class="btn btn-link btn-lg link-body-emphasis" href="#" role="button" data-bs-toggle="modal" :data-bs-target="`#editUserModal${user.id}`"><font-awesome-icon :icon="['fas', 'fa-pen-to-square']" /></a>
<UsersAddEditUserModalComponent :action="'edit'" :user="user" />
<UsersAddEditUserModalComponent :action="'edit'" :user="user" @editedUser="editUserList"/>
<!-- delete user button -->
<a class="btn btn-link btn-lg link-body-emphasis" href="#" role="button" data-bs-toggle="modal" :data-bs-target="`#deleteUserModal${user.id}`" v-if="authStore.user.id != user.id"><font-awesome-icon :icon="['fas', 'fa-trash-can']" /></a>
@@ -94,7 +94,7 @@ export default {
required: true,
},
},
emits: ["userDeleted"],
emits: ["userDeleted", "editedUser"],
setup(props, { emit }) {
const { t } = useI18n();
const authStore = useAuthStore();
@@ -189,6 +189,10 @@ export default {
}
}
function editUserList(editedUser) {
emit("editedUser", editedUser);
}
async function submitDeleteUserPhoto() {
try {
await users.deleteUserPhoto(userProp.value.id);
@@ -261,6 +265,7 @@ export default {
userSessions,
isLoading,
updateSessionListDeleted,
editUserList,
};
},
};

View File

@@ -22,6 +22,8 @@
"addEditUserModalUnitsOption2": "Imperial",
"addEditUserModalHeightLabel": "Height",
"addEditUserModalHeightPlaceholder": "Height",
"addEditUserModalFeetValidationLabel": "Invalid height. Please enter a valid height in feet.",
"addEditUserModalInchesValidationLabel": "Invalid height. Please enter a valid height in inches.",
"addEditUserModalUserPreferredLanguageLabel": "Preferred language",
"addEditUserModalPreferredLanguageOption1": "English (US)",
"addEditUserModalPreferredLanguageOption2": "Catalan",

View File

@@ -1,11 +1,16 @@
export function cmToFeetInches(cm) {
const inches = cm / 2.54;
const feet = Math.floor(inches / 12);
const remainingInches = (inches % 12).toFixed(0);
const totalInches = cm / 2.54;
const feet = Math.floor(totalInches / 12);
const inches = Math.round(totalInches % 12);
return `${feet}${remainingInches}`;
return { feet, inches };
}
export function feetAndInchesToCm(feet, inches) {
const totalInches = (feet * 12) + inches;
return (totalInches * 2.54).toFixed(0);
}
export function metersToMiles(meters) {
return (meters / 1609.344).toFixed(2);
return (meters / 1609.344).toFixed(2);
}