first commit
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import { PublicOrganizationView } from "@core/Models/Company.model";
|
||||
import { useOrgDataStore } from "@core/Store/OrganizationData.Store";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export type OrganizationDataProps = {
|
||||
orgData: PublicOrganizationView;
|
||||
};
|
||||
|
||||
export default function OrganizationDataConnector({ orgData }: OrganizationDataProps) {
|
||||
const OrganizationData = useOrgDataStore();
|
||||
useEffect(() => {
|
||||
OrganizationData.setData(orgData);
|
||||
}, [orgData]);
|
||||
return <></>;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import AnimatedContainer from "@components/AnimatedConainer/AnimatedContainer";
|
||||
import ApiServerService from "@services/Api.Server.Service";
|
||||
import { PublicOrganizationView } from "@core/Models/Company.model";
|
||||
import OrganizationHeader from "@core/app/components/Home/OrganizationHeader/OrganizationHeader";
|
||||
import OrganizationDataConnector from "./components/OrganizationData";
|
||||
|
||||
const GetOrganizationData = async (companyId: string): Promise<PublicOrganizationView> => {
|
||||
try {
|
||||
const organizationData = await ApiServerService.post<PublicOrganizationView>(
|
||||
"views/organization",
|
||||
{
|
||||
companyId: companyId,
|
||||
}
|
||||
);
|
||||
|
||||
return organizationData;
|
||||
} catch {
|
||||
return {} as PublicOrganizationView;
|
||||
}
|
||||
};
|
||||
|
||||
type Params = {
|
||||
oid: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
params: Promise<Params>; // `params` ahora es una Promesa
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export default async function OrganizationPublicProfile({ params, children }: Props) {
|
||||
const { oid } = await params;
|
||||
const orgData = await GetOrganizationData(oid);
|
||||
|
||||
return (
|
||||
<AnimatedContainer
|
||||
color={orgData.headerColor}
|
||||
minSize={150}
|
||||
maxSize={500}
|
||||
items={10}
|
||||
style={{
|
||||
background: `linear-gradient(180deg, ${orgData.headerColor} 0%, var(--white) 50%)`,
|
||||
}}
|
||||
>
|
||||
<div className="homeCentered">
|
||||
{orgData.id && (
|
||||
<>
|
||||
<OrganizationHeader organization={orgData} />
|
||||
<OrganizationDataConnector orgData={orgData} />
|
||||
<div>{children}</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!orgData.id && (
|
||||
<div
|
||||
style={{
|
||||
backgroundImage: `url(/no-org-slug.webp)`,
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "center center",
|
||||
backgroundSize: "50% auto",
|
||||
width: "100%",
|
||||
height: "750px",
|
||||
color: "var(--wine-red)",
|
||||
fontWeight: "600",
|
||||
fontSize: "26px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
marginTop: "10px",
|
||||
marginBottom: "30px",
|
||||
textAlign: "center",
|
||||
paddingLeft: "20px",
|
||||
paddingRight: "20px",
|
||||
}}
|
||||
>
|
||||
<p style={{ padding: "20px" }}>
|
||||
La organización que estas buscando no existe
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AnimatedContainer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,875 @@
|
||||
"use client";
|
||||
import style from "./profile.module.css";
|
||||
//import ProductGallery from "@components/Home/ProductsGallery/ProductGallery";
|
||||
import WhatsAppIcon from "@mui/icons-material/WhatsApp";
|
||||
import LocationOnIcon from "@mui/icons-material/LocationOn";
|
||||
import LocationMap from "@core/app/components/LocationMap/LocationMap";
|
||||
import Button from "@core/app/components/Button/Button";
|
||||
import { useNavigation } from "@core/app/hooks/goto";
|
||||
import { useOrgDataStore } from "@core/Store/OrganizationData.Store";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { PublicOrganizationView } from "@core/Models/Company.model";
|
||||
import { blurElementsOnLoad } from "@core/helpers/blur";
|
||||
import { ButtonGroup, IconButton } from "@mui/material";
|
||||
import GridViewOutlinedIcon from "@mui/icons-material/GridViewOutlined";
|
||||
import SplitscreenOutlinedIcon from "@mui/icons-material/SplitscreenOutlined";
|
||||
import ProductItem, { FindResultsViewMode } from "@core/app/components/ProductItem/ProductItem";
|
||||
import {
|
||||
FacebookShareButton,
|
||||
EmailShareButton,
|
||||
WhatsappShareButton,
|
||||
TelegramShareButton,
|
||||
TwitterShareButton,
|
||||
FacebookIcon,
|
||||
WhatsappIcon as WhatsAppShareIcon,
|
||||
EmailIcon,
|
||||
TelegramIcon,
|
||||
TwitterIcon,
|
||||
} from "react-share";
|
||||
import HeaderConfProvider from "@core/app/components/HeaderConfProvider/HeaderConfProvider";
|
||||
import useWindowSize from "@core/app/hooks/WindowSize";
|
||||
import AddIcon from "@mui/icons-material/AddOutlined";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import QuillEditor from "@core/app/components/QuillEditor/QuillEditor";
|
||||
import {
|
||||
deletePost,
|
||||
findPostsByOrganization,
|
||||
getPost,
|
||||
moveFixedPost,
|
||||
savePost,
|
||||
toggleFixedPost,
|
||||
} from "./post.service";
|
||||
import { useSessionStore } from "@core/Store/Sesion.Store";
|
||||
import { EVENT_TYPES, useEventHandlerStore } from "@core/Store/EventHandler.Store";
|
||||
import { useAlert } from "@core/Store/Alert.Store";
|
||||
import { IPost } from "@core/Models/Posts.model";
|
||||
import { preprocessQuillHtml, transformQuillContentForDisplay } from "@core/app/helpers/htmlSainitize";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
import { ThemeProvider } from "@emotion/react";
|
||||
import MaterialButton from "@mui/material/Button";
|
||||
import ClipIcon from "@mui/icons-material/AttachFileRounded";
|
||||
import DeleteIcon from "@mui/icons-material/DeleteOutlineRounded";
|
||||
import UpIcon from "@mui/icons-material/ArrowUpwardRounded";
|
||||
import DownIcon from "@mui/icons-material/ArrowDownwardRounded";
|
||||
import PinIcon from "@mui/icons-material/PushPin";
|
||||
import EditIcon from "@mui/icons-material/AutoFixHigh";
|
||||
import turnosXpressTheme from "@core/app/theme/turnosXpress";
|
||||
import { useParams } from "next/navigation";
|
||||
import dayjs from "dayjs";
|
||||
import { useConfirmStore } from "@core/Store/Confirm.Store";
|
||||
|
||||
enum VIEW {
|
||||
POSTS = "posts",
|
||||
SERVICES = "services",
|
||||
OPINIONS = "opinions",
|
||||
INFO = "info",
|
||||
}
|
||||
|
||||
type UrlData = {
|
||||
oid: string;
|
||||
};
|
||||
|
||||
export default function OrganizationPublicProfile() {
|
||||
const data = useParams<UrlData>();
|
||||
const oid = data.oid;
|
||||
const OrganizationData = useOrgDataStore();
|
||||
const [position, setPosition] = useState<[number, number]>([0, 0]);
|
||||
const [orgData, setOrgData] = useState<PublicOrganizationView>();
|
||||
const [viewMode, setViewMode] = useState<FindResultsViewMode>("grid");
|
||||
const [shareTitle, setShareTitle] = useState<string>("");
|
||||
const [view, setView] = useState<VIEW>(VIEW.POSTS);
|
||||
const [wndWidth, wndHeight] = useWindowSize();
|
||||
const { goTo } = useNavigation();
|
||||
const refDivContent = useRef<HTMLDivElement>(null);
|
||||
const refPostsContainer = useRef<HTMLDivElement>(null);
|
||||
const [showAddButton, setShowAddButton] = useState<boolean>(false);
|
||||
const [showEditor, setShowEditor] = useState<boolean>(false);
|
||||
const [firstLoad, setFirstLoad] = useState<boolean>(true);
|
||||
|
||||
const [editorPostId, setEditorPostId] = useState<string>("");
|
||||
const [editorContent, setEditorContent] = useState("");
|
||||
|
||||
const [posts, setPosts] = useState<IPost[]>([]);
|
||||
const [authorizedUsers, setAuthorizedUsers] = useState<string[]>([]);
|
||||
const [currentPage, setCurrentPage] = useState<number>(1);
|
||||
|
||||
const SessionInfo = useSessionStore();
|
||||
const eventHandler = useEventHandlerStore();
|
||||
const alert = useAlert();
|
||||
const confirm = useConfirmStore();
|
||||
const searchParams = useSearchParams();
|
||||
const tab = searchParams.get("tab") || "posts";
|
||||
|
||||
useEffect(() => {
|
||||
blurElementsOnLoad();
|
||||
|
||||
if (!tab) {
|
||||
setView(VIEW.POSTS);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (tab) {
|
||||
case "posts":
|
||||
setView(VIEW.POSTS);
|
||||
break;
|
||||
case "services":
|
||||
setView(VIEW.SERVICES);
|
||||
break;
|
||||
case "opinions":
|
||||
setView(VIEW.OPINIONS);
|
||||
break;
|
||||
case "info":
|
||||
setView(VIEW.INFO);
|
||||
break;
|
||||
default:
|
||||
setView(VIEW.POSTS);
|
||||
break;
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
requestAnimationFrame(() => {
|
||||
setTimeout(() => {
|
||||
processAddButton();
|
||||
}, 1000);
|
||||
});
|
||||
}, [SessionInfo, authorizedUsers]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!OrganizationData.orgData) return;
|
||||
if (!oid) return; //if (!OrganizationData.orgData.id) return;
|
||||
setOrgData(OrganizationData.orgData);
|
||||
setPosition([OrganizationData.orgData.latitude, OrganizationData.orgData.longitude]);
|
||||
setShareTitle(OrganizationData.orgData.name + " - " + OrganizationData.orgData.description);
|
||||
setCurrentPage(1);
|
||||
setPosts([]);
|
||||
cargarPosts(OrganizationData.orgData.id, 1);
|
||||
}, [OrganizationData.orgData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (orgData && oid) {
|
||||
//if (orgData && orgData.id) {
|
||||
cargarPosts(orgData.id, currentPage);
|
||||
}
|
||||
}, [currentPage, orgData]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
if (refPostsContainer.current) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = refPostsContainer.current;
|
||||
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 50;
|
||||
if (isAtBottom) {
|
||||
setCurrentPage((prevPage) => prevPage + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const currentRef = refPostsContainer.current;
|
||||
|
||||
if (currentRef) {
|
||||
currentRef.addEventListener("scroll", handleScroll);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (currentRef) {
|
||||
currentRef.removeEventListener("scroll", handleScroll);
|
||||
}
|
||||
};
|
||||
}, [refPostsContainer.current, view, currentPage]);
|
||||
|
||||
const processAddButton = (currentView: VIEW = view) => {
|
||||
if (currentView != VIEW.POSTS) {
|
||||
setShowAddButton(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SessionInfo.userId) {
|
||||
setShowAddButton(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!authorizedUsers.includes(SessionInfo.userId)) {
|
||||
setShowAddButton(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setShowAddButton(true);
|
||||
};
|
||||
|
||||
const checkPostButtons = (): boolean => {
|
||||
if (!SessionInfo.userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (authorizedUsers.includes(SessionInfo.userId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const checkShowInfo = (): boolean => {
|
||||
if (wndWidth < 800) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (view === VIEW.INFO) {
|
||||
setView(VIEW.POSTS);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const getContentHeight = () => {
|
||||
if (!refDivContent.current) return 0;
|
||||
|
||||
const containerSize = refDivContent.current.getClientRects().item(0);
|
||||
|
||||
if (!containerSize) return 0;
|
||||
|
||||
return wndHeight - containerSize.y - 90;
|
||||
};
|
||||
|
||||
const getHeaderItemStyle = (item: VIEW): React.CSSProperties => {
|
||||
const returnValue: React.CSSProperties = {};
|
||||
|
||||
returnValue.fontWeight = "bold";
|
||||
returnValue.color = "var(--wine-black)";
|
||||
returnValue.height = "100%";
|
||||
returnValue.display = "flex";
|
||||
returnValue.alignItems = "center";
|
||||
returnValue.padding = "20px";
|
||||
returnValue.cursor = "pointer";
|
||||
|
||||
if (item === view) {
|
||||
returnValue.borderBottom = "3px solid var(--wine-red)";
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
const getAddButtonStyle = (): React.CSSProperties => {
|
||||
const returnValue: React.CSSProperties = {};
|
||||
const buttonWidth = 64;
|
||||
const buttonMargin = 40;
|
||||
|
||||
returnValue.position = "absolute";
|
||||
returnValue.backgroundColor = "var(--wine-dark)";
|
||||
returnValue.width = `${buttonWidth}px`;
|
||||
returnValue.height = `${buttonWidth}px`;
|
||||
returnValue.borderRadius = "50%";
|
||||
returnValue.display = "flex";
|
||||
returnValue.justifyContent = "center";
|
||||
returnValue.alignItems = "center";
|
||||
returnValue.cursor = "pointer";
|
||||
returnValue.color = "var(--white)";
|
||||
returnValue.zIndex = 10;
|
||||
returnValue.right = `${buttonMargin}px`;
|
||||
returnValue.bottom = `${buttonMargin}px`;
|
||||
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
const getEditorStyle = (): React.CSSProperties => {
|
||||
const returnValue: React.CSSProperties = {};
|
||||
returnValue.position = "fixed";
|
||||
returnValue.backgroundColor = "var(--white)";
|
||||
returnValue.top = "60px";
|
||||
returnValue.left = "0px";
|
||||
returnValue.right = "0px";
|
||||
returnValue.bottom = "0px";
|
||||
returnValue.color = "var(--black)";
|
||||
returnValue.zIndex = 9999;
|
||||
returnValue.display = "flex";
|
||||
returnValue.flexDirection = "column";
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
const crearPost = (content: string) => {
|
||||
setShowEditor(false);
|
||||
|
||||
eventHandler.setEventType(EVENT_TYPES.LOADING);
|
||||
|
||||
savePost({
|
||||
content: content,
|
||||
companyId: orgData!.id,
|
||||
postId: editorPostId ? editorPostId : undefined,
|
||||
sessionUser: SessionInfo.userId,
|
||||
})
|
||||
.then(() => {
|
||||
alert.showSuccess("Post creado");
|
||||
// After creating a post, reset and reload the first page
|
||||
setCurrentPage(1);
|
||||
setPosts([]);
|
||||
if (orgData) {
|
||||
cargarPosts(orgData.id, 1);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.showError(error.format());
|
||||
})
|
||||
.finally(() => {
|
||||
setEditorContent("");
|
||||
eventHandler.setEventType(EVENT_TYPES.SLEEP);
|
||||
});
|
||||
};
|
||||
|
||||
const cargarPosts = (companyId: string, page: number) => {
|
||||
if (!companyId) {
|
||||
return;
|
||||
}
|
||||
|
||||
findPostsByOrganization({
|
||||
companyId: companyId,
|
||||
page: page,
|
||||
limit: 10,
|
||||
sessionUser: SessionInfo.userId,
|
||||
})
|
||||
.then((response) => {
|
||||
setAuthorizedUsers(response.authorizedUsers);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
setPosts((prevPosts) => {
|
||||
if (page === 1) {
|
||||
return response.data;
|
||||
}
|
||||
const newPosts = response.data.filter(
|
||||
(newPost) => !prevPosts.some((prevPost) => prevPost._id === newPost._id)
|
||||
);
|
||||
|
||||
return [...prevPosts, ...newPosts];
|
||||
});
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
processAddButton();
|
||||
});
|
||||
});
|
||||
|
||||
if (firstLoad) {
|
||||
if (view === VIEW.POSTS && response.data.length == 0) {
|
||||
requestAnimationFrame(() => {
|
||||
setView(VIEW.SERVICES);
|
||||
processAddButton();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setFirstLoad(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error.format());
|
||||
alert.showError(error.format());
|
||||
});
|
||||
};
|
||||
|
||||
const fijarPost = (postId: string) => {
|
||||
if (!orgData) {
|
||||
alert.showError("No se pudo determinar la organización.");
|
||||
return;
|
||||
}
|
||||
|
||||
eventHandler.setEventType(EVENT_TYPES.LOADING);
|
||||
|
||||
toggleFixedPost({
|
||||
companyId: orgData.id,
|
||||
postId: postId,
|
||||
sessionUser: SessionInfo.userId,
|
||||
})
|
||||
.then(() => {
|
||||
alert.showSuccess("Post actualizado");
|
||||
// After creating a post, reset and reload the first page
|
||||
setCurrentPage(1);
|
||||
setPosts([]);
|
||||
if (orgData) {
|
||||
cargarPosts(oid, 1); //cargarPosts(orgData.id, 1);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.showError(error.format());
|
||||
})
|
||||
.finally(() => {
|
||||
eventHandler.setEventType(EVENT_TYPES.SLEEP);
|
||||
});
|
||||
};
|
||||
|
||||
const moverPost = (postId: string, moveLength: number) => {
|
||||
if (!orgData) {
|
||||
alert.showError("No se pudo determinar la organización.");
|
||||
return;
|
||||
}
|
||||
|
||||
eventHandler.setEventType(EVENT_TYPES.LOADING);
|
||||
|
||||
moveFixedPost({
|
||||
companyId: orgData.id,
|
||||
postId: postId,
|
||||
moveLength: moveLength,
|
||||
sessionUser: SessionInfo.userId,
|
||||
})
|
||||
.then(() => {
|
||||
alert.showSuccess("Post actualizado");
|
||||
// After creating a post, reset and reload the first page
|
||||
setCurrentPage(1);
|
||||
setPosts([]);
|
||||
if (orgData) {
|
||||
cargarPosts(oid, 1); //cargarPosts(orgData.id, 1);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.showError(error.format());
|
||||
})
|
||||
.finally(() => {
|
||||
eventHandler.setEventType(EVENT_TYPES.SLEEP);
|
||||
});
|
||||
};
|
||||
|
||||
const eliminarPost = (postId: string) => {
|
||||
if (!orgData) {
|
||||
alert.showError("No se pudo determinar la organización.");
|
||||
return;
|
||||
}
|
||||
|
||||
confirm.show(
|
||||
"¿Estás seguro de eliminar el post?",
|
||||
() => {
|
||||
eventHandler.setEventType(EVENT_TYPES.LOADING);
|
||||
|
||||
deletePost({
|
||||
companyId: orgData.id,
|
||||
id: postId,
|
||||
sessionUser: SessionInfo.userId,
|
||||
})
|
||||
.then(() => {
|
||||
setPosts((prevPosts) => prevPosts.filter((post) => post._id !== postId));
|
||||
alert.showSuccess("Post eliminado");
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.showError(error.format());
|
||||
})
|
||||
.finally(() => {
|
||||
eventHandler.setEventType(EVENT_TYPES.SLEEP);
|
||||
});
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
};
|
||||
|
||||
const editarPost = (postId: string) => {
|
||||
getPost({
|
||||
id: postId,
|
||||
})
|
||||
.then((postData) => {
|
||||
setEditorPostId(postId);
|
||||
setEditorContent(postData.content);
|
||||
setShowEditor(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.showError(error.format());
|
||||
});
|
||||
};
|
||||
|
||||
const LocationInfo = (data: PublicOrganizationView | null) => {
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Información Útil</h2>
|
||||
<LocationMap position={position} orgData={data} />
|
||||
<ul>
|
||||
<li>
|
||||
<LocationOnIcon />
|
||||
{data.address}
|
||||
</li>
|
||||
<li>
|
||||
<WhatsAppIcon />
|
||||
{data.phone}
|
||||
</li>
|
||||
</ul>
|
||||
<Button
|
||||
color="primary"
|
||||
text="Enviar mensaje"
|
||||
width="100%"
|
||||
onClick={() => {
|
||||
goTo(`/landing/org/${data.id}/send-message`);
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<WhatsappShareButton title={shareTitle} separator=":: " url={window.location.href}>
|
||||
<WhatsAppShareIcon size={32} round />
|
||||
</WhatsappShareButton>
|
||||
<FacebookShareButton title={shareTitle} url={window.location.href}>
|
||||
<FacebookIcon size={32} round />
|
||||
</FacebookShareButton>
|
||||
<EmailShareButton title={shareTitle} separator=":: " url={window.location.href}>
|
||||
<EmailIcon size={32} round />
|
||||
</EmailShareButton>
|
||||
<TelegramShareButton title={shareTitle} url={window.location.href}>
|
||||
<TelegramIcon size={32} round />
|
||||
</TelegramShareButton>
|
||||
<TwitterShareButton title={shareTitle} url={window.location.href}>
|
||||
<TwitterIcon size={32} round />
|
||||
</TwitterShareButton>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderConfProvider />
|
||||
{orgData && (
|
||||
<div className={style.organizationContent}>
|
||||
{showEditor && (
|
||||
<div style={getEditorStyle()}>
|
||||
{/* Header */}
|
||||
<div
|
||||
style={{
|
||||
height: "50px",
|
||||
backgroundColor: "#f0f0f0",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingLeft: " 20px",
|
||||
borderBottom: "1px solid #ccc",
|
||||
}}
|
||||
>
|
||||
<h2>Qué estas pensando?</h2>
|
||||
|
||||
<IconButton
|
||||
color="inherit"
|
||||
style={{ width: "64px", height: "64px" }}
|
||||
onClick={() => {
|
||||
setShowEditor(false);
|
||||
}}
|
||||
>
|
||||
<CloseIcon sx={{ ml: 1, mr: 1 }} />
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
{/* Content (Editor) */}
|
||||
<div
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
<QuillEditor value={editorContent} onChange={setEditorContent} />
|
||||
</div>
|
||||
|
||||
{/* Bottom Bar */}
|
||||
<div
|
||||
style={{
|
||||
height: "60px",
|
||||
backgroundColor: "#f0f0f0",
|
||||
borderTop: "1px solid #ccc",
|
||||
padding: "0 20px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
text={editorPostId ? "Actualizar" : "Publicar"}
|
||||
width="100%"
|
||||
onClick={() => {
|
||||
setShowEditor(false);
|
||||
crearPost(editorContent);
|
||||
}}
|
||||
color="success"
|
||||
></Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={style.organizationMap}>{LocationInfo(orgData)}</div>
|
||||
<div
|
||||
ref={refDivContent}
|
||||
style={{
|
||||
border: "1px solid var(--gray-light)",
|
||||
borderRadius: "10px",
|
||||
marginTop: "30px",
|
||||
width: "100%",
|
||||
marginBottom: "30px",
|
||||
backgroundColor: "var(--white)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
borderBottom: "1px solid var(--gray-light)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: "50px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={getHeaderItemStyle(VIEW.POSTS)}
|
||||
onClick={() => {
|
||||
setView(VIEW.POSTS);
|
||||
processAddButton(VIEW.POSTS);
|
||||
}}
|
||||
>
|
||||
Posts
|
||||
</div>
|
||||
<div
|
||||
style={getHeaderItemStyle(VIEW.SERVICES)}
|
||||
onClick={() => {
|
||||
setView(VIEW.SERVICES);
|
||||
processAddButton(VIEW.SERVICES);
|
||||
}}
|
||||
>
|
||||
Servicios
|
||||
</div>
|
||||
{checkShowInfo() && (
|
||||
<div
|
||||
style={getHeaderItemStyle(VIEW.INFO)}
|
||||
onClick={() => {
|
||||
setView(VIEW.INFO);
|
||||
processAddButton(VIEW.INFO);
|
||||
}}
|
||||
>
|
||||
Contacto
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{view === VIEW.SERVICES && (
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<ButtonGroup
|
||||
variant="outlined"
|
||||
aria-label="Basic button group"
|
||||
sx={{ marginTop: "10px", marginRight: "30px" }}
|
||||
>
|
||||
<IconButton onClick={() => setViewMode("card")}>
|
||||
<GridViewOutlinedIcon
|
||||
sx={{
|
||||
color:
|
||||
viewMode === "card" ? "var(--wine-red)" : "ineherit",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
<IconButton onClick={() => setViewMode("grid")}>
|
||||
<SplitscreenOutlinedIcon
|
||||
sx={{
|
||||
color:
|
||||
viewMode === "grid" ? "var(--wine-red)" : "ineherit",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{view === VIEW.POSTS && (
|
||||
<div
|
||||
ref={refPostsContainer}
|
||||
style={{
|
||||
overflowY: "scroll",
|
||||
height: `${getContentHeight()}px`,
|
||||
}}
|
||||
>
|
||||
{showAddButton && (
|
||||
<div
|
||||
style={getAddButtonStyle()}
|
||||
onClick={() => {
|
||||
setEditorPostId("");
|
||||
setEditorContent("");
|
||||
setShowEditor(true);
|
||||
}}
|
||||
>
|
||||
<AddIcon sx={{ fontSize: "32px" }} />
|
||||
</div>
|
||||
)}
|
||||
{posts.length === 0 && (
|
||||
<div
|
||||
style={{
|
||||
textAlign: "center",
|
||||
backgroundImage: "url(/no-conversation.webp)",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "center center",
|
||||
backgroundSize: "50% auto",
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
Nada por aquí
|
||||
</div>
|
||||
)}
|
||||
{posts.map((post: IPost) => {
|
||||
return (
|
||||
<div
|
||||
key={post._id}
|
||||
style={{
|
||||
borderBottom: "1px solid var(--gray-light)",
|
||||
paddingTop: "5px",
|
||||
paddingBottom: "10px",
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<div style={{ color: "var(--gray-darkest)" }}>
|
||||
{dayjs(post.createdAt).format("DD MMMM YYYY HH:mm")}
|
||||
</div>
|
||||
{post.fixed && (
|
||||
<div
|
||||
style={{
|
||||
color: "var(--gray-light)",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<PinIcon fontSize="small" />
|
||||
<span>Anclado</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: preprocessQuillHtml(
|
||||
transformQuillContentForDisplay(post.content)
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{checkPostButtons() && (
|
||||
<div
|
||||
style={{
|
||||
marginTop: "10px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
flexWrap: "wrap",
|
||||
color: "var(--gray-darkest)",
|
||||
}}
|
||||
>
|
||||
<ThemeProvider theme={turnosXpressTheme}>
|
||||
<MaterialButton
|
||||
color="inherit"
|
||||
startIcon={<ClipIcon />}
|
||||
onClick={() => {
|
||||
fijarPost(`${post._id}`);
|
||||
}}
|
||||
>
|
||||
{post.fixed ? "Liberar" : "Fijar"}
|
||||
</MaterialButton>
|
||||
|
||||
{post.fixed && (
|
||||
<>
|
||||
<MaterialButton
|
||||
color="inherit"
|
||||
startIcon={<UpIcon />}
|
||||
onClick={() => {
|
||||
moverPost(`${post._id}`, -1);
|
||||
}}
|
||||
>
|
||||
Subir
|
||||
</MaterialButton>
|
||||
|
||||
<MaterialButton
|
||||
color="inherit"
|
||||
startIcon={<DownIcon />}
|
||||
onClick={() => {
|
||||
moverPost(`${post._id}`, 1);
|
||||
}}
|
||||
>
|
||||
Bajar
|
||||
</MaterialButton>
|
||||
</>
|
||||
)}
|
||||
|
||||
<MaterialButton
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
editarPost(`${post._id}`);
|
||||
}}
|
||||
>
|
||||
<EditIcon />
|
||||
</MaterialButton>
|
||||
|
||||
<MaterialButton
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
eliminarPost(`${post._id}`);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</MaterialButton>
|
||||
</ThemeProvider>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{view === VIEW.SERVICES && (
|
||||
<div
|
||||
className={style.organizationServices}
|
||||
style={{
|
||||
overflowY: "scroll",
|
||||
height: `${getContentHeight()}px`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: "0px 0px 40px 0px",
|
||||
display: "flex",
|
||||
flexDirection: viewMode === "card" ? "row" : "column",
|
||||
justifyContent: "start",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
{orgData.services.data.map((service) => (
|
||||
<ProductItem
|
||||
product={service}
|
||||
key={service.id}
|
||||
width={260}
|
||||
viewMode={viewMode}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{view === VIEW.INFO && checkShowInfo() && (
|
||||
<div
|
||||
className={style.organizationMoreInfo}
|
||||
style={{
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
{LocationInfo(orgData)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import ApiRequest from "@services/Api.Service";
|
||||
import * as Yup from "yup";
|
||||
import { ApiError } from "@core/Models/Server.Error.model";
|
||||
import {
|
||||
DeletePostParams,
|
||||
GetPostParams,
|
||||
IPost,
|
||||
PaginatePostsParams,
|
||||
PaginatePostsResults,
|
||||
SavePostParams,
|
||||
} from "@core/Models/Posts.model";
|
||||
import { FixCompanyPostParams, MoveCompanyFixedPostParams } from "@core/Models/Company.model";
|
||||
|
||||
const schemaPostSave = Yup.object().shape({
|
||||
postId: Yup.string().optional(),
|
||||
companyId: Yup.string().required("No es posible deteriminar la organización."),
|
||||
content: Yup.string().required("El post esta vacío."),
|
||||
fixed: Yup.boolean().optional(),
|
||||
fixedOrder: Yup.number().optional(),
|
||||
sessionUser: Yup.string().required("Para crear un descuento debe iniciar sesión."),
|
||||
});
|
||||
|
||||
export const savePost = async (data: SavePostParams): Promise<IPost> => {
|
||||
let yupErrors: Yup.ValidationError = new Yup.ValidationError("");
|
||||
|
||||
const validateData = async () => {
|
||||
try {
|
||||
await schemaPostSave.validate(data, { abortEarly: true });
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof Yup.ValidationError) {
|
||||
yupErrors = error as Yup.ValidationError;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
if ((await validateData()) == false) {
|
||||
throw new ApiError(400, yupErrors.message);
|
||||
}
|
||||
|
||||
return ApiRequest.post<IPost>("posts/save", data);
|
||||
};
|
||||
|
||||
export const findPostsByOrganization = async (
|
||||
data: PaginatePostsParams
|
||||
): Promise<PaginatePostsResults> => {
|
||||
return ApiRequest.post<PaginatePostsResults>("posts/paginate", data);
|
||||
};
|
||||
|
||||
export const getPost = async (
|
||||
data: GetPostParams
|
||||
): Promise<IPost> => {
|
||||
return ApiRequest.post<IPost>("posts/get-post", data);
|
||||
};
|
||||
|
||||
export const deletePost = async (data: DeletePostParams): Promise<void> => {
|
||||
return ApiRequest.post<void>("posts/delete", data);
|
||||
};
|
||||
|
||||
export const toggleFixedPost = async (data: FixCompanyPostParams): Promise<void> => {
|
||||
return ApiRequest.post<void>("companies/toggle-fixed-post", data);
|
||||
};
|
||||
|
||||
export const moveFixedPost = async (data: MoveCompanyFixedPostParams): Promise<void> => {
|
||||
return ApiRequest.post<void>("companies/move-fixed-post", data);
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
@media (max-width: 800px) {
|
||||
.organizationContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
margin-left: 30px;
|
||||
margin-right: 30px;
|
||||
gap: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.organizationMap {
|
||||
border: solid 1px var(--gray-light);
|
||||
border-radius: 10px;
|
||||
background-color: white;
|
||||
padding: 30px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.organizationMap ul {
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.organizationMap li {
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
color: (--black);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.organizationMoreInfo {
|
||||
background-color: white;
|
||||
padding: 30px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 30px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.organizationMoreInfo ul {
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.organizationMoreInfo li {
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
color: (--black);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.organizationServices {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 801px) {
|
||||
.organizationContent {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
/*margin-top: 30px;*/
|
||||
margin-left: 30px;
|
||||
margin-right: 30px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.organizationMap {
|
||||
border: solid 1px var(--gray-light);
|
||||
border-radius: 10px;
|
||||
background-color: white;
|
||||
padding: 30px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.organizationMap ul {
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.organizationMap li {
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
color: (--black);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.organizationServices {
|
||||
margin-bottom: 30px;
|
||||
padding-right: 10px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
"use client";
|
||||
|
||||
import style from "./style.module.css";
|
||||
import Textbox from "@core/app/components/Textbox/Textbox";
|
||||
import Button from "@core/app/components/Button/Button";
|
||||
import { useOrgDataStore } from "@core/Store/OrganizationData.Store";
|
||||
import { PublicOrganizationView } from "@core/Models/Company.model";
|
||||
import { useEffect, useState } from "react";
|
||||
import { sendMessage } from "@core/app/messenger/Messages.service";
|
||||
import { MESSAGE_CONVERSATION_TYPES, SendMessageParams } from "@core/Models/Messages.model";
|
||||
import { useSessionStore } from "@core/Store/Sesion.Store";
|
||||
import { useAlert } from "@core/Store/Alert.Store";
|
||||
|
||||
export default function OrganizationPublicProfile() {
|
||||
const SessionInfo = useSessionStore();
|
||||
const alert = useAlert();
|
||||
const OrganizationData = useOrgDataStore();
|
||||
const [orgData, setOrgData] = useState<PublicOrganizationView>();
|
||||
const [comments, setComments] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
if (!OrganizationData.orgData) return;
|
||||
setOrgData(OrganizationData.orgData);
|
||||
}, [OrganizationData.orgData]);
|
||||
|
||||
const send = () => {
|
||||
if (!orgData) return;
|
||||
|
||||
const conversationType = MESSAGE_CONVERSATION_TYPES.USER_TO_ORG;
|
||||
|
||||
const sendMessageData: SendMessageParams = {
|
||||
conversationId: undefined,
|
||||
type: conversationType,
|
||||
userIdFrom: SessionInfo.userId,
|
||||
userIdTo: undefined,
|
||||
companyId: orgData.id,
|
||||
message: comments,
|
||||
sessionUser: SessionInfo.userId,
|
||||
};
|
||||
|
||||
sendMessage(sendMessageData)
|
||||
.then(() => {
|
||||
alert.showSuccess("Mensaje enviado");
|
||||
setComments("");
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.showError(error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{orgData && (
|
||||
<div className="homeCentered">
|
||||
<div className={style.organizationContent}>
|
||||
<h1 className="homeTitleHeader">Contactate con nosotros</h1>
|
||||
<p className="homeResumeHeader">
|
||||
¿Tienes preguntas, sugerencias o necesitas más información? Estás en el
|
||||
lugar indicado. Aquí puedes ponerte en contacto directamente con nuestro
|
||||
equipo.{" "}
|
||||
<b>
|
||||
Para enviar tu mensaje, es necesario estar registrado en el sitio.{" "}
|
||||
</b>
|
||||
Completa el formulario y te responderemos lo antes posible. ¡Será un
|
||||
placer ayudarte!
|
||||
</p>
|
||||
<Textbox
|
||||
type="comments"
|
||||
placeholder="Escribe tu mensaje"
|
||||
value={comments}
|
||||
width="100%"
|
||||
style={{ height: "200px" }}
|
||||
onChange={(e) => setComments(e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
color="primary"
|
||||
text="Enviar"
|
||||
width="100%"
|
||||
style={{ marginTop: "20px", marginBottom: "20px" }}
|
||||
onClick={send}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
@media (max-width: 800px) {
|
||||
.organizationContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
margin-left: 30px;
|
||||
margin-right: 30px;
|
||||
gap: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 801px) {
|
||||
.organizationContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
/*margin-top: 30px;*/
|
||||
margin-left: 30px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user