Skip to content

Commit

Permalink
Merge pull request #502 from codescalers/development_approve_all_pend…
Browse files Browse the repository at this point in the history
…ing_requests

feat: Support approve/reject all button:
  • Loading branch information
Mahmoud-Emad authored Nov 3, 2024
2 parents bb110b5 + aa0885c commit a4dea72
Show file tree
Hide file tree
Showing 29 changed files with 737 additions and 297 deletions.
74 changes: 7 additions & 67 deletions client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,92 +6,32 @@

<script lang="ts">
import { defineComponent, onMounted } from 'vue'
import { useNotifier } from 'vue3-notifier'
import { useApi } from '@/hooks'
import SideDrawer from './components/SideDrawer.vue'
import { useWSConnectionStore } from './stores/WSConnection'
import { useNotificationStore } from './stores/notifications'
import type { notificationType, WSErrorType } from './types'
import { useHomeEventsStore } from './stores/homeEvents'
export default defineComponent({
name: 'App',
components: {
SideDrawer
},
setup() {
const api = useApi()
const notifier = useNotifier()
if (api && notifier) {
notifier.notify
api.setNotifier(notifier)
}
const WSConnection = useWSConnectionStore()
const notifications = useNotificationStore()
const connection = WSConnection.connect()
const handleIncomingMessage = (event: MessageEvent) => {
const data: notificationType | WSErrorType = JSON.parse(event.data as string);
if ('code' in data && 'message' in data) {
// Handle error
const error: WSErrorType = data as WSErrorType;
const noti = notifier.notify({
title: 'An error received from the WebSocket',
description: error.message,
type: 'error'
});
setTimeout(() => {
noti?.destroy();
}, 4000);
} else {
// Handle notification
const notification: notificationType = data as notificationType;
const homeEventsStore = useHomeEventsStore();
notifications.addNotification(notification);
if (notification.request.type === "vacation") {
homeEventsStore.reload = true;
}
const noti = notifier.notify({
title: notification.title,
description: notification.body,
type: 'success'
});
setTimeout(() => {
noti?.destroy();
}, 4000);
}
};
onMounted(async () => {
window.connections = {
ws: connection
}
if (window.connections.ws.value) {
window.connections.ws.value!.onmessage = (event: MessageEvent) =>
handleIncomingMessage(event)
window.connections.ws.value!.onerror = (error) => {
console.error('WebSocket error:', error)
}
window.connections.ws.value!.onclose = (event) => {
console.log('WebSocket connection closed:', event)
onMounted(async () => {
window.connections = {
ws: connection
}
}
})
}
WSConnection.WSHandleConnection();
})
}
})
</script>
<style>
.vue3-notifier-container .text-error {
background-color: rgb(44, 16, 16) !important;
}
.vue3-notifier-container .text-success {
background-color: rgb(2, 29, 3) !important;
}
Expand Down
13 changes: 11 additions & 2 deletions client/src/clients/api/vacations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,16 @@ export class VacationsApi extends ApiClientBase {
async myPendingRequests(query?: any) {
ApiClientBase.assertUser()
const requests = await this.unwrap(
() => this.$http.get<Api.Returns.List<Api.Vacation>>(this.getUrl('/my-peneding-requests', query)),
() => this.$http.get<Api.Returns.List<Api.Vacation>>(this.getUrl('/my-pending-requests', query)),
{ transform: (d) => d }
)
return requests
}

async approveOrRejectAllTeamPendingRequets(data: Api.ApproveOrRejectAllTeamPendingRequets) {
ApiClientBase.assertUser()
const requests = await this.unwrap(
() => this.$http.put<Api.Returns.List<Api.Vacation>>(this.getUrl('/action-team-pending-requests'), data),
{ transform: (d) => d }
)
return requests
Expand All @@ -42,7 +51,7 @@ export class VacationsApi extends ApiClientBase {
async myTeamPendingRequests(query?: any) {
ApiClientBase.assertUser()
const requests = await this.unwrap(
() => this.$http.get<Api.Returns.List<Api.Vacation>>(this.getUrl('/my-team-peneding-requests', query)),
() => this.$http.get<Api.Returns.List<Api.Vacation>>(this.getUrl('/my-team-pending-requests', query)),
{ transform: (d) => d }
)
return requests
Expand Down
25 changes: 17 additions & 8 deletions client/src/components/CalenderComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
</template>

<script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import { onMounted, reactive, ref, watch } from 'vue'
import { type CalendarApi, type CalendarOptions } from '@fullcalendar/core'
import { useApi } from '@/hooks'
import { useAsyncState } from '@vueuse/core'
Expand Down Expand Up @@ -241,17 +241,26 @@ const filterEvents = () => {
})
}
const resetHomeEventsStore = async () => {
homeEventsStore.events = [];
homeEventsStore.vacations = [];
homeEventsStore.meetings = [];
homeEventsStore.userEvents = [];
homeEventsStore.birthdays = [];
homeEventsStore.holidays = [];
await loadEvents();
}
onMounted(async () => {
await resetHomeEventsStore();
})
watch(
() => homeEventsStore.reload,
async () => {
if(homeEventsStore.reload){
homeEventsStore.events = [];
homeEventsStore.vacations = [];
homeEventsStore.meetings = [];
homeEventsStore.userEvents = [];
homeEventsStore.birthdays = [];
homeEventsStore.holidays = [];
await loadEvents();
await resetHomeEventsStore();
homeEventsStore.reload = false
}
},
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/CshrToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export default {
if (!user.state.value) {
window.location.href = '/login'
}
})
});
const loadNotifications = async () => {
const notificationData = await $api.notifications.list()
Expand Down
22 changes: 16 additions & 6 deletions client/src/components/NotificationDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
<v-col class="d-flex justify-start mb-0">
<v-card-title class="font-weight-bold mb-0"> {{ $props.modelValue.title }} </v-card-title>
</v-col>
<v-col class="d-flex justify-end mb-0 align-center">
<v-chip :color="getStatusColor(requestStatus!)">
{{ formatRequestStatus(requestStatus!) }}
</v-chip>
</v-col>
</v-row>
<v-card-text class="mt-0 pt-0">
<p>
Expand Down Expand Up @@ -51,22 +56,23 @@
:vacation="vacation"
:display-close-btn="true"
@update:close-dialog="closeDialog"
@update:vacation="$emit('update:approvalUser', $event.approval_user)"
@update:vacation="$emit('update:vacation', $event)"
/>
</v-row>
</v-card>
</template>

<script lang="ts">
import { computed, ref, type PropType } from 'vue'
import type { notificationType } from '@/types'
import { computed, type PropType } from 'vue'
import type { Api, notificationType } from '@/types'
import { ApiClientBase } from '@/clients/api/base'
import ActionButtons from "@/components/requests/ActionButtons.vue"
import { formatRequestStatus, getStatusColor } from '@/utils';
export default {
components: { ActionButtons },
name: 'NotificationDetails',
emits: ["update:approvalUser"],
emits: ["update:vacation"],
props: {
modelValue: {
type: Object as PropType<notificationType>,
Expand All @@ -76,6 +82,10 @@ export default {
type: Array as PropType<any[]>,
required: true
},
requestStatus: {
type: Object as PropType<Api.RequestStatus>,
required: false
},
vacation: {
type: Object as PropType<notificationType["request"]>,
required: false
Expand All @@ -87,7 +97,6 @@ export default {
},
setup(props) {
const user = ApiClientBase.user
const vacationStatus = ref(props.modelValue.request.status)
const closeDialog = () => {
props.onClose()
Expand All @@ -102,8 +111,9 @@ export default {
return {
couldApprove,
vacationStatus,
closeDialog,
formatRequestStatus,
getStatusColor,
}
}
}
Expand Down
16 changes: 11 additions & 5 deletions client/src/components/NotificationDetailsDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
v-if="couldAccessNotification"
:modelValue.="$props.modelValue"
:sections="getSections(notification.state.value)"
:request-status="requestStatus"
:vacation="
notification.state.value.request.type === 'vacation' ? notification.state.value.request : undefined
"
@close="closeDialog"
@update:approval-user="handleApprovalUser"
@update:vacation="updateVacation($event)"
/>
<div v-else class="not-allowed">
<v-container>
Expand Down Expand Up @@ -69,6 +70,7 @@ export default {
const $api = useApi()
const applyingUser = ref<Api.User | null>()
const approvalUser = ref<Api.User | null>()
const requestStatus = ref<Api.RequestStatus>()
const selected = useRouteQuery<undefined | string>('selected-' + props.routeQuery, undefined)
const notificationsStore = useNotificationStore()
const notifications = computed(() => notificationsStore.notifications)
Expand All @@ -83,6 +85,7 @@ export default {
const response = await $api.notifications.getNotification(+id)
applyingUser.value = response.request.applying_user
approvalUser.value = response.request.approval_user
requestStatus.value = response.request.status
ctx.emit("set:notification", response)
return response
}
Expand Down Expand Up @@ -125,7 +128,7 @@ export default {
label: 'From Date',
value: `${new Date(data.request.from_date).toDateString()}`
},
{ label: 'End Date', value: `${new Date(data.request.end_date).toDateString()}` }
{ label: 'End Date', value: `${new Date(data.request.end_date).toDateString()}` },
]
},
{
Expand Down Expand Up @@ -176,8 +179,10 @@ export default {
ctx.emit('update:model-value')
}
function handleApprovalUser(user: Api.User) {
approvalUser.value = user
function updateVacation(vacation: Api.Vacation) {
requestStatus.value = vacation.status;
applyingUser.value = vacation.applying_user;
approvalUser.value = vacation.approval_user;
}
return {
Expand All @@ -186,7 +191,8 @@ export default {
closeDialog,
getSections,
capitalize,
handleApprovalUser,
requestStatus,
updateVacation,
couldAccessNotification,
}
}
Expand Down
Loading

0 comments on commit a4dea72

Please sign in to comment.