Skip to content

Commit

Permalink
Development admin accessibility (#367)
Browse files Browse the repository at this point in the history
* Adding access to admin to request vacations for other users

* Adding admin endpoit

* fixing vacation card error

* Development admin accessibility backend (#368)

* Implementation: Implemented an endpoint for admins to post vacations behalf of the user.

* Enhancement: rename the route of the admin vacations request

* Typo: replaced the pinding by pending.

* Using enum in selectionOption and exporting ListUsers in helpers

---------

Co-authored-by: Thunder <mahmmoud.hassanein@gmail.com>
  • Loading branch information
maayarosama and Mahmoud-Emad authored Mar 6, 2024
1 parent 8dfbb3d commit e1ec9aa
Show file tree
Hide file tree
Showing 17 changed files with 352 additions and 93 deletions.
17 changes: 17 additions & 0 deletions client/src/clients/api/vacations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export class VacationsApi extends ApiClientBase {
readonly edit: VacationsEditApi
readonly get_admin_balance: VacationsGetAdminBalanceApi
readonly post_admin_balance: VacationsPostAdminBalanceApi
readonly admin: VacationsAdmin
readonly user: VacationsUserApi

constructor(options: Api.ClientOptions) {
Expand All @@ -25,6 +26,7 @@ export class VacationsApi extends ApiClientBase {
this.edit = new VacationsEditApi(options, this.path)
this.get_admin_balance = new VacationsGetAdminBalanceApi(options, this.path)
this.post_admin_balance = new VacationsPostAdminBalanceApi(options, this.path)
this.admin= new VacationsAdmin(options, this.path)
this.user = new VacationsUserApi(options, this.path)
}

Expand Down Expand Up @@ -168,6 +170,21 @@ class VacationsGetAdminBalanceApi extends ApiClientBase {
})
}
}
class VacationsAdmin extends ApiClientBase {
protected readonly path = '/admin'


async create(id: number, input: Api.Inputs.Leave) {
ApiClientBase.assertUser()
const vacation = await this.unwrap(
() => this.$http.post<Api.Returns.LeaveRequest>(this.getUrl(`/${id}`), input),
{ transform: (d) => d.results }
)

return vacation
}
}


class VacationsPostAdminBalanceApi extends ApiClientBase {
protected readonly path = '/post-admin-balance'
Expand Down
38 changes: 13 additions & 25 deletions client/src/components/SetUserVacations.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,8 @@

<template #append-item v-if="reloadMore">
<VContainer>
<VBtn
@click="
() => {
return page++, count--, listUsers()
}
"
block
color="secondary"
variant="tonal"
prepend-icon="mdi-reload"
>
<VBtn @click="() => { return page++, count--, concatUsers() }" block color="secondary" variant="tonal"
prepend-icon="mdi-reload">
Load More Users
</VBtn>
</VContainer>
Expand Down Expand Up @@ -136,7 +127,7 @@

<script lang="ts">
import { $api } from '@/clients'
import { requiredRules, requiredStringRules, vacationRules } from '@/utils'
import { listUsers, requiredRules, requiredStringRules, vacationRules } from '@/utils'
import { onMounted, ref, watchEffect, computed } from 'vue'
import { useAsyncState } from '@vueuse/core'
import { ApiClientBase } from '@/clients/api/base'
Expand Down Expand Up @@ -276,21 +267,18 @@ export default {
}
})
async function listUsers() {
const res = await $api.users.admin.office_users.list({ page: page.value })
if (res.count) {
count.value = Math.ceil(res.count / 10)
} else {
count.value = 0
}
res.results.forEach((user: any) => {
officeUsers.value.push(user)
})
return officeUsers.value
async function concatUsers() {
const { page: currentPage, count: currentCount, users: newUsers } = await listUsers($api, page.value, count.value);
page.value = currentPage;
count.value = currentCount;
officeUsers.value = officeUsers.value.concat(newUsers);
}
onMounted(async () => {
try {
await listUsers()
await concatUsers()
selectedUsers.value.push(officeUsers.value[0])
await getUserBalance()
} catch (error) {
Expand Down Expand Up @@ -322,7 +310,7 @@ export default {
vacationRules,
isLoading,
reloadMore,
listUsers,
concatUsers,
execute,
setUserBalanceValues,
userBalance,
Expand Down
31 changes: 14 additions & 17 deletions client/src/components/UpdateUser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<template #append-item v-if="reloadMore">
<VContainer>
<VBtn @click="() => { return page++, count--, listUsers() }" block color="secondary" variant="tonal"
<VBtn @click="() => { return page++, count--, concatUsers()}" block color="secondary" variant="tonal"
prepend-icon="mdi-reload">
Load More Users
</VBtn>
Expand Down Expand Up @@ -114,7 +114,8 @@ import {
telegramRules,
requiredStringRules,
requiredRules,
formatDate
formatDate,
listUsers
} from '@/utils'
import { useAsyncState } from '@vueuse/core'
import { ApiClientBase } from '@/clients/api/base'
Expand Down Expand Up @@ -166,18 +167,6 @@ export default {
}
return false;
});
async function listUsers() {
const res = await $api.users.admin.office_users.list({ page: page.value })
if (res.count) {
count.value = Math.ceil(res.count / 10)
} else {
count.value = 0
}
res.results.forEach((user: any) => {
officeUsers.value.push(user)
})
return officeUsers.value
}
async function listSupervisors() {
const res = await $api.users.supervisors.list({ page: supervisorPage.value })
Expand All @@ -193,13 +182,21 @@ export default {
return supervisors.value
}
async function concatUsers() {
const { page: currentPage, count: currentCount, users: newUsers } = await listUsers($api, page.value, count.value);
page.value = currentPage;
count.value = currentCount;
officeUsers.value = officeUsers.value.concat(newUsers);
}
onMounted(async () => {
try {
offices.value = (await $api.office.list()).map((office: any) => ({
id: office.id,
name: office.name
}))
await listUsers()
}));
await concatUsers()
selectedUser.value = officeUsers.value[0]
await listSupervisors()
reporting_to.value = selectedUser.value?.reporting_to[0]
Expand Down Expand Up @@ -299,7 +296,7 @@ export default {
page,
reloadMore,
reloadMoreSupervisor,
listUsers,
concatUsers,
chooseImage
}
}
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/cards/vacationCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<b color="primary">{{ vacation.end_date }} </b> vacation

</p>

<v-form ref="form" @submit.prevent="updateVacation()">

<v-row class="d-flex justify-center my-2" v-if="couldUpdate">
Expand Down Expand Up @@ -157,7 +158,7 @@ export default {
return true
}
if (
props.vacation.user.reporting_to[0].id === props.vacation.user?.id &&
props.vacation.user.reporting_to[0]?.id === props.vacation.user?.id &&
props.vacation.user.location.name === user.value.fullUser.location.name
) {
return true
Expand Down
134 changes: 116 additions & 18 deletions client/src/components/requests/leaveRequest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@
<v-alert v-if="form?.isValid && isValid" density="compact" class="pa-5 my-5" type="warning">
{{ actualDays.state.value === 0 ? "Actual vacation days requested is zero, Selected days might include weekends or public holidays" : "Actual vacation days requested are " + actualDays.state.value +" days" }}
</v-alert>
<div class="mt-3" v-if="user?.fullUser.user_type === 'Admin'"> <v-radio-group inline v-model="selectedOption">
<v-radio label="For yourself" value="me"></v-radio>
<v-radio label="For Another User" value="anotheruser"></v-radio>
</v-radio-group> </div>
<div class="mt-3" v-if="selectedOption === 'anotheruser'">
<v-autocomplete color="info" item-color="info" base-color="info" variant="outlined" v-model="selectedUser"
:items="officeUsers" item-title="full_name" item-value="id" label="User" return-object :rules="requiredRules">

<template #append-item v-if="reloadMore">
<VContainer>
<VBtn @click="() => { return page++, count--, concatUsers() }" block color="secondary" variant="tonal"
prepend-icon="mdi-reload">
Load More Users
</VBtn>
</VContainer>
</template>
</v-autocomplete>

</div>

<div class="mt-3">
<v-text-field ref="startDateField" color="info" item-color="info" base-color="info" variant="outlined"
Expand All @@ -26,7 +45,8 @@
</template>
<script lang="ts">
import { useApi } from '@/hooks'
import type { Api } from '@/types';
import { Selection, type Api } from '@/types';
import { requiredRules, listUsers } from '@/utils'
import { useAsyncState } from '@vueuse/core';
import { computed, onMounted, ref, watch } from 'vue';
import { ApiClientBase } from '@/clients/api/base';
Expand All @@ -40,15 +60,21 @@ export default {
setup(props, ctx) {
const $api = useApi()
const form = ref()
const officeUsers = ref<any[]>([]);
const selectedUser = ref()
const selectedOption = ref<Selection>(Selection.ME)
const page = ref(1)
const count = ref(0)
const startDateField = ref()
const endDateField = ref()
const startDate = ref<Date>(props.dates.startStr)
const endDate = ref<any>(new Date(props.dates.endStr))
endDate.value.setDate(endDate.value.getDate() - 1);
endDate.value = endDate.value.toISOString().split('T')[0];
const user = ApiClientBase.user
const userId = ref<number | undefined>()
const leaveReason = ref<Api.LeaveReason>()
const leaveReasons = ref<Api.LeaveReason[]>([])
const actualDays = useAsyncState(
async () => {
return $api.vacations.calculate.list({
Expand All @@ -64,9 +90,9 @@ export default {
let val = leaveReason.value ? true : false;
return val;
});
const leaveReasons = ref<Api.LeaveReason[]>([])
const balance = useAsyncState($api.vacations.getVacationBalance({ "user_ids": user.value?.fullUser.id }), null, {
const balance = useAsyncState(() => $api.vacations.getVacationBalance({ "user_ids": userId.value }), null, {
immediate: false,
onSuccess(data: any) {
leaveReasons.value = [{
name: `Emergency Leaves ${data[0].emergency_leaves.reserved} / ${data[0].emergency_leaves.all}`,
Expand All @@ -91,6 +117,33 @@ export default {
}
})
watch(
() => [selectedOption.value],
() => {
if (selectedOption.value === Selection.ANOTHERUSER) {
selectedUser.value = officeUsers.value[0]
} else {
selectedUser.value = undefined
userId.value = user.value?.fullUser.id
balance.execute()
}
},
);
watch(
() => [selectedUser.value],
async () => {
if (selectedUser.value) {
userId.value = selectedUser.value.id
balance.execute()
}
},
);
watch(
() => [startDate.value, endDate.value],
Expand All @@ -110,27 +163,64 @@ export default {
return true;
};
onMounted(() => {
async function concatUsers() {
const { page: currentPage, count: currentCount, users: newUsers } = await listUsers($api, page.value, count.value);
page.value = currentPage;
count.value = currentCount;
officeUsers.value = officeUsers.value.concat(newUsers);
}
onMounted(async () => {
startDateField.value.validate();
endDateField.value.validate();
userId.value = user.value?.fullUser.id
balance.execute()
concatUsers()
})
const reloadMore = computed(() => {
if (page.value === count.value) {
return false
}
if (count.value > 1) {
return true
}
return false;
});
async function createLeave() {
if (leaveReason.value) {
useAsyncState($api.vacations.create(
{
reason: leaveReason.value.reason,
from_date: startDate.value,
end_date: endDate.value
}),
undefined,
{
onSuccess(data) {
ctx.emit('create-event', data)
if (selectedOption.value === Selection.ANOTHERUSER) {
useAsyncState(
$api.vacations.admin.create(selectedUser.value.id, {
reason: leaveReason.value.reason,
from_date: startDate.value,
end_date: endDate.value,
}),
null,
{
onSuccess(data) {
ctx.emit('create-event', data)
}
}
)
}
else {
useAsyncState($api.vacations.create(
{
reason: leaveReason.value.reason,
from_date: startDate.value,
end_date: endDate.value
}),
undefined,
{
onSuccess(data) {
ctx.emit('create-event', data)
}
}
}
)
)
}
}
}
Expand All @@ -145,6 +235,14 @@ export default {
user,
startDateField,
endDateField,
requiredRules,
reloadMore,
selectedUser,
officeUsers,
page,
count,
selectedOption,
concatUsers,
createLeave,
validateDates,
};
Expand Down
Loading

0 comments on commit e1ec9aa

Please sign in to comment.