From 546b66c9720fc8842bb279e08d578a4baf0c3797 Mon Sep 17 00:00:00 2001 From: Horacio Daniel Ros Date: Mon, 14 Sep 2026 15:07:02 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20agregar=20funcionalidad=20de=20edici?= =?UTF-8?q?=C3=B3n=20de=20imagen=20en=20el=20servicio=20y=20mejorar=20la?= =?UTF-8?q?=20gesti=C3=B3n=20de=20permisos=20de=20administrador?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../6aa8001acb16345c5a340947/image.jpg | Bin 0 -> 5004 bytes .../6aa8001acb16345c5a340947/image.webp | Bin 0 -> 3544 bytes .../components/ProductItem/ProductItem.tsx | 12 +- .../components/ProductItem/style.module.css | 26 +++++ .../landing/service/[sid]/Service.Service.ts | 10 ++ .../service/[sid]/ServiceDetailCard.tsx | 59 ++++++++++ .../service/[sid]/ServiceImageEditPopup.tsx | 105 ++++++++++++++++++ .../src/app/landing/service/[sid]/page.tsx | 4 +- 8 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 server/uploads/services/6aa8001acb16345c5a340947/image.jpg create mode 100644 server/uploads/services/6aa8001acb16345c5a340947/image.webp create mode 100644 txclient/src/app/landing/service/[sid]/Service.Service.ts create mode 100644 txclient/src/app/landing/service/[sid]/ServiceDetailCard.tsx create mode 100644 txclient/src/app/landing/service/[sid]/ServiceImageEditPopup.tsx diff --git a/server/uploads/services/6aa8001acb16345c5a340947/image.jpg b/server/uploads/services/6aa8001acb16345c5a340947/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..add9632ca8f2856151c28e0a285686ffc1f381ff GIT binary patch literal 5004 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 literal 0 HcmV?d00001 diff --git a/server/uploads/services/6aa8001acb16345c5a340947/image.webp b/server/uploads/services/6aa8001acb16345c5a340947/image.webp new file mode 100644 index 0000000000000000000000000000000000000000..0aff6b526fa6e1fc137f5d2b44c1a14c44e63f0d GIT binary patch literal 3544 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 ShfR5B-p}L!0000000007G~hr0 literal 0 HcmV?d00001 diff --git a/txclient/src/app/components/ProductItem/ProductItem.tsx b/txclient/src/app/components/ProductItem/ProductItem.tsx index 0aebed4..6aac845 100644 --- a/txclient/src/app/components/ProductItem/ProductItem.tsx +++ b/txclient/src/app/components/ProductItem/ProductItem.tsx @@ -6,6 +6,7 @@ import { PublicServiceView } from "@models/Service.model"; import style from "./style.module.css"; import { getServiceImage } from "@core/helpers/getServiceImage"; import ServiceRatingSummary from "@components/ServiceRatingSummary/ServiceRatingSummary"; +import EditIcon from "@mui/icons-material/Edit"; export type FindResultsViewMode = "grid" | "card"; @@ -14,6 +15,8 @@ export interface ProductItemProps { width?: number | string; viewMode?: FindResultsViewMode; dragging?: boolean; + isAdmin?: boolean; + onImageEdit?: () => void; } export default function ProductItem(props: ProductItemProps) { @@ -21,6 +24,8 @@ export default function ProductItem(props: ProductItemProps) { const { product } = props; const { viewMode = "card" } = props; const { dragging = false } = props; + const { isAdmin = false } = props; + const { onImageEdit } = props; const { goTo } = useNavigation(); return ( @@ -38,7 +43,7 @@ export default function ProductItem(props: ProductItemProps) { textAlign: "center", padding: "10px", border: "solid 1px rgba(0, 0, 0, 0.1)", - cursor: "pointer", + cursor: isAdmin ? "default" : "pointer", position: "relative", }} onClick={() => { @@ -58,6 +63,11 @@ export default function ProductItem(props: ProductItemProps) { display: "block", }} /> + {isAdmin && ( + { e.stopPropagation(); onImageEdit?.(); }}> + + + )} => { + return ApiRequest.postFile("services/upload-image", data); +}; + +export const checkOrgAdmin = async (companyId: string, sessionUser: string): Promise => { + return ApiRequest.post("companies/check-admin", { companyId, sessionUser }); +}; diff --git a/txclient/src/app/landing/service/[sid]/ServiceDetailCard.tsx b/txclient/src/app/landing/service/[sid]/ServiceDetailCard.tsx new file mode 100644 index 0000000..706e58b --- /dev/null +++ b/txclient/src/app/landing/service/[sid]/ServiceDetailCard.tsx @@ -0,0 +1,59 @@ +"use client"; +import { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import ProductItem from "@core/app/components/ProductItem/ProductItem"; +import { PublicServiceView } from "@models/Service.model"; +import { PublicOrganizationView } from "@models/Company.model"; +import ServiceImageEditPopup from "./ServiceImageEditPopup"; +import { useSessionStore } from "@core/Store/Sesion.Store"; +import { checkOrgAdmin } from "./Service.Service"; + +interface ServiceDetailCardProps { + service: PublicServiceView; + organization: PublicOrganizationView; +} + +export default function ServiceDetailCard({ service, organization }: ServiceDetailCardProps) { + const router = useRouter(); + const SessionInfo = useSessionStore(); + const [isAdmin, setIsAdmin] = useState(false); + const [showEditPopup, setShowEditPopup] = useState(false); + + useEffect(() => { + if (!SessionInfo.userId || !organization.id || !service.id) return; + + checkOrgAdmin(organization.id, SessionInfo.userId) + .then((result) => { + setIsAdmin(result); + }) + .catch(() => { + setIsAdmin(false); + }); + }, [SessionInfo.userId, organization.id, service.id]); + + const handleImageSaved = () => { + setShowEditPopup(false); + router.refresh(); + }; + + return ( + <> + setShowEditPopup(true)} + /> + {isAdmin && ( + setShowEditPopup(false)} + serviceId={service.id} + companyId={organization.id} + currentImage={service.image} + onSaved={handleImageSaved} + /> + )} + + ); +} diff --git a/txclient/src/app/landing/service/[sid]/ServiceImageEditPopup.tsx b/txclient/src/app/landing/service/[sid]/ServiceImageEditPopup.tsx new file mode 100644 index 0000000..bf191ea --- /dev/null +++ b/txclient/src/app/landing/service/[sid]/ServiceImageEditPopup.tsx @@ -0,0 +1,105 @@ +"use client"; +import { useState } from "react"; +import { IconButton, Dialog, DialogActions, DialogContent, DialogTitle, Button } from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import turnosXpressTheme from "@core/app/theme/turnosXpress"; +import { ThemeProvider } from "@emotion/react"; +import ImagePicker from "@components/ImagePicker/ImagePicker"; +import { useAlert } from "@core/Store/Alert.Store"; +import { useSessionStore } from "@core/Store/Sesion.Store"; +import { uploadServiceImage } from "./Service.Service"; + +interface ServiceImageEditPopupProps { + open: boolean; + onClose: () => void; + serviceId: string; + companyId: string; + currentImage: string; + onSaved: () => void; +} + +export default function ServiceImageEditPopup(props: ServiceImageEditPopupProps) { + const { open, onClose, serviceId, companyId, currentImage, onSaved } = props; + const alert = useAlert(); + const SessionInfo = useSessionStore(); + const [loading, setLoading] = useState(false); + + const handleImageChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + + setLoading(true); + + try { + const formData = new FormData(); + formData.append("companyId", companyId); + formData.append("serviceId", serviceId); + formData.append("sessionUser", SessionInfo.userId); + formData.append("file", file); + + uploadServiceImage(formData) + .then(() => { + alert.showSuccess("Imagen actualizada"); + onSaved(); + }) + .catch((error) => { + const formatted = error instanceof Error ? error.message : "Error al guardar la imagen"; + alert.showError(formatted || "Error al guardar la imagen"); + }) + .finally(() => { + setLoading(false); + }); + } catch (error) { + const formatted = error instanceof Error ? error.message : "Error al guardar la imagen"; + alert.showError(formatted || "Error al guardar la imagen"); + setLoading(false); + } + }; + + return ( + + + + Editar imagen del servicio + + + + + + + + + + + + + + + + ); +} diff --git a/txclient/src/app/landing/service/[sid]/page.tsx b/txclient/src/app/landing/service/[sid]/page.tsx index b42f198..cd96b32 100644 --- a/txclient/src/app/landing/service/[sid]/page.tsx +++ b/txclient/src/app/landing/service/[sid]/page.tsx @@ -2,7 +2,6 @@ import AnimatedContainer from "@components/AnimatedConainer/AnimatedContainer"; import style from "./profile.module.css"; import ApiServerService from "@services/Api.Server.Service"; import { PublicOrganizationServiceView } from "@core/Models/Service.model"; -import ProductItem from "@core/app/components/ProductItem/ProductItem"; import Appointment from "./components/Appointment/Appointment"; import OrganizationHeader from "@components/Home/OrganizationHeader/OrganizationHeader"; import ProductGallery from "@core/app/components/Home/ProductsGallery/ProductGallery"; @@ -12,6 +11,7 @@ import ServicePrivate from "@core/app/components/ServicePrivate/ServicePrivate"; import EvangelizeBanner from "@components/EvangelizeBanner/EvangelizeBanner"; import ReviewsCarousel from "@components/ReviewsCarousel/ReviewsCarousel"; import { RatingTargetType } from "@core/Models/Ratings.model"; +import ServiceDetailCard from "./ServiceDetailCard"; const GetServiceData = async (serviceId: string): Promise => { try { @@ -60,7 +60,7 @@ export default async function OrganizationPublicProfile({ params }: Props) {
- +