Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/src/clients/api/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ class UsersTeamSupervisorsApi extends ApiClientBase {
class UsersActiveApi extends ApiClientBase {
protected readonly path = '/active'

async list() {
return this.unwrap(() => this.$http.get(this.getUrl()), {
async list(query?: { include_admins?: boolean }) {
return this.unwrap(() => this.$http.get(this.getUrl('', query)), {
transform: (d: any) => d.results
})
}
Expand Down
13 changes: 11 additions & 2 deletions client/src/components/dashboard/UpdateUser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<v-select v-if="field.type === 'select'" :items="field.options" :loading="field.isLoading"
:disabled="field.disabled || field.isLoading" v-model="field.value" :label="field.label"
:rules="field.rules" hide-details="auto" :item-title="field.itemTitle" :item-value="field.itemValue"
clearable>
:multiple="field.multiple" clearable>
<template #append-item v-if="field.options && field.count">
<div v-if="field.options!.length < field.count">
<VContainer>
Expand Down Expand Up @@ -52,7 +52,7 @@
import { $api } from '@/clients';
import { requiredRules, listAllOfficeUsers } from '@/utils';
import type { Api } from '@/types';
import { listOffices, listTeamLeads, userInputs as UpdateUserForm } from '@/utils/add_update_user_form';
import { listOffices, listTeamLeads, userInputs as UpdateUserForm , listAllUsers} from '@/utils/add_update_user_form';
import { onMounted, ref, watch } from 'vue';

export default {
Expand All @@ -68,6 +68,7 @@ export default {
const officeUsersPage = ref(1)
const officeUsersCount = ref(0)
const userInputs = ref([...UpdateUserForm.value])
const hiddenFrom = ref<Api.User[]>([])

onMounted(async () => {
try {
Expand All @@ -85,6 +86,7 @@ export default {
locations.value = await listOffices({ count: 10, key: 'location', items: [], page: 1 })
teamLeads.value = await listTeamLeads()
officeUsers.value = await listAllOfficeUsers($api)
hiddenFrom.value = await listAllUsers()
} catch (error) {
console.error(error)
} finally {
Expand Down Expand Up @@ -204,6 +206,12 @@ export default {
if (input.key === 'user_type' && input.value === 'Supervisor') {
input.value = 'Team Lead'
}
if (input.key === 'hidden_from_users') {
if (Array.isArray(input.value)) {
input.value = input.value.map((u: any) => u.id)
}
input.options = hiddenFrom.value.filter((u: any) => u.id !== selectedUser.value!.id)
}
});
await readUserImage();
form.value.validate();
Expand All @@ -216,6 +224,7 @@ export default {
isLoading,
selectedUser,
officeUsers,
hiddenFrom,
officeUsersCount,
officeUsersPage,
requiredRules,
Expand Down
23 changes: 23 additions & 0 deletions client/src/utils/add_update_user_form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface InputInterface {
itemValue?: string;
count?: number;
page?: number;
multiple?: boolean;
}

export async function listOffices(options: { key: string, count: number, items: any[], page: number }) {
Expand Down Expand Up @@ -54,6 +55,15 @@ export async function listTeamLeads() {
return res.results
}

export async function listAllUsers() {
const hiddenFromInput = userInputs.value.filter(i => i.key === 'hidden_from_users')[0]
hiddenFromInput.isLoading = true
const res = await $api.users.active.list({ include_admins: true })
hiddenFromInput.options = res ?? []
hiddenFromInput.isLoading = false
return res ?? []
}

export const userBalance = ref<InputInterface[]>([
{
label: 'Annual',
Expand Down Expand Up @@ -311,6 +321,19 @@ export const userInputs = ref<InputInterface[]>([
isLoading: false,
disabled: false,
},
{
label: 'Hidden From',
key: 'hidden_from_users',
value: [],
type: 'select',
rules: [],
options: [],
itemTitle: 'full_name',
itemValue: 'id',
multiple: true,
isLoading: false,
disabled: false,
},
{
label: 'is active user',
key: 'is_active',
Expand Down
23 changes: 23 additions & 0 deletions server/cshr/migrations/0037_user_hidden_from.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.17 on 2026-06-03 16:34

from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("cshr", "0036_update_excuse_balance"),
]

operations = [
migrations.AddField(
model_name="user",
name="hidden_from",
field=models.ManyToManyField(
blank=True,
related_name="hidden_from_users",
to=settings.AUTH_USER_MODEL,
),
),
]
1 change: 1 addition & 0 deletions server/cshr/models/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class User(AbstractBaseUser, TimeStamp):
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
objects = CshrBaseUserManger()
hidden_from = models.ManyToManyField("User",related_name="hidden_from_users", blank=True)

@property
def full_name(self) -> str:
Expand Down
2 changes: 2 additions & 0 deletions server/cshr/routes/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
PostUserSkillsAPIView,
GetUsersBirthDatesAPIView,
AllActiveUsersAPIView,
HiddenFromUserAPIView,
)


Expand All @@ -36,6 +37,7 @@
path("team/supervisors/", TeamSupervisorsAPIView.as_view()),
path("supervisors/", SupervisorsAPIView.as_view()),
path("supervisor/<str:id>/", SupervisorUserAPIView.as_view()),
path("admin/<str:id>/hidden_from/", HiddenFromUserAPIView.as_view()),
path("admin/<str:id>/", AdminUserAPIView.as_view()),
path("<str:id>/", GeneralUserAPIView.as_view()),
]
10 changes: 10 additions & 0 deletions server/cshr/serializers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class GeneralUserSerializer(ModelSerializer):
reporting_to = SerializerMethodField()
location = SerializerMethodField()
leads = SerializerMethodField()
hidden_from_users = SerializerMethodField()

class Meta:
model = User
Expand All @@ -83,6 +84,7 @@ class Meta:
"user_type",
"is_active",
"leads",
"hidden_from_users",
]

def get_skills(self, obj):
Expand All @@ -97,6 +99,9 @@ def get_location(self, obj):
def get_leads(self, obj):
return build_user_reporting_to_hierarchy(obj)

def get_hidden_from_users(self, obj):
return BasicUserSerializer(obj.hidden_from.all(), many=True).data


class SupervisorUserSerializer(ModelSerializer):
"""
Expand Down Expand Up @@ -330,6 +335,7 @@ class UpdateUserSerializer(ModelSerializer):
)
skills = SerializerMethodField()
location = SerializerMethodField()
hidden_from_users = SerializerMethodField()

class Meta:
model = User
Expand All @@ -356,12 +362,16 @@ class Meta:
"user_type",
"background_color",
"is_active",
"hidden_from_users",
]

def get_reporting_to(self, obj):
reporting_to = obj.reporting_to.all()
return TeamSerializer(reporting_to, many=True).data

def get_hidden_from_users(self, obj):
return BasicUserSerializer(obj.hidden_from.all(), many=True).data

def get_skills(self, obj):
return UserSkillsSerializer(obj.skills.all(), many=True).data

Expand Down
14 changes: 6 additions & 8 deletions server/cshr/services/event.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
"""This file contains everything related to the Event model."""

from django.db.models.query import QuerySet
import datetime
from cshr.models.event import Event
from typing import List

from cshr.models.users import User


def get_event_by_id(id: str) -> Event:
def get_event_by_id(id: str) -> Event | None:
"""Return event who have the same id"""
try:
return Event.objects.get(id=int(id))
except Event.DoesNotExist:
return None


def get_all_events() -> Event:
def get_all_events() -> QuerySet[Event]:
"""Return all events"""
return Event.objects.all()


def filter_events_by_month_and_year(user: User, month: str, year: str) -> Event:
def filter_events_by_month_and_year(month: str, year: str) -> QuerySet[Event]:
"""
This function will filter all of events based on its yesr, month.
"""
events: List[Event] = Event.objects.filter(
return Event.objects.filter(
from_date__month=month, from_date__year=year
)
return events


def filter_events_by_day(day: int) -> List[Event]:
def filter_events_by_day(day: int) -> QuerySet[Event]:
"""Filter all users by birthdates"""
today = datetime.datetime.now()
return Event.objects.filter(
Expand Down
26 changes: 15 additions & 11 deletions server/cshr/services/landing_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,35 @@ def landing_page_calendar_functionality(user: User, month: str, year: str) -> Li
response: List[Any] = []

# Fetch vacations
vacations = filter_vacations_by_month_and_year(month, year).order_by("-created_at")
vacations = filter_vacations_by_month_and_year(
month, year, requesting_user=user
).order_by("-created_at")
for vacation in vacations:
vacation_data = wrap_vacation_request(vacation)
response.append(vacation_data)

# Fetch meetings
meetings = filter_meetings_by_month_and_year(user, month, year).order_by(
"-created_at"
)
meetings = filter_meetings_by_month_and_year(
month, year, requesting_user=user
).order_by("-created_at")
for meeting in meetings:
meeting_data = wrap_meeting_request(meeting)
response.append(meeting_data)

# Fetch users' birthdays
birthdates = filter_users_by_birth_month(month, requesting_user=user).order_by(
"-created_at"
) # type: ignore
for birthday_user in birthdates:
birthday_data = wrap_birthday_event(birthday_user)
response.append(birthday_data)

# Fetch events
events = filter_events_by_month_and_year(user, month, year).order_by("-created_at")
events = filter_events_by_month_and_year(month, year).order_by("-created_at") # type: ignore
for event in events:
event_data = wrap_event_request(event)
response.append(event_data)

# Fetch users' birthdays
users_birthdates = filter_users_by_birth_month(month).order_by("-created_at")
for birthday_user in users_birthdates:
birthday_data = wrap_birthday_event(birthday_user)
response.append(birthday_data)

# Fetch public holidays
public_holidays = filter_public_holidays_by_month_and_year(year, month).order_by(
"-created_at"
Expand Down
30 changes: 19 additions & 11 deletions server/cshr/services/meetings.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
"""This file contains everything related to the Meetings model."""

from typing import Optional
from django.db.models.query import QuerySet
from cshr.models.meetings import Meetings
from typing import List

from cshr.models.users import User


def get_meeting_by_id(id: str) -> Meetings:
def get_meeting_by_id(id: str) -> Meetings | None:
"""Return meeting who have the same id"""
try:
return Meetings.objects.get(id=int(id))
except Meetings.DoesNotExist:
return None


def get_all_meetings() -> Meetings:
def get_all_meetings(requesting_user: Optional[User] = None) -> QuerySet[Meetings]:
"""Return all meetings"""
return Meetings.objects.all()
queryset = Meetings.objects.all()
if requesting_user:
queryset = queryset.exclude(host_user__hidden_from=requesting_user)
return queryset


def filter_meetings_by_month_and_year(user: User, month: str, year: str) -> Meetings:
def filter_meetings_by_month_and_year(month: str, year: str, requesting_user: Optional[User] = None) -> QuerySet[Meetings]:
"""
This function will filter all of meetings based on its yesr, month.
"""
meetings: List[Meetings] = Meetings.objects.filter(
queryset = Meetings.objects.filter(
date__month=month, date__year=year
)
return meetings
if requesting_user:
queryset = queryset.exclude(host_user__hidden_from=requesting_user)
return queryset


def filter_meetings_by_day(year: int, month: int, day: int) -> List[Meetings]:
"""Filter all users by birthdates"""
return Meetings.objects.filter(date__year=year, date__month=month, date__day=day)
def filter_meetings_by_day(year: int, month: int, day: int, requesting_user: Optional[User] = None) -> QuerySet[Meetings]:
"""Filter meetings by day"""
queryset = Meetings.objects.filter(date__year=year, date__month=month, date__day=day)
if requesting_user:
queryset = queryset.exclude(host_user__hidden_from=requesting_user)
return queryset
Loading
Loading