From dd3e07311dc8944ea1eb64b3cdc742a56da6f91c Mon Sep 17 00:00:00 2001 From: Horacio Daniel Ros Date: Wed, 22 Jul 2026 16:27:46 -0300 Subject: [PATCH] feat: add public employee profile view and implement ratings review page components --- .../Companies/Companies.Adapter.Mongoose.ts | 5 + .../Models/Companies/Companies.Interface.ts | 3 + server/src/Models/Companies/Companies.ts | 9 +- server/src/Models/Employees/Employee.ts | 68 +++ .../Models/Employees/Employees.Interface.ts | 21 + .../Ratings/Ratings.Adapter.Mongoose.ts | 32 +- .../src/Models/Ratings/Ratings.Interface.ts | 14 +- server/src/Models/Ratings/Ratings.ts | 11 +- server/src/Models/Views/Views.ts | 2 + .../src/api/Employees/Employees.Controller.ts | 43 ++ server/src/api/Employees/Employees.Service.ts | 21 + server/src/api/Ratings/Ratings.Controller.ts | 4 +- server/src/api/Ratings/Ratings.Service.ts | 4 +- txclient/src/Models/Company.model.ts | 4 + txclient/src/Models/Employees.model.ts | 22 + txclient/src/Models/Ratings.model.ts | 10 + txclient/src/Services/Employees.Service.ts | 47 ++ txclient/src/Services/Ratings.Service.ts | 10 +- .../org/profile/[id]/edit/page.tsx | 43 +- .../OrganizationProfessionals.module.css | 114 ++++ .../OrganizationProfessionals.tsx | 157 +++++ .../ReviewsCarousel.module.css | 24 + .../ReviewsCarousel/ReviewsCarousel.tsx | 12 +- txclient/src/app/landing/org/[oid]/page.tsx | 538 +++++++++--------- .../src/app/landing/reviews/page.module.css | 365 ++++++++++++ txclient/src/app/landing/reviews/page.tsx | 322 +++++++++++ .../src/app/landing/service/[sid]/page.tsx | 20 +- 27 files changed, 1621 insertions(+), 304 deletions(-) create mode 100644 txclient/src/Models/Employees.model.ts create mode 100644 txclient/src/Services/Employees.Service.ts create mode 100644 txclient/src/app/components/OrganizationProfessionals/OrganizationProfessionals.module.css create mode 100644 txclient/src/app/components/OrganizationProfessionals/OrganizationProfessionals.tsx create mode 100644 txclient/src/app/landing/reviews/page.module.css create mode 100644 txclient/src/app/landing/reviews/page.tsx diff --git a/server/src/Models/Companies/Companies.Adapter.Mongoose.ts b/server/src/Models/Companies/Companies.Adapter.Mongoose.ts index 72278ac..e66174b 100644 --- a/server/src/Models/Companies/Companies.Adapter.Mongoose.ts +++ b/server/src/Models/Companies/Companies.Adapter.Mongoose.ts @@ -69,6 +69,7 @@ export class CompaniesAdapterMongoose implements ICompaniesAdapter { banned: { type: Boolean, required: false, default: false }, showPublicScores: { type: Boolean, required: false, default: true }, showPublicOpinions: { type: Boolean, required: false, default: true }, + showPublicProfessionals: { type: Boolean, required: false, default: true }, }); this.companyList = model("Company", this.schema); @@ -245,6 +246,10 @@ export class CompaniesAdapterMongoose implements ICompaniesAdapter { updateCompany.showPublicOpinions = data.showPublicOpinions; } + if (data.showPublicProfessionals !== undefined) { + updateCompany.showPublicProfessionals = data.showPublicProfessionals; + } + updateCompany.save(); //actualizo los campos correspondientes a la localizacion de los servicios de la organizacion. diff --git a/server/src/Models/Companies/Companies.Interface.ts b/server/src/Models/Companies/Companies.Interface.ts index e6beeae..57493a7 100644 --- a/server/src/Models/Companies/Companies.Interface.ts +++ b/server/src/Models/Companies/Companies.Interface.ts @@ -56,6 +56,7 @@ export type UpdateCompanyParams = { banned?: boolean; showPublicScores?: boolean; showPublicOpinions?: boolean; + showPublicProfessionals?: boolean; }; export type SetCompanyFileParams = { @@ -158,6 +159,7 @@ export interface ICompany { banned?: boolean; showPublicScores?: boolean; showPublicOpinions?: boolean; + showPublicProfessionals?: boolean; } export interface MyOranizationsView { @@ -209,6 +211,7 @@ export interface MyOranizationsView { banned?: boolean; showPublicScores?: boolean; showPublicOpinions?: boolean; + showPublicProfessionals?: boolean; } export interface ClientOrganizationView { diff --git a/server/src/Models/Companies/Companies.ts b/server/src/Models/Companies/Companies.ts index b1e4c3b..8308c7b 100644 --- a/server/src/Models/Companies/Companies.ts +++ b/server/src/Models/Companies/Companies.ts @@ -500,7 +500,11 @@ class CompaniesManager implements ICompaniesManager { throw new Error(NoPermissionMessage()); } - if (data.showPublicScores !== undefined || data.showPublicOpinions !== undefined) { + if ( + data.showPublicScores !== undefined || + data.showPublicOpinions !== undefined || + data.showPublicProfessionals !== undefined + ) { await this.validatePublicRatingFlagsPlan(String(companyCheck.ownerId)); } @@ -614,6 +618,7 @@ class CompaniesManager implements ICompaniesManager { onboardingCompleted: company.onboardingCompleted, showPublicScores: company.showPublicScores ?? true, showPublicOpinions: company.showPublicOpinions ?? true, + showPublicProfessionals: company.showPublicProfessionals ?? true, }; } @@ -735,6 +740,7 @@ class CompaniesManager implements ICompaniesManager { onboardingCompleted: company.onboardingCompleted, showPublicScores: company.showPublicScores ?? true, showPublicOpinions: company.showPublicOpinions ?? true, + showPublicProfessionals: company.showPublicProfessionals ?? true, }); } @@ -807,6 +813,7 @@ class CompaniesManager implements ICompaniesManager { onboardingCompleted: company.onboardingCompleted, showPublicScores: company.showPublicScores ?? true, showPublicOpinions: company.showPublicOpinions ?? true, + showPublicProfessionals: company.showPublicProfessionals ?? true, }); } } diff --git a/server/src/Models/Employees/Employee.ts b/server/src/Models/Employees/Employee.ts index 858052d..4da0a8a 100644 --- a/server/src/Models/Employees/Employee.ts +++ b/server/src/Models/Employees/Employee.ts @@ -8,6 +8,9 @@ import { PaginateEmployeesParams, PaginateEmployeesResults, CompanyEmployeesView, + PublicCompanyEmployeeView, + FindPublicEmployeeParams, + PublicEmployeeView, FindEmployeesParams, FindEmployeesByIdParams, UpdateEmployeeRolesParams, @@ -442,6 +445,71 @@ class EmployeeManager implements IEmployeesManager { return view; } + public async findPublicByCompanyId( + data: FindEmployeesParams + ): Promise { + const employees = await this.employees.find({ + companyId: data.companyId, + guestOk: true, + hostOk: true, + removed: { $ne: true }, + } as any); + + const view = await Promise.all( + employees.map(async (employee) => { + const userData = await UsersManager.users.findOne({ _id: employee.userId }); + + if (!userData) { + return null; + } + + const strFirstName = isNull(userData.firstName, ""); + const strLastName = isNull(userData.lastName, ""); + const strFullName = joinStrings([strFirstName, strLastName], " "); + + return { + employeeId: isNull(employee.id, ""), + userId: employee.userId, + firstName: strFirstName, + lastName: strLastName, + avatarUrl: getAvatar(userData.id, userData.avatar, strFullName), + }; + }) + ); + + return view.filter((employee): employee is PublicCompanyEmployeeView => employee !== null); + } + + public async findPublicByEmployeeId(data: FindPublicEmployeeParams): Promise { + const employee = await this.employees.findOne({ + _id: data.employeeId, + guestOk: true, + hostOk: true, + removed: { $ne: true }, + } as any); + + if (!employee) { + throw new Error("El colaborador no existe"); + } + + const userData = await UsersManager.users.findOne({ _id: employee.userId }); + + if (!userData) { + throw new Error("El colaborador no existe"); + } + + const strFirstName = isNull(userData.firstName, ""); + const strLastName = isNull(userData.lastName, ""); + const strFullName = joinStrings([strFirstName, strLastName], " "); + + return { + employeeId: isNull(employee.id, ""), + firstName: strFirstName, + lastName: strLastName, + avatarUrl: getAvatar(userData.id, userData.avatar, strFullName), + }; + } + public async findById(data: FindEmployeesByIdParams): Promise { const employee = await this.employees.findOne({ _id: data.id }); diff --git a/server/src/Models/Employees/Employees.Interface.ts b/server/src/Models/Employees/Employees.Interface.ts index 785d72f..4fc350b 100644 --- a/server/src/Models/Employees/Employees.Interface.ts +++ b/server/src/Models/Employees/Employees.Interface.ts @@ -119,6 +119,25 @@ export interface CompanyEmployeesView { }; } +export interface PublicCompanyEmployeeView { + employeeId: string; + userId: string; + firstName: string; + lastName: string; + avatarUrl: string; +} + +export interface FindPublicEmployeeParams { + employeeId: string; +} + +export interface PublicEmployeeView { + employeeId: string; + firstName: string; + lastName: string; + avatarUrl: string; +} + export interface IncompleteCollaboratorView { employeeId: string; companyId: string; @@ -143,6 +162,8 @@ export interface IEmployeesManager { updateEmployeeRoles(data: UpdateEmployeeRolesParams): Promise; update(data: UpdateEmployeeParams): Promise; findByCompanyId(data: FindEmployeesParams): Promise; + findPublicByCompanyId(data: FindEmployeesParams): Promise; + findPublicByEmployeeId(data: FindPublicEmployeeParams): Promise; findById(data: FindEmployeesByIdParams): Promise; textObjectFilter(data: FindEmployeesParams): Promise; deleteEmployeesByCompany(data: DeleteEmployeesByCompany): Promise; diff --git a/server/src/Models/Ratings/Ratings.Adapter.Mongoose.ts b/server/src/Models/Ratings/Ratings.Adapter.Mongoose.ts index e8b81f8..0d92205 100644 --- a/server/src/Models/Ratings/Ratings.Adapter.Mongoose.ts +++ b/server/src/Models/Ratings/Ratings.Adapter.Mongoose.ts @@ -8,8 +8,8 @@ import { RATING_COMMENT_MAX_LENGTH, RatingTargetSummaryParams, RatingTargetSummaryResult, - RatingTargetReviewItem, RatingTargetReviewsParams, + RatingTargetReviewsResult, RatingTargetType, } from "./Ratings.Interface"; @@ -127,20 +127,26 @@ export class RatingsAdapterMongoose implements IRatingsAdapter { }; } - public async reviewsByTarget(filters: RatingTargetReviewsParams): Promise { + public async reviewsByTarget(filters: RatingTargetReviewsParams): Promise { + const page = filters.page || 1; + const pageSize = filters.pageSize || filters.limit || 20; + const targetReviewsFilter = { + targetType: filters.targetType, + targetId: new Types.ObjectId(filters.targetId), + comment: { $regex: /\S/ }, + }; + + const total = await this.ratingsList.countDocuments(targetReviewsFilter).exec(); const ratings = await this.ratingsList - .find({ - targetType: filters.targetType, - targetId: new Types.ObjectId(filters.targetId), - comment: { $regex: /\S/ }, - }) + .find(targetReviewsFilter) .sort({ score: -1, createdAt: -1 }) - .limit(filters.limit || 20) + .skip((page - 1) * pageSize) + .limit(pageSize) .select("score comment createdAt userId") .populate("userId", "firstName lastName avatar") .exec(); - return (ratings as unknown as RatingReviewDocument[]).map((rating) => { + const reviews = (ratings as unknown as RatingReviewDocument[]).map((rating) => { const user = rating.userId; const displayName = user ? joinStrings([user.firstName, user.lastName], " ") : ""; const authorName = displayName || GENERIC_REVIEW_AUTHOR; @@ -156,6 +162,14 @@ export class RatingsAdapterMongoose implements IRatingsAdapter { }, }; }); + + return { + reviews, + total, + page, + pageSize, + totalPages: Math.ceil(total / pageSize), + }; } private mapRating(rating: IRatingDocument): IRating { diff --git a/server/src/Models/Ratings/Ratings.Interface.ts b/server/src/Models/Ratings/Ratings.Interface.ts index cc40644..48f4867 100644 --- a/server/src/Models/Ratings/Ratings.Interface.ts +++ b/server/src/Models/Ratings/Ratings.Interface.ts @@ -53,6 +53,8 @@ export type RatingTargetSummaryResult = { }; export type RatingTargetReviewsParams = RatingTargetSummaryParams & { + page?: number; + pageSize?: number; limit?: number; }; @@ -67,6 +69,14 @@ export type RatingTargetReviewItem = { }; }; +export type RatingTargetReviewsResult = { + reviews: RatingTargetReviewItem[]; + total: number; + page: number; + pageSize: number; + totalPages: number; +}; + export interface IRating { id?: string; appointmentId: string; @@ -101,7 +111,7 @@ export interface IRatingsAdapter { count(filters: FindRatingsParams): Promise; summaryByTarget(filters: RatingTargetSummaryParams): Promise; summaryByTargets(filters: { targetType: RatingTargetType; targetIds: string[] }): Promise; - reviewsByTarget(filters: RatingTargetReviewsParams): Promise; + reviewsByTarget(filters: RatingTargetReviewsParams): Promise; } export interface IRatingsManager { @@ -111,5 +121,5 @@ export interface IRatingsManager { countByUser(data: CountRatingsByUserParams): Promise; professionalSummaryByUser(data: ProfessionalRatingSummaryByUserParams): Promise; summaryByTarget(data: RatingTargetSummaryParams): Promise; - reviewsByTarget(data: RatingTargetReviewsParams): Promise; + reviewsByTarget(data: RatingTargetReviewsParams): Promise; } diff --git a/server/src/Models/Ratings/Ratings.ts b/server/src/Models/Ratings/Ratings.ts index 2061521..8bc614a 100644 --- a/server/src/Models/Ratings/Ratings.ts +++ b/server/src/Models/Ratings/Ratings.ts @@ -20,8 +20,8 @@ import { RATING_COMMENT_MAX_LENGTH, RatingTargetSummaryParams, RatingTargetSummaryResult, - RatingTargetReviewItem, RatingTargetReviewsParams, + RatingTargetReviewsResult, RatingTargetType, } from "./Ratings.Interface"; @@ -221,16 +221,21 @@ class RatingsManager implements IRatingsManager { return this.ratings.summaryByTarget(data); } - public async reviewsByTarget(data: RatingTargetReviewsParams): Promise { + public async reviewsByTarget(data: RatingTargetReviewsParams): Promise { this.validateTargetType(data.targetType); if (!Types.ObjectId.isValid(data.targetId)) { throw new Error("No se ha proporcionado un objetivo válido."); } + const pageSize = Math.min(Math.max(data.pageSize || data.limit || TARGET_REVIEWS_LIMIT, 1), TARGET_REVIEWS_LIMIT); + const page = Math.max(data.page || 1, 1); + return this.ratings.reviewsByTarget({ ...data, - limit: Math.min(Math.max(data.limit || TARGET_REVIEWS_LIMIT, 1), TARGET_REVIEWS_LIMIT), + page, + pageSize, + limit: pageSize, }); } diff --git a/server/src/Models/Views/Views.ts b/server/src/Models/Views/Views.ts index b9451e9..f08e985 100644 --- a/server/src/Models/Views/Views.ts +++ b/server/src/Models/Views/Views.ts @@ -57,6 +57,7 @@ export type PublicOrganizationView = { appointmentAlert: string; showPublicScores: boolean; showPublicOpinions: boolean; + showPublicProfessionals: boolean; }; export type PublicOrganizationServiceViewParams = { @@ -167,6 +168,7 @@ class Views { appointmentAlert: isNull(company.appointmentAlert, ""), showPublicScores: company.showPublicScores ?? true, showPublicOpinions: company.showPublicOpinions ?? true, + showPublicProfessionals: company.showPublicProfessionals ?? true, }; } diff --git a/server/src/api/Employees/Employees.Controller.ts b/server/src/api/Employees/Employees.Controller.ts index c468e23..ae55a83 100644 --- a/server/src/api/Employees/Employees.Controller.ts +++ b/server/src/api/Employees/Employees.Controller.ts @@ -9,6 +9,9 @@ import { PaginateEmployeesResults, ValidateEmployeeParams, CompanyEmployeesView, + PublicCompanyEmployeeView, + FindPublicEmployeeParams, + PublicEmployeeView, FindEmployeesByIdParams, UpdateEmployeeRolesParams, UpdateEmployeeParams, @@ -117,6 +120,46 @@ export class GetEmployeesByCompanyController extends Controller { } } +@Route("employees/public-by-company") +export class GetPublicEmployeesByCompanyController extends Controller { + @Response(500, "Ha ocurrido un error") + @SuccessResponse(200, "Done") + @Post() + public async findPublicByCompanyId( + @Body() requestBody: FindEmployeesParams + ): Promise { + try { + const employees = await new EmployeesService().findPublicByCompanyId(requestBody); + this.setStatus(200); + return employees; + } catch (e) { + const errorOccurred: Error = e as Error; + this.setStatus(500); + return new ApiValidationError(500, errorOccurred.message); + } + } +} + +@Route("employees/public-by-id") +export class GetPublicEmployeeByIdController extends Controller { + @Response(500, "Ha ocurrido un error") + @SuccessResponse(200, "Done") + @Post() + public async findPublicByEmployeeId( + @Body() requestBody: FindPublicEmployeeParams + ): Promise { + try { + const employee = await new EmployeesService().findPublicByEmployeeId(requestBody); + this.setStatus(200); + return employee; + } catch (e) { + const errorOccurred: Error = e as Error; + this.setStatus(500); + return new ApiValidationError(500, errorOccurred.message); + } + } +} + @Route("employees/get-by-id") export class GetEmployeesByIdController extends Controller { @Response(500, "Ha ocurrido un error") diff --git a/server/src/api/Employees/Employees.Service.ts b/server/src/api/Employees/Employees.Service.ts index 2b91bf2..6662b81 100644 --- a/server/src/api/Employees/Employees.Service.ts +++ b/server/src/api/Employees/Employees.Service.ts @@ -8,6 +8,9 @@ import { PaginateEmployeesParams, PaginateEmployeesResults, CompanyEmployeesView, + PublicCompanyEmployeeView, + FindPublicEmployeeParams, + PublicEmployeeView, FindEmployeesByIdParams, UpdateEmployeeRolesParams, UpdateEmployeeParams, @@ -60,6 +63,24 @@ export class EmployeesService { return employees; } + public async findPublicByCompanyId( + data: FindEmployeesParams + ): Promise { + await connect(`${process.env.DATABASE_CONNECTION}`); + + const employees = await EmployeesList.findPublicByCompanyId(data); + + return employees; + } + + public async findPublicByEmployeeId(data: FindPublicEmployeeParams): Promise { + await connect(`${process.env.DATABASE_CONNECTION}`); + + const employee = await EmployeesList.findPublicByEmployeeId(data); + + return employee; + } + public async findById(data: FindEmployeesByIdParams): Promise { await connect(`${process.env.DATABASE_CONNECTION}`); diff --git a/server/src/api/Ratings/Ratings.Controller.ts b/server/src/api/Ratings/Ratings.Controller.ts index 5dc6722..4a05028 100644 --- a/server/src/api/Ratings/Ratings.Controller.ts +++ b/server/src/api/Ratings/Ratings.Controller.ts @@ -7,8 +7,8 @@ import { PendingRatingItem, PendingRatingsByUserParams, ProfessionalRatingSummaryByUserParams, - RatingTargetReviewItem, RatingTargetReviewsParams, + RatingTargetReviewsResult, RatingTargetSummaryParams, RatingTargetSummaryResult, } from "../../Models/Ratings/Ratings.Interface"; @@ -126,7 +126,7 @@ export class RatingTargetReviewsController extends Controller { @Post() public async reviewsByTarget( @Body() requestBody: RatingTargetReviewsParams - ): Promise { + ): Promise { try { const ratingReviews = await new RatingsService().reviewsByTarget(requestBody); this.setStatus(200); diff --git a/server/src/api/Ratings/Ratings.Service.ts b/server/src/api/Ratings/Ratings.Service.ts index 571899a..ac6ede6 100644 --- a/server/src/api/Ratings/Ratings.Service.ts +++ b/server/src/api/Ratings/Ratings.Service.ts @@ -10,8 +10,8 @@ import { ProfessionalRatingSummaryByUserParams, RatingTargetSummaryParams, RatingTargetSummaryResult, - RatingTargetReviewItem, RatingTargetReviewsParams, + RatingTargetReviewsResult, } from "../../Models/Ratings/Ratings.Interface"; export class RatingsService { @@ -40,7 +40,7 @@ export class RatingsService { return await RatingsList.summaryByTarget(data); } - public async reviewsByTarget(data: RatingTargetReviewsParams): Promise { + public async reviewsByTarget(data: RatingTargetReviewsParams): Promise { await connect(`${process.env.DATABASE_CONNECTION}`); return await RatingsList.reviewsByTarget(data); } diff --git a/txclient/src/Models/Company.model.ts b/txclient/src/Models/Company.model.ts index 6f626a4..e1151f6 100644 --- a/txclient/src/Models/Company.model.ts +++ b/txclient/src/Models/Company.model.ts @@ -47,6 +47,7 @@ export interface MyOranizationsView { onboardingCompleted?: boolean; showPublicScores: boolean; showPublicOpinions: boolean; + showPublicProfessionals: boolean; } export type CreateCompanyParams = { @@ -97,6 +98,7 @@ export type UpdateCompanyParams = { onboardingCompleted?: boolean; showPublicScores?: boolean; showPublicOpinions?: boolean; + showPublicProfessionals?: boolean; }; export type DeleteCompanyParams = { @@ -164,6 +166,7 @@ export interface ICompany { banned?: boolean; showPublicScores?: boolean; showPublicOpinions?: boolean; + showPublicProfessionals?: boolean; } export type PublicOrganizationViewParams = { @@ -192,6 +195,7 @@ export type PublicOrganizationView = { banned?: boolean; showPublicScores: boolean; showPublicOpinions: boolean; + showPublicProfessionals: boolean; }; export type SetNotificationAutoParams = { diff --git a/txclient/src/Models/Employees.model.ts b/txclient/src/Models/Employees.model.ts new file mode 100644 index 0000000..3277072 --- /dev/null +++ b/txclient/src/Models/Employees.model.ts @@ -0,0 +1,22 @@ +export type PublicCompanyEmployeeView = { + employeeId: string; + userId: string; + firstName: string; + lastName: string; + avatarUrl: string; +}; + +export type FindPublicCompanyEmployeesParams = { + companyId: string; +}; + +export type FindPublicEmployeeParams = { + employeeId: string; +}; + +export type PublicEmployeeView = { + employeeId: string; + firstName: string; + lastName: string; + avatarUrl: string; +}; diff --git a/txclient/src/Models/Ratings.model.ts b/txclient/src/Models/Ratings.model.ts index b10840b..57a158e 100644 --- a/txclient/src/Models/Ratings.model.ts +++ b/txclient/src/Models/Ratings.model.ts @@ -42,6 +42,8 @@ export type RatingTargetSummaryResult = { }; export type RatingTargetReviewsParams = RatingTargetSummaryParams & { + page?: number; + pageSize?: number; limit?: number; }; @@ -56,6 +58,14 @@ export type RatingTargetReviewItem = { }; }; +export type RatingTargetReviewsResult = { + reviews: RatingTargetReviewItem[]; + total: number; + page: number; + pageSize: number; + totalPages: number; +}; + export interface PendingRatingItem { appointmentId: string; appointmentDate: string; diff --git a/txclient/src/Services/Employees.Service.ts b/txclient/src/Services/Employees.Service.ts new file mode 100644 index 0000000..a47b082 --- /dev/null +++ b/txclient/src/Services/Employees.Service.ts @@ -0,0 +1,47 @@ +import { ApiError } from "@core/Models/Server.Error.model"; +import { + FindPublicCompanyEmployeesParams, + FindPublicEmployeeParams, + PublicCompanyEmployeeView, + PublicEmployeeView, +} from "@core/Models/Employees.model"; +import ApiRequest from "@services/Api.Service"; +import * as Yup from "yup"; + +const schemaFindPublicCompanyEmployees = Yup.object().shape({ + companyId: Yup.string().required("No se ha proporcionado la organización."), +}); + +const schemaFindPublicEmployee = Yup.object().shape({ + employeeId: Yup.string().required("No se ha proporcionado el profesional."), +}); + +export const findPublicCompanyEmployees = async ( + data: FindPublicCompanyEmployeesParams +): Promise => { + return new Promise((resolve, reject) => { + schemaFindPublicCompanyEmployees + .validate(data, { abortEarly: true }) + .then(() => { + resolve(ApiRequest.post("employees/public-by-company", data)); + }) + .catch((error) => { + reject(new ApiError(500, error.message)); + }); + }); +}; + +export const findPublicEmployee = async ( + data: FindPublicEmployeeParams +): Promise => { + return new Promise((resolve, reject) => { + schemaFindPublicEmployee + .validate(data, { abortEarly: true }) + .then(() => { + resolve(ApiRequest.post("employees/public-by-id", data)); + }) + .catch((error) => { + reject(new ApiError(500, error.message)); + }); + }); +}; diff --git a/txclient/src/Services/Ratings.Service.ts b/txclient/src/Services/Ratings.Service.ts index fe04a4e..c1aeae9 100644 --- a/txclient/src/Services/Ratings.Service.ts +++ b/txclient/src/Services/Ratings.Service.ts @@ -11,8 +11,8 @@ import { RATING_COMMENT_MAX_LENGTH, RatingTargetSummaryParams, RatingTargetSummaryResult, - RatingTargetReviewItem, RatingTargetReviewsParams, + RatingTargetReviewsResult, RatingTargetType, } from "@core/Models/Ratings.model"; import * as Yup from "yup"; @@ -48,6 +48,8 @@ const schemaRatingTargetSummary = Yup.object().shape({ }); const schemaRatingTargetReviews = schemaRatingTargetSummary.shape({ + page: Yup.number().integer().min(1), + pageSize: Yup.number().integer().min(1).max(20), limit: Yup.number().integer().min(1).max(20), }); @@ -118,12 +120,12 @@ export const ratingTargetSummary = async (data: RatingTargetSummaryParams): Prom }); }; -export const ratingTargetReviews = async (data: RatingTargetReviewsParams): Promise => { - return new Promise((resolve, reject) => { +export const ratingTargetReviews = async (data: RatingTargetReviewsParams): Promise => { + return new Promise((resolve, reject) => { schemaRatingTargetReviews .validate(data, { abortEarly: true }) .then(() => { - resolve(ApiRequest.post("ratings/target-reviews", data)); + resolve(ApiRequest.post("ratings/target-reviews", data)); }) .catch((error) => { reject(new ApiError(500, error.message)); diff --git a/txclient/src/app/admin/(organization-profile)/org/profile/[id]/edit/page.tsx b/txclient/src/app/admin/(organization-profile)/org/profile/[id]/edit/page.tsx index 51e480e..8d73484 100644 --- a/txclient/src/app/admin/(organization-profile)/org/profile/[id]/edit/page.tsx +++ b/txclient/src/app/admin/(organization-profile)/org/profile/[id]/edit/page.tsx @@ -62,6 +62,7 @@ export default function OrganizationEdit() { const [orgSlug, setOrgSlug] = useState(""); const [showPublicScores, setShowPublicScores] = useState(true); const [showPublicOpinions, setShowPublicOpinions] = useState(true); + const [showPublicProfessionals, setShowPublicProfessionals] = useState(true); const [servicesShowPublicScores, setServicesShowPublicScores] = useState(true); const [servicesShowPublicOpinions, setServicesShowPublicOpinions] = useState(true); const [originalServicesShowPublicScores, setOriginalServicesShowPublicScores] = useState(true); @@ -76,7 +77,7 @@ export default function OrganizationEdit() { const [combosLoaded, setCombosLoaded] = useState(false); const ownerSubscription = SessionInfo.organizationSubscriptions[id]; - const canEditPublicRatingFlags = (ownerSubscription?.plan.price || 0) > 0; + const canEditPublicVisibilityFlags = (ownerSubscription?.plan.price || 0) > 0; const [categoryFilterContent] = useState({ header: ( @@ -242,6 +243,11 @@ export default function OrganizationEdit() { setOrgPublished(organization.published); setShowPublicScores(organization.showPublicScores !== undefined ? organization.showPublicScores : true); setShowPublicOpinions(organization.showPublicOpinions !== undefined ? organization.showPublicOpinions : true); + setShowPublicProfessionals( + organization.showPublicProfessionals !== undefined + ? organization.showPublicProfessionals + : true + ); setServices(loadedServices); setServicesShowPublicScores( loadedServices.every((service) => service.showPublicScores !== false) @@ -302,10 +308,11 @@ export default function OrganizationEdit() { templateWapNotifId: templateWapNotifId === "" ? undefined : templateWapNotifId, templateWapAltaId: templateWapAltaId === "" ? undefined : templateWapAltaId, templateWapCancellationId: templateWapCancellationId === "" ? undefined : templateWapCancellationId, - ...(canEditPublicRatingFlags + ...(canEditPublicVisibilityFlags ? { showPublicScores, showPublicOpinions, + showPublicProfessionals, } : {}), }; @@ -378,7 +385,7 @@ export default function OrganizationEdit() { const servicesShowPublicOpinionsChanged = originalServicesShowPublicOpinions !== servicesShowPublicOpinions; const changedServices = - canEditPublicRatingFlags && + canEditPublicVisibilityFlags && (servicesShowPublicScoresChanged || servicesShowPublicOpinionsChanged) ? services : []; @@ -574,13 +581,18 @@ export default function OrganizationEdit() { /> - Visibilidad pública: Elegí si querés mostrar puntuaciones y opiniones en el perfil público y las páginas de reserva. Disponible para planes pagos. + Visibilidad pública: Elegí si querés mostrar puntuaciones, opiniones y profesionales en el perfil público y las páginas de reserva. Disponible para planes pagos. + {!canEditPublicVisibilityFlags && ( + + Para modificar las opciones de visibilidad pública de opiniones, puntuaciones y profesionales de esta organización y sus servicios, el propietario de la organización necesita un plan pago. + + )}
)}