feat: add CollaboratorPicker component and integrate into CollaboratorScheduleSummaryFlow

- Implemented CollaboratorPicker for selecting employees with support for single and multiple selections.
- Created CollaboratorScheduleSummaryFlow to display collaborator schedules and exceptions.
- Added ServicePublicationFlow for managing service visibility on public pages.
- Introduced CSS styles for ServicePrivate component to enhance UI.
This commit is contained in:
2026-08-22 12:34:09 -03:00
parent b6399d28fc
commit 33743f12b9
25 changed files with 2145 additions and 287 deletions
@@ -0,0 +1,45 @@
import { ScheduleItem } from "../Schedules/Schedules.Interface";
import { ISchedulesDisabled } from "../SchedulesDisabled/SchedulesDisabled.Interface";
import { ISchedulesOverrides } from "../SchedulesOverrides/SchedulesOverrides.Interface";
export type CollaboratorScheduleDetailsParams = {
companyId: string;
employeeId: string;
sessionUser: string;
fromDate?: Date;
};
export type CollaboratorScheduleDetailsCollaborator = {
employeeId: string;
fullName: string;
email: string;
avatar: string;
userId: string;
};
export type CollaboratorWeeklySchedule = {
companyId: string;
employeeId: string;
weekDay: number;
scheduleId: string;
schedules: ScheduleItem[];
};
export type CollaboratorScheduleServiceSummary = {
id: string;
name: string;
};
export type CollaboratorScheduleDetailsView = {
collaborator: CollaboratorScheduleDetailsCollaborator;
servicesById: Record<string, CollaboratorScheduleServiceSummary>;
weeklySchedules: CollaboratorWeeklySchedule[];
futureDisabledSchedules: ISchedulesDisabled[];
futureOverrides: ISchedulesOverrides[];
};
export interface IAdminAssistantManager {
getCollaboratorScheduleDetails(
data: CollaboratorScheduleDetailsParams
): Promise<CollaboratorScheduleDetailsView>;
}
@@ -0,0 +1,98 @@
import dayjs from "dayjs";
import EmployeesList from "../Employees/Employee";
import ServicesList from "../Services/Service";
import SchedulesList from "../Schedules/Schedules";
import SchedulesDisabledList from "../SchedulesDisabled/SchedulesDisabled";
import SchedulesOverridesList from "../SchedulesOverrides/SchedulesOverrides";
import UsersManager from "../Users/Users";
import getAvatar from "../../helpers/getAvatar";
import { joinStrings } from "../../helpers/String";
import { validatePermissionsByCompany, validateSessionUser } from "../../helpers/check";
import {
CollaboratorScheduleDetailsParams,
CollaboratorScheduleDetailsView,
IAdminAssistantManager,
} from "./AdminAssistant.Interface";
class AdminAssistantManager implements IAdminAssistantManager {
public async getCollaboratorScheduleDetails(
data: CollaboratorScheduleDetailsParams
): Promise<CollaboratorScheduleDetailsView> {
await validateSessionUser({ sessionUser: data.sessionUser });
await validatePermissionsByCompany({ companyId: data.companyId, sessionUser: data.sessionUser });
const employee = await EmployeesList.employees.findOne({
_id: data.employeeId,
companyId: data.companyId,
});
if (!employee) {
throw new Error("El colaborador no existe o no pertenece a la organización.");
}
const user = await UsersManager.users.findOne({ _id: String(employee.userId) });
const snapshot = employee.profileSnapshot || {};
const fullName = user
? joinStrings([user.firstName, user.lastName], " ")
: joinStrings([snapshot.firstName, snapshot.lastName], " ");
const email = user?.email || snapshot.email || "";
const avatar = user
? getAvatar(String(user.id), user.avatar, fullName)
: getAvatar(String(employee.userId), snapshot.avatar || "", fullName);
const fromDate = data.fromDate ? dayjs(data.fromDate).startOf("day").toDate() : dayjs().startOf("day").toDate();
const weeklySchedules = await SchedulesList.find({
companyId: data.companyId,
employeeId: data.employeeId,
});
const futureDisabledSchedules = await SchedulesDisabledList.find({
companyId: data.companyId,
employeeId: data.employeeId,
endDate: { $gte: fromDate } as any,
sessionUser: data.sessionUser,
});
const futureOverrides = await SchedulesOverridesList.find({
companyId: data.companyId,
employeeId: data.employeeId,
date: { $gte: fromDate } as any,
});
const companyServices = await ServicesList.findByCompanyId({ companyId: data.companyId });
const servicesById = companyServices.reduce((index, service) => {
index[service.id] = {
id: service.id,
name: service.name,
};
return index;
}, {} as CollaboratorScheduleDetailsView["servicesById"]);
return {
collaborator: {
employeeId: String(employee.id || employee._id),
fullName: fullName || email || "Colaborador",
email,
avatar,
userId: String(employee.userId),
},
servicesById,
weeklySchedules: weeklySchedules
.sort((a, b) => a.weekDay - b.weekDay)
.map((schedule) => ({
companyId: String(schedule.companyId),
employeeId: String(schedule.employeeId),
scheduleId: String(schedule.id || (schedule as any)._id || ""),
weekDay: schedule.weekDay,
schedules: schedule.schedules || [],
})),
futureDisabledSchedules: futureDisabledSchedules.sort((a, b) =>
new Date(a.startDate).getTime() - new Date(b.startDate).getTime()
),
futureOverrides: futureOverrides.sort((a, b) =>
new Date(a.date).getTime() - new Date(b.date).getTime()
),
};
}
}
const AdminAssistantList = new AdminAssistantManager();
export default AdminAssistantList;