Files
turnosxpress/txclient/src/app/components/CalendarRowView/CalendarRowView.tsx
T

219 lines
7.0 KiB
TypeScript

import { timeFormatter, timeRangeFormatter, TXCalendarEvent } from "@core/app/theme/scheduleView";
import { useEffect, useState, useRef } from "react";
import { DaySchedule } from "react-schedule-view";
import style from "../../theme/scheduleView.module.css";
import Avatar from "@components/Avatar/Avatar";
export interface CalendarRowViewProps {
daySchedules: DaySchedule<TXCalendarEvent>[];
handleEventClick: (event: TXCalendarEvent) => void;
onEventLongPress?: (event: TXCalendarEvent) => void;
}
type CalendarRowViewData = {
name: string;
events: TXCalendarEvent[];
};
// Custom component to manage long press state per item
function EventItem({
item,
onClick,
onLongPress,
}: {
item: TXCalendarEvent;
onClick: () => void;
onLongPress?: () => void;
}) {
const timerRef = useRef<NodeJS.Timeout | null>(null);
const isLongPress = useRef(false);
const isScrolling = useRef(false);
const startPos = useRef({ x: 0, y: 0 });
const [pressed, setPressed] = useState(false);
const getAvatar = (data: string | undefined) => {
let imageAvatar = "";
if (data) {
imageAvatar = data;
}
return imageAvatar;
};
const startPress = (e: React.TouchEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>) => {
isLongPress.current = false;
isScrolling.current = false;
if ('touches' in e) {
startPos.current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
}
setPressed(true);
timerRef.current = setTimeout(() => {
isLongPress.current = true;
setPressed(false);
if (window.navigator && window.navigator.vibrate) {
window.navigator.vibrate(50); // Haptic feedback
}
if (onLongPress) {
onLongPress();
}
}, 500);
};
const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => {
if (!isScrolling.current) {
const moveX = e.touches[0].clientX;
const moveY = e.touches[0].clientY;
if (
Math.abs(moveX - startPos.current.x) > 10 ||
Math.abs(moveY - startPos.current.y) > 10
) {
isScrolling.current = true;
cancelPress();
}
}
};
const cancelPress = () => {
setPressed(false);
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
};
const handleClick = () => {
cancelPress();
if (!isLongPress.current) {
onClick();
}
};
return (
<div
style={{
transform: pressed ? "scale(0.97)" : "scale(1)",
transition: "transform 100ms ease",
cursor: "pointer",
backgroundColor: item.color,
padding: "10px",
borderRadius: "5px",
color: "var(--white)",
fontWeight: "bold",
marginLeft: "10px",
marginRight: "10px",
marginBottom: "5px",
opacity: item.appointmentId ? 1 : 0.6,
userSelect: "none",
WebkitUserSelect: "none",
}}
onTouchStart={startPress}
onTouchEnd={cancelPress}
onTouchMove={handleTouchMove}
onTouchCancel={cancelPress}
onMouseDown={startPress}
onMouseUp={cancelPress}
onMouseLeave={cancelPress}
onClick={handleClick}
onContextMenu={(e) => e.preventDefault()} // Prevent context menu on long press
>
<div>
<div className={style.turnosXpressTitleContent}>
<Avatar
src={getAvatar(item.avatar)}
alt={item.title}
size="super-small"
border="none"
style={{ minWidth: "32px" }}
/>
<h1>{item.title}</h1>
</div>
<div className={style.turnosXpressTimeContent}>
{timeRangeFormatter(item.startTime, item.endTime)}
{item.payment && (
<span style={{ marginLeft: "5px", marginTop: "5px" }}>💰</span>
)}
</div>
</div>
</div>
);
}
export default function CalendarRowView(props: CalendarRowViewProps) {
const {
daySchedules = [
{
name: "",
events: [],
},
],
handleEventClick = () => {},
onEventLongPress,
} = props;
const [data, setData] = useState<CalendarRowViewData[]>([]);
const getTimeHeader = (event: TXCalendarEvent): string => {
return timeFormatter(event.startTime);
};
useEffect(() => {
const auxData: CalendarRowViewData[] = [];
for (const item of daySchedules[0].events) {
const time = getTimeHeader(item);
const check = auxData.find((x) => x.name === time);
if (check) {
check.events.push(item);
} else {
auxData.push({
name: time,
events: [item],
});
}
}
setData(auxData.sort((a, b) => a.name.localeCompare(b.name)));
}, [daySchedules]);
return (
<>
{data.map((event: CalendarRowViewData) => {
return (
<div
key={event.name}
style={{
display: "flex",
flexDirection: "row",
width: "100%",
marginLeft: "5px",
}}
>
<div
style={{
fontWeight: "bold",
color: "var(--gray-black)",
width: "80px",
}}
>
{event.name}
</div>
<div style={{ width: "100%" }}>
{event.events.map((item: TXCalendarEvent) => (
<EventItem
key={item.appointmentId || item.repeatId}
item={item}
onClick={() => handleEventClick(item)}
onLongPress={() => {
if (onEventLongPress && item.appointmentId) {
onEventLongPress(item);
}
}}
/>
))}
</div>
</div>
);
})}
</>
);
}