From 745bfd4749cc19895a94cd509afa3a4e2b07fb94 Mon Sep 17 00:00:00 2001 From: Mahmoud Emad Date: Sun, 24 Mar 2024 22:23:28 +0200 Subject: [PATCH 1/7] Fix the filter issue. --- client/src/components/CalenderComponent.vue | 193 ++++++++ client/src/components/calender.vue | 510 -------------------- client/src/router/index.ts | 1 + client/src/types/api.ts | 8 +- client/src/utils/helpers.ts | 11 +- client/src/views/CalendarView.vue | 14 +- client/src/views/TestView.vue | 82 ++++ server/cshr/services/landing_page.py | 161 ++---- server/cshr/services/users.py | 4 +- 9 files changed, 335 insertions(+), 649 deletions(-) create mode 100644 client/src/components/CalenderComponent.vue delete mode 100644 client/src/components/calender.vue create mode 100644 client/src/views/TestView.vue diff --git a/client/src/components/CalenderComponent.vue b/client/src/components/CalenderComponent.vue new file mode 100644 index 000000000..97ca60684 --- /dev/null +++ b/client/src/components/CalenderComponent.vue @@ -0,0 +1,193 @@ + + + + + diff --git a/client/src/components/calender.vue b/client/src/components/calender.vue deleted file mode 100644 index 87ef51361..000000000 --- a/client/src/components/calender.vue +++ /dev/null @@ -1,510 +0,0 @@ - - - - diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 019015b99..ca24a0256 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -6,6 +6,7 @@ const router = createRouter({ routes: [ { path: '/login', component: () => import('@/views/LoginView.vue') }, { path: '/', component: () => import('@/views/CalendarView.vue') }, + { path: '/test', component: () => import('@/views/TestView.vue') }, { path: '/notifications', component: () => import('@/views/NotificationsView.vue') }, { path: '/team', component: () => import('@/views/TeamView.vue') }, { path: '/users', name: 'users', component: () => import('@/views/UsersView.vue') }, diff --git a/client/src/types/api.ts b/client/src/types/api.ts index 4c54fb026..b8663e59b 100644 --- a/client/src/types/api.ts +++ b/client/src/types/api.ts @@ -40,7 +40,7 @@ export module Api { actual_days: number applying_user: number | any approval_user: number - user?: Api.User + applying_user_full_name?: string; isUpdated?: boolean } @@ -53,6 +53,7 @@ export module Api { export interface Salary {} export interface Meetings { + type: string; id: number invited_users: any[] date: any @@ -71,6 +72,7 @@ export module Api { location: string } export interface User { + type: string; id: number first_name: string last_name: string @@ -243,6 +245,7 @@ export module Api { } } export interface Holiday { + type: string id: number location: { id: number @@ -256,7 +259,7 @@ export module Api { export interface Home { id: number - title: string + type: string className: string eventName: string vacation?: any @@ -270,6 +273,7 @@ export module Api { export module Inputs { export interface Event { + type: string name: string description: string from_date: any diff --git a/client/src/utils/helpers.ts b/client/src/utils/helpers.ts index 2ed5bd648..5d4dea6ef 100644 --- a/client/src/utils/helpers.ts +++ b/client/src/utils/helpers.ts @@ -69,6 +69,7 @@ export function normalizeEvent(e: Api.Inputs.Event): any { const dates = handelDates(e.from_date, e.end_date) return { + type: e.type, title: 'Event', classNames: ['cshr-event'], color: '#47a2ff', @@ -83,7 +84,8 @@ export function normalizeVacation(v: Api.Vacation) { const dates = handelDates(v.from_date, v.end_date) return { - title: `${v.user!.full_name}'s Vacation`, + type: v.type, + title: `${v.applying_user_full_name}'s Vacation`, color: '#fcd091', start: dates.start, end: dates.end, @@ -97,6 +99,7 @@ export function normalizeHoliday(h: Api.Holiday) { const dates = handelDates(h.holiday_date, h.holiday_date) return { + type: h.type, title: `Public Holiday`, color: '#5effb4', start: dates.start, @@ -107,9 +110,10 @@ export function normalizeHoliday(h: Api.Holiday) { } } export function normalizedBirthday(u: Api.User) { - const dates = handelDates(u.birthday, u.birthday) + const dates = handelDates(u.date, u.date) return { + type: u.type, title: `Birthday`, color: '#e0adf0', start: dates.start, @@ -125,6 +129,7 @@ export function normalizeMeeting(m: Api.Meetings): any { const dates = handelDates(m.date, m.date) return { + type: m.type, title: 'Meeting', color: '#efeaea', start: dates.start, @@ -168,7 +173,7 @@ export function isValidToken(token: string): boolean { export async function listUsers($api: ApiClient, page: number, count: number): Promise<{ page: number, count: number, users: any[]}> { const res = await $api.users.admin.office_users.list({ page }); - let users: any[] = []; + const users: any[] = []; if (res.count) { count = Math.ceil(res.count / 10) } else { diff --git a/client/src/views/CalendarView.vue b/client/src/views/CalendarView.vue index b03b8667e..09b8d2f46 100644 --- a/client/src/views/CalendarView.vue +++ b/client/src/views/CalendarView.vue @@ -1,21 +1,13 @@ diff --git a/client/src/views/TestView.vue b/client/src/views/TestView.vue new file mode 100644 index 000000000..99c82e111 --- /dev/null +++ b/client/src/views/TestView.vue @@ -0,0 +1,82 @@ + + + diff --git a/server/cshr/services/landing_page.py b/server/cshr/services/landing_page.py index 3e28c96cc..e5b42bacd 100644 --- a/server/cshr/services/landing_page.py +++ b/server/cshr/services/landing_page.py @@ -1,10 +1,5 @@ """This file contains everything related to the landing page functionalty.""" -import datetime -from cshr.api.response import CustomResponse -from cshr.models.event import Event -from cshr.models.meetings import Meetings from cshr.models.users import User -from cshr.models.vacations import PublicHoliday, Vacation from cshr.serializers.event import EventSerializer from cshr.serializers.meetings import MeetingsSerializer from cshr.serializers.public_holidays import PublicHolidaySerializer @@ -15,135 +10,59 @@ from cshr.services.public_holidays import ( filter_public_holidays_by_month_and_year, ) -from cshr.services.users import filter_users_by_berithday_month +from cshr.services.users import filter_users_by_birth_month from cshr.services.vacations import filter_vacations_by_month_and_year -from typing import Any, List, Dict, Union -from itertools import chain +from typing import Any, List from enum import Enum +import datetime class LandingPageTypeEnum(Enum): VACATION = "vacation" - PUBLIC_HOLIDAY = "public_holiday" + PUBLIC_HOLIDAY = "holiday" BIRTHDAY = "birthday" MEETING = "meeting" - Event = "event" - - -class LandingPageClassNameEnum(Enum): - PUBLIC_HOLIDAY = "task--success" - VACATION = "task--warning" - BIRTHDAY = "task--primary" - MEETING = "task--danger" - Event = "task--info" + EVENT = "event" +def landing_page_calendar_functionality(user: User, month: str, year: str) -> List[Any]: + response: List[Any] = [] -def landing_page_calendar_functionality(user: User, month: str, year: str): - """ - This function will filter all of events based on its yesr, month. - """ - vacations: List[Vacation] = filter_vacations_by_month_and_year( - month, year - ).order_by("-created_at") - meetings: List[Meetings] = filter_meetings_by_month_and_year( - user, month, year - ).order_by("-created_at") - events: List[Event] = filter_events_by_month_and_year(user, month, year).order_by( - "-created_at" - ) - users_birthdates: List[User] = filter_users_by_berithday_month(month).order_by( - "-created_at" - ) - public_holidays: PublicHoliday = filter_public_holidays_by_month_and_year( - year, month - ).order_by("-created_at") + # Fetch vacations + vacations = filter_vacations_by_month_and_year(month, year).order_by("-created_at") + for vacation in vacations: + vacation_data = LandingPageVacationsSerializer(vacation).data + vacation_data["type"] = LandingPageTypeEnum.VACATION.value + vacation_data["applying_user_full_name"] = vacation.applying_user.full_name + response.append(vacation_data) - objects: Union[List[Any], None] = list( - chain(public_holidays, vacations, events, meetings, users_birthdates) - ) + # Fetch meetings + meetings = filter_meetings_by_month_and_year(user, month, year).order_by("-created_at") + for meeting in meetings: + meeting_data = MeetingsSerializer(meeting).data + meeting_data["type"] = LandingPageTypeEnum.MEETING.value + response.append(meeting_data) - response: List[Any] = [] + # Fetch events + events = filter_events_by_month_and_year(user, month, year).order_by("-created_at") + for event in events: + event_data = EventSerializer(event).data + event_data["type"] = LandingPageTypeEnum.EVENT.value + response.append(event_data) - for object in objects: - obj: Dict = {} - obj["id"] = object.id - if isinstance(object, Vacation): - obj["title"] = LandingPageTypeEnum.VACATION.value - obj["className"] = LandingPageClassNameEnum.VACATION.value - obj["eventName"] = LandingPageTypeEnum.VACATION.value - obj["vacation"] = LandingPageVacationsSerializer( - vacations.filter( - from_date__day=object.from_date.day, - from_date__month=object.from_date.month, - ), - many=True, - ).data - elif isinstance(object, PublicHoliday): - obj["title"] = LandingPageTypeEnum.PUBLIC_HOLIDAY.value - obj["className"] = LandingPageClassNameEnum.PUBLIC_HOLIDAY.value - obj["eventName"] = LandingPageTypeEnum.PUBLIC_HOLIDAY.value - obj["holidays"] = PublicHolidaySerializer( - public_holidays.filter( - holiday_date__day=object.holiday_date.day, - holiday_date__month=object.holiday_date.month, - ), - many=True, - ).data - obj["date"] = object.holiday_date - obj["len"] = 1 - elif isinstance(object, Meetings): - obj["title"] = LandingPageTypeEnum.MEETING.value - obj["date"] = object.date - obj["len"] = 1 - obj["vlen"] = 2 - obj["className"] = LandingPageClassNameEnum.MEETING.value - obj["eventName"] = LandingPageTypeEnum.MEETING.value - obj["meeting"] = MeetingsSerializer( - meetings.filter( - date__day=object.date.day, date__month=object.date.month - ), - many=True, - ).data - elif isinstance(object, Event): - obj["len"] = (object.end_date - object.from_date).days + 1 - obj["date"] = object.from_date - obj["title"] = LandingPageTypeEnum.Event.value - obj["className"] = LandingPageClassNameEnum.Event.value - obj["isBottom"] = True - obj["eventName"] = LandingPageTypeEnum.Event.value - obj["event"] = EventSerializer( - events.filter( - from_date__day=object.from_date.day, - from_date__month=object.from_date.month, - from_date__year=object.from_date.year, - ), - many=True, - ).data - elif isinstance(object, User): - today = datetime.datetime.now() - obj["title"] = LandingPageTypeEnum.BIRTHDAY.value - obj["className"] = LandingPageClassNameEnum.BIRTHDAY.value - obj["eventName"] = LandingPageTypeEnum.BIRTHDAY.value - obj["date"] = f"{today.year}-{object.birthday.month}-{object.birthday.day}" - obj["len"] = 1 - obj["vlen"] = 2 - obj["users"] = BaseUserSerializer( - users_birthdates.filter( - birthday__day=object.birthday.day, - birthday__month=object.birthday.month, - ), - many=True, - ).data - else: - return CustomResponse.bad_request(message="Unknown landing page type") + # Fetch users' birthdays + users_birthdates = filter_users_by_birth_month(month).order_by("-created_at") + for birthday_user in users_birthdates: + today = datetime.datetime.now() + birthday_data = BaseUserSerializer(birthday_user).data + birthday_data["type"] = LandingPageTypeEnum.BIRTHDAY.value + birthday_data["date"] = f"{today.year}-{birthday_user.birthday.month}-{birthday_user.birthday.day}" + response.append(birthday_data) - if ( - hasattr(object, "from_date") - and hasattr(object, "end_date") - and not isinstance(object, Event) - ): - obj["len"] = (object.end_date - object.from_date).days + 1 - obj["date"] = object.from_date - response.append(obj) + # Fetch public holidays + public_holidays = filter_public_holidays_by_month_and_year(year, month).order_by("-created_at") + for holiday in public_holidays: + holiday_data = PublicHolidaySerializer(holiday).data + holiday_data["type"] = LandingPageTypeEnum.PUBLIC_HOLIDAY.value + response.append(holiday_data) return response diff --git a/server/cshr/services/users.py b/server/cshr/services/users.py index 6685b8603..6f2e552ab 100644 --- a/server/cshr/services/users.py +++ b/server/cshr/services/users.py @@ -62,7 +62,7 @@ def get_user_type_by_id(id: str) -> User: return None -def filter_users_by_berithday_month(month: str) -> User: +def filter_users_by_birth_month(month: str) -> User: """Filter users based on birthdayes.""" users: List[User] = User.objects.filter(birthday__month=month) return users @@ -102,7 +102,7 @@ def get_admin_office_users(admin: User) -> User: ) -def get_or_create_skill_by_name(name: str) -> UserSkills or bool: +def get_or_create_skill_by_name(name: str) -> UserSkills or bool: # type: ignore """Return a skill by name""" return UserSkills.objects.get_or_create(name=name.lower()) From f64bdd8640813c674be418c0f737aeb4bc8b5351 Mon Sep 17 00:00:00 2001 From: Mahmoud Emad Date: Sun, 24 Mar 2024 22:42:49 +0200 Subject: [PATCH 2/7] Update the id of each event. --- client/src/components/CalenderComponent.vue | 4 +++- client/src/types/api.ts | 1 + client/src/utils/helpers.ts | 10 +++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/client/src/components/CalenderComponent.vue b/client/src/components/CalenderComponent.vue index 97ca60684..f6e4ba09f 100644 --- a/client/src/components/CalenderComponent.vue +++ b/client/src/components/CalenderComponent.vue @@ -76,11 +76,13 @@ const loadEvents = async () => { [], { onSuccess(data) { + console.log("data", data); + for (const event of data) { updateEventCalendarType(event) } filteredEvents.value = [...homeEvents.value] - console.log(filteredEvents.value.length); + console.log(filteredEvents.value); } } ) diff --git a/client/src/types/api.ts b/client/src/types/api.ts index b8663e59b..6fc9788c0 100644 --- a/client/src/types/api.ts +++ b/client/src/types/api.ts @@ -86,6 +86,7 @@ export module Api { mobile_number: string reporting_to: number[] birthday: string + date: string location: { id: number name: string diff --git a/client/src/utils/helpers.ts b/client/src/utils/helpers.ts index 5d4dea6ef..45c25f464 100644 --- a/client/src/utils/helpers.ts +++ b/client/src/utils/helpers.ts @@ -76,7 +76,7 @@ export function normalizeEvent(e: Api.Inputs.Event): any { start: dates.start, end: dates.end, backgroundColor: '#47a2ff', - id: e.name, + id: e.type + e.name, allDay: true } } @@ -90,7 +90,7 @@ export function normalizeVacation(v: Api.Vacation) { start: dates.start, end: dates.end, backgroundColor: '#fcd091', - id: v.id.toString(), + id: v.type + v.id.toString(), allDay: true } } @@ -105,7 +105,7 @@ export function normalizeHoliday(h: Api.Holiday) { start: dates.start, end: dates.end, backgroundColor: '#5effb4', - id: h.id.toString(), + id: h.type + h.id.toString(), allDay: true } } @@ -119,7 +119,7 @@ export function normalizedBirthday(u: Api.User) { start: dates.start, end: dates.end, backgroundColor: '#e0adf0', - id: u.id.toString(), + id: u.type + u.id.toString(), allDay: true } } @@ -135,7 +135,7 @@ export function normalizeMeeting(m: Api.Meetings): any { start: dates.start, end: dates.end, backgroundColor: '#efeaea', - id: m.id, + id: m.type + m.id, allDay: true } } From ffc2449727d57194e75c21adc5321dc91e3261b7 Mon Sep 17 00:00:00 2001 From: Mahmoud Emad Date: Mon, 25 Mar 2024 16:08:22 +0200 Subject: [PATCH 3/7] Update backend: Implemented wrappers to wrap the event request, working on the dialogs in the frontend. --- client/src/clients/api/meeting.ts | 2 +- client/src/components/CalenderComponent.vue | 197 +++++++++++++++++-- client/src/components/cards/vacationCard.vue | 16 +- client/src/types/api.ts | 12 +- client/src/types/index.ts | 30 +-- client/src/utils/helpers.ts | 8 +- server/cshr/serializers/event.py | 1 + server/cshr/services/event.py | 31 +-- server/cshr/services/landing_page.py | 47 ++--- server/cshr/services/meetings.py | 30 +-- server/cshr/services/vacations.py | 31 +-- server/cshr/utils/wrappers.py | 63 ++++++ server/cshr/views/event.py | 4 +- server/cshr/views/meetings.py | 8 +- server/cshr/views/vacations.py | 80 ++++---- 15 files changed, 359 insertions(+), 201 deletions(-) create mode 100644 server/cshr/utils/wrappers.py diff --git a/client/src/clients/api/meeting.ts b/client/src/clients/api/meeting.ts index 7a61205ba..f7f6f9c71 100644 --- a/client/src/clients/api/meeting.ts +++ b/client/src/clients/api/meeting.ts @@ -14,7 +14,7 @@ export class MeetingApi extends ApiClientBase { list(query?: any) { return this.unwrap( - () => this.$http.get>(this.getUrl('', query)), + () => this.$http.get>(this.getUrl('', query)), { transform: (d) => d.results } diff --git a/client/src/components/CalenderComponent.vue b/client/src/components/CalenderComponent.vue index f6e4ba09f..febb85155 100644 --- a/client/src/components/CalenderComponent.vue +++ b/client/src/components/CalenderComponent.vue @@ -19,7 +19,6 @@
- {{ isLoading }}
@@ -32,31 +31,54 @@ }" > -
+ + + + + + + + + + diff --git a/client/src/components/requests/meetingRequest.vue b/client/src/components/requests/meetingRequest.vue index 9e6e5afd4..ec6730ae8 100644 --- a/client/src/components/requests/meetingRequest.vue +++ b/client/src/components/requests/meetingRequest.vue @@ -59,7 +59,14 @@ - Submit + + Submit + @@ -82,6 +89,7 @@ export default { const location = ref('') const form = ref() const meetingTime = ref() + const requesting = ref(false) const meetingDateTime = computed(() => { let val = new Date(startDate.value) @@ -94,7 +102,8 @@ export default { }) async function createMeeting() { - useAsyncState( + requesting.value = true + await useAsyncState( $api.meeting.create({ date: meetingDateTime.value, meeting_link: meetingLink.value, @@ -107,6 +116,7 @@ export default { } } ) + requesting.value = false } return { @@ -115,6 +125,7 @@ export default { location, form, meetingTime, + requesting, fieldRequired, createMeeting } diff --git a/client/src/types/api.ts b/client/src/types/api.ts index 5e7c9baef..58569ad49 100644 --- a/client/src/types/api.ts +++ b/client/src/types/api.ts @@ -282,7 +282,6 @@ export module Api { export module Inputs { export interface Event { - type: string name: string description: string from_date: any diff --git a/server/cshr/services/vacations.py b/server/cshr/services/vacations.py index 884fd7f61..7320badba 100644 --- a/server/cshr/services/vacations.py +++ b/server/cshr/services/vacations.py @@ -14,12 +14,10 @@ def filter_vacations_by_month_and_year(month: str, year: str) -> Vacation: Q( from_date__month=month, from_date__year=year, - status__in=[STATUS_CHOICES.PENDING, STATUS_CHOICES.APPROVED], ) | Q( end_date__month=month, end_date__year=year, - status__in=[STATUS_CHOICES.PENDING, STATUS_CHOICES.APPROVED], ) ) return vacations diff --git a/server/cshr/utils/wrappers.py b/server/cshr/utils/wrappers.py index b8373b827..8bfa261b9 100644 --- a/server/cshr/utils/wrappers.py +++ b/server/cshr/utils/wrappers.py @@ -47,7 +47,7 @@ def wrap_event_request(event: Event) -> EventSerializer : # type: ignore """ Wrap the event request with [type: string] field, to be ready to be sent to the calendar as the `type` field is required there. """ -def wrap_holiday_request(holiday: PublicHoliday) -> EventSerializer : # type: ignore +def wrap_holiday_request(holiday: PublicHoliday) -> PublicHolidaySerializer : # type: ignore holiday_data = PublicHolidaySerializer(holiday).data holiday_data["type"] = LandingPageTypeEnum.PUBLIC_HOLIDAY.value return holiday_data diff --git a/server/cshr/views/meetings.py b/server/cshr/views/meetings.py index dd711a045..1f439e4db 100644 --- a/server/cshr/views/meetings.py +++ b/server/cshr/views/meetings.py @@ -12,7 +12,7 @@ from rest_framework.request import Request from rest_framework.response import Response from cshr.api.response import CustomResponse -from cshr.utils.wrappers import wrap_event_request +from cshr.utils.wrappers import wrap_meeting_request class BaseMeetingsApiView(ListAPIView, GenericAPIView): @@ -34,7 +34,7 @@ def post(self, request: Request) -> Response: event = serializer.save(host_user=current_user, invited_users=[]) - response_date: Dict = wrap_event_request(event) + response_date: Dict = wrap_meeting_request(event) return CustomResponse.success( data=response_date, message="meeting is created successfully", diff --git a/server/cshr/views/vacations.py b/server/cshr/views/vacations.py index 30ae76ccb..72b41b0a7 100644 --- a/server/cshr/views/vacations.py +++ b/server/cshr/views/vacations.py @@ -330,8 +330,8 @@ def delete(self, request: Request, id, format=None) -> Response: vacation = get_vacation_by_id(id=id) if vacation is not None: vacation.delete() - return CustomResponse.success(message="Hr Letter deleted", status_code=204) - return CustomResponse.not_found(message="Hr Letter not found", status_code=404) + return CustomResponse.success(message="The vacation has been deleted successfully.", status_code=204) + return CustomResponse.not_found(message="The vacation is not found.", status_code=404) class VacationUserApiView(ListAPIView, GenericAPIView): From 308fa0aead2528a64ae6ccfd7791a399bac0ebd4 Mon Sep 17 00:00:00 2001 From: mayar osama Date: Tue, 26 Mar 2024 13:20:51 +0200 Subject: [PATCH 5/7] fixing create event submit button --- client/src/components/cards/vacationCard.vue | 5 ++ .../src/components/requests/eventRequest.vue | 55 +++++++++++++++---- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/client/src/components/cards/vacationCard.vue b/client/src/components/cards/vacationCard.vue index 6c8ad08fd..405785ee6 100644 --- a/client/src/components/cards/vacationCard.vue +++ b/client/src/components/cards/vacationCard.vue @@ -138,6 +138,7 @@ export default { const couldUpdate = computed(() => { if (user.value) { if (props.vacation.status == 'pending') { + // could update if user signed in is the same user applied for vacation if (props.vacation.isUpdated && user.value.fullUser.id == props.vacation.applying_user) { return true } @@ -156,13 +157,17 @@ export default { const couldApprove = computed(() => { if (user.value) { + // only admins or supervisors could approve vacation if ( user.value.fullUser.user_type === 'Admin' || user.value.fullUser.user_type === 'Supervisor' ) { + // Could approve if user signed in is the same user applied for vacation if (props.vacation.applying_user.id == user.value.fullUser.id) { return true } + // Could approve if applying user reports to the supervisor or admin logged in + // and works from the same office if ( props.vacation.applying_user.reporting_to && props.vacation.applying_user.reporting_to[0]?.id === props.vacation.applying_user?.id && diff --git a/client/src/components/requests/eventRequest.vue b/client/src/components/requests/eventRequest.vue index e8768bbb2..0bbd264f5 100644 --- a/client/src/components/requests/eventRequest.vue +++ b/client/src/components/requests/eventRequest.vue @@ -1,13 +1,33 @@