Skip to content

Commit

Permalink
refactor(ClassInfo): DRY
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrianAndersen committed Apr 10, 2024
1 parent 588f09b commit 35fb990
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 97 deletions.
131 changes: 34 additions & 97 deletions src/components/modals/ClassInfo/ClassInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ import CalendarMonthIcon from "@mui/icons-material/CalendarMonth";
import LocationOnRoundedIcon from "@mui/icons-material/LocationOnRounded";
import PersonRoundedIcon from "@mui/icons-material/PersonRounded";
import LoadingButton from "@mui/lab/LoadingButton";
import { Alert, AlertTitle, Box, Typography } from "@mui/material";
import { Alert, AlertTitle, Box, Stack, Typography } from "@mui/material";
import Button from "@mui/material/Button";
import Image from "next/image";
import React, { useState } from "react";

import ClassInfoEntry from "@/components/modals/ClassInfo/ClassInfoEntry";
import ClassInfoUsersGroup from "@/components/modals/ClassInfo/ClassInfoUsersGroup";
import ClassPopularityMeter from "@/components/schedule/class/ClassPopularityMeter";
import ConfirmCancellation from "@/components/schedule/class/ConfirmCancellation";
Expand Down Expand Up @@ -90,8 +91,6 @@ export default function ClassInfo({
setBookingLoading(false);
}

const cancelledOpacity = 0.5;

return (
<Box
sx={{
Expand All @@ -112,14 +111,7 @@ export default function ClassInfo({
},
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: 1,
paddingBottom: 1,
}}
>
<Stack direction={"row"} gap={1} alignItems={"center"} paddingBottom={1}>
<Box
sx={{
borderRadius: "50%",
Expand All @@ -134,7 +126,7 @@ export default function ClassInfo({
<Typography variant="h6" component="h2">
{_class.activity.name}
</Typography>
</Box>
</Stack>
{_class.isCancelled && (
<Alert severity={"error"} icon={<CancelRounded />}>
{_class.cancelText ? (
Expand All @@ -147,96 +139,41 @@ export default function ClassInfo({
)}
</Alert>
)}
<Box
sx={{
display: "flex",
paddingTop: 1,
gap: 1,
alignItems: "center",
opacity: _class.isCancelled ? cancelledOpacity : 1,
}}
>
<CalendarMonthIcon />
<Typography variant="body2" color="text.secondary">
{_class.startTime.toFormat("EEEE d. LLLL")}
</Typography>
</Box>
<Box
sx={{
display: "flex",
paddingTop: 1,
gap: 1,
alignItems: "center",
opacity: _class.isCancelled ? cancelledOpacity : 1,
}}
>
<AccessTimeRoundedIcon />
<Typography variant="body2" color="text.secondary">
{_class.startTime.toFormat("HH:mm")}{_class.endTime.toFormat("HH:mm")}
</Typography>
</Box>
<Box
sx={{
display: "flex",
paddingTop: 1,
gap: 1,
alignItems: "center",
opacity: _class.isCancelled ? cancelledOpacity : 1,
}}
>
<LocationOnRoundedIcon />
<Typography variant="body2" color="text.secondary">
{_class.location.studio}
{_class.location.room && _class.location.room.length > 0 ? `, ${_class.location.room}` : ""}
</Typography>
</Box>
<ClassInfoEntry
icon={<CalendarMonthIcon />}
label={_class.startTime.toFormat("EEEE d. LLLL")}
cancelled={_class.isCancelled}
/>
<ClassInfoEntry
icon={<AccessTimeRoundedIcon />}
label={`${_class.startTime.toFormat("HH:mm")}${_class.endTime.toFormat("HH:mm")}`}
cancelled={_class.isCancelled}
/>
<ClassInfoEntry
icon={<LocationOnRoundedIcon />}
label={`${_class.location.studio} ${_class.location.room && _class.location.room.length > 0 ? `, ${_class.location.room}` : ""}`}
cancelled={_class.isCancelled}
/>
{_class.instructors.length > 0 && (
<Box
sx={{
display: "flex",
paddingTop: 1,
gap: 1,
alignItems: "center",
opacity: _class.isCancelled ? cancelledOpacity : 1,
}}
>
<PersonRoundedIcon />
<Typography variant="body2" color="text.secondary">
{_class.instructors.map((i) => i.name).join(", ")}
</Typography>
</Box>
<ClassInfoEntry
icon={<PersonRoundedIcon />}
label={_class.instructors.map((i) => i.name).join(", ")}
cancelled={_class.isCancelled}
/>
)}
{_class.totalSlots !== null && ((!_class.isBookable && !isInThePast) || _class.isCancelled) && (
<Box
sx={{
display: "flex",
paddingTop: 1,
gap: 1,
alignItems: "center",
opacity: _class.isCancelled ? cancelledOpacity : 1,
}}
>
<Diversity3Rounded />
<Typography variant="body2" color="text.secondary">
{_class.totalSlots} plasser
</Typography>
</Box>
<ClassInfoEntry
icon={<Diversity3Rounded />}
label={`${_class.totalSlots} plasser`}
cancelled={_class.isCancelled}
/>
)}
{!_class.isCancelled && _class.totalSlots !== null && _class.availableSlots !== null && (
<Box
sx={{
display: "flex",
paddingTop: 1,
gap: 1,
alignItems: "center",
opacity: _class.isCancelled ? cancelledOpacity : 1,
}}
>
<ClassPopularityMeter _class={_class} historicPopularity={classPopularity} />
<Typography variant="body2" color="text.secondary">
{stringifyClassPopularity(_class, classPopularity)}
</Typography>
</Box>
<ClassInfoEntry
icon={<ClassPopularityMeter _class={_class} historicPopularity={classPopularity} />}
label={stringifyClassPopularity(_class, classPopularity) ?? ""}
cancelled={_class.isCancelled}
/>
)}
{!isInThePast && (
<>
Expand Down
22 changes: 22 additions & 0 deletions src/components/modals/ClassInfo/ClassInfoEntry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Box, Typography } from "@mui/material";
import React, { ReactNode } from "react";

function ClassInfoEntry({ icon, label, cancelled }: { icon: ReactNode; label: string; cancelled: boolean }) {
return (
<Box
sx={{
display: "flex",
paddingTop: 1,
gap: 1,
alignItems: "center",
opacity: cancelled ? 0.5 : 1,
}}
>
{icon}
<Typography variant="body2" color="text.secondary">
{label}
</Typography>
</Box>
);
}
export default ClassInfoEntry;
87 changes: 87 additions & 0 deletions src/components/utils/EditableText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { Box, Collapse, Typography, IconButton, useTheme } from "@mui/material";
import React, { useState, useEffect, useRef } from "react";

interface ExpandableTextProps {
text: string;
initiallyCollapsed?: boolean;
}

const ExpandableText: React.FC<ExpandableTextProps> = ({ text, initiallyCollapsed = true }) => {
const [expanded, setExpanded] = useState(false);
const [showExpansionToggle, setShowExpansionToggle] = useState(initiallyCollapsed);
const textContainerRef = useRef<HTMLDivElement>(null);
const defaultCollapsedSize = 85;
const theme = useTheme();

useEffect(() => {
if (textContainerRef.current && initiallyCollapsed) {
const actualHeight = textContainerRef.current.scrollHeight;
const isOverflowing = actualHeight > defaultCollapsedSize + 25;
setShowExpansionToggle(isOverflowing);
} else {
setShowExpansionToggle(false);
}
}, [text, initiallyCollapsed]);

return (
<Box sx={{ position: "relative", width: "100%", mt: 2, mb: showExpansionToggle ? 4 : 0 }}>
<Collapse
in={expanded || !showExpansionToggle}
collapsedSize={showExpansionToggle ? `${defaultCollapsedSize}px` : "auto"}
sx={{ position: "relative" }}
>
<Typography
ref={textContainerRef}
sx={{
overflow: "hidden",
":after":
showExpansionToggle && !expanded && initiallyCollapsed
? {
content: '""',
position: "absolute",
width: "100%",
height: 20, // Height of the fade effect
bottom: 0,
left: 0,
backgroundImage: `linear-gradient(to bottom, ${theme.palette.mode === "dark" ? "rgba(0,0,0,0)" : "rgba(255,255,255,0)"}, ${theme.palette.mode === "dark" ? "rgba(50,50,50,1)" : "rgba(255,255,255,1)"})`,
}
: {},
}}
>
{text}
</Typography>
</Collapse>
{showExpansionToggle && (
<IconButton
sx={{
position: "absolute",
bottom: "-25px",
left: "50%",
transform: "translateX(-50%)",
cursor: "pointer",
}}
onClick={(event) => {
event.stopPropagation(); // Prevent event bubbling to Collapse onClick
initiallyCollapsed && setExpanded(!expanded);
setTimeout(() => {
textContainerRef.current?.scrollIntoView({
behavior: "smooth",
inline: "end",
});
}, 185);
}}
>
<ExpandMoreIcon
sx={{
transform: expanded ? "rotate(180deg)" : "rotate(0deg)",
transition: "transform 0.2s",
}}
/>
</IconButton>
)}
</Box>
);
};

export default ExpandableText;

0 comments on commit 35fb990

Please sign in to comment.