feat: permitir a admins personalizar header desde landing pública

- Nuevo endpoint POST /companies/check-admin para verificar rol de admin
- Componente HeaderEditPopup con color pickers e image picker
- Botón flotante de editar sobre el header cuando el usuario es admin
- Popup tipo drawer inferior con formulario de personalización
- Reutiliza APIs existentes: companies/update y companies/upload-header
This commit is contained in:
2026-09-14 12:34:59 -03:00
parent ad44e775e2
commit 80d9f5d428
9 changed files with 322 additions and 1 deletions
@@ -61,6 +61,9 @@ import { useConfirmStore } from "@core/Store/Confirm.Store";
import ReviewsCarousel from "@core/app/components/ReviewsCarousel/ReviewsCarousel";
import OrganizationProfessionals from "@core/app/components/OrganizationProfessionals/OrganizationProfessionals";
import { RatingTargetType } from "@core/Models/Ratings.model";
import HeaderEditPopup from "./HeaderEditPopup";
import { checkOrgAdmin } from "./post.service";
import { IconButton as MuiIconButton } from "@mui/material";
enum VIEW {
POSTS = "posts",
@@ -96,6 +99,47 @@ export default function OrganizationPublicProfile() {
const [posts, setPosts] = useState<IPost[]>([]);
const [authorizedUsers, setAuthorizedUsers] = useState<string[]>([]);
const [currentPage, setCurrentPage] = useState<number>(1);
const [isOrgAdmin, setIsOrgAdmin] = useState<boolean>(false);
const [showEditPopup, setShowEditPopup] = useState<boolean>(false);
// Append edit button to header wrapper when admin
useEffect(() => {
if (!isOrgAdmin) return;
const wrapper = document.getElementById("header-wrapper");
if (!wrapper) return;
const btn = document.createElement("div");
btn.id = "edit-header-btn";
btn.innerHTML = `
<button style="
position: absolute;
top: 12px;
right: 12px;
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(0,0,0,0.45);
color: white;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
transition: background 0.2s;
" onmouseover="this.style.background='rgba(0,0,0,0.7)'" onmouseout="this.style.background='rgba(0,0,0,0.45)'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a.9959.9959 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
</button>
`;
btn.querySelector("button")!.addEventListener("click", () => setShowEditPopup(true));
wrapper.appendChild(btn);
return () => {
const existing = document.getElementById("edit-header-btn");
if (existing) existing.remove();
};
}, [isOrgAdmin]);
const SessionInfo = useSessionStore();
const eventHandler = useEventHandlerStore();
@@ -139,6 +183,22 @@ export default function OrganizationPublicProfile() {
});
}, [SessionInfo, authorizedUsers]);
// Check if the logged-in user is admin of this organization
useEffect(() => {
if (!SessionInfo.userId || !oid) {
setIsOrgAdmin(false);
return;
}
checkOrgAdmin(oid, SessionInfo.userId)
.then((isAdmin) => {
setIsOrgAdmin(isAdmin);
})
.catch(() => {
setIsOrgAdmin(false);
});
}, [SessionInfo.userId, oid]);
useEffect(() => {
if (!OrganizationData.orgData) return;
if (!oid) return; //if (!OrganizationData.orgData.id) return;
@@ -531,6 +591,16 @@ export default function OrganizationPublicProfile() {
return (
<>
<HeaderConfProvider />
{isOrgAdmin && oid && (
<HeaderEditPopup
open={showEditPopup}
onClose={() => setShowEditPopup(false)}
companyId={oid}
onSaved={() => {
setShowEditPopup(false);
}}
/>
)}
{orgData && (
<div className={style.organizationContent}>
{showEditor && (