Created
September 30, 2023 13:01
-
-
Save tripolskypetr/62d737854e2b0d06d178fc030a62a822 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Async, | |
Center, | |
Chip, | |
LoaderView, | |
useAsyncAction, | |
} from "react-declarative"; | |
import { forwardRef, useCallback, useMemo, useState } from "react"; | |
import Box from "@mui/material/Box"; | |
import { Button } from "@mui/material"; | |
import { DEAL_STATUS_LIST } from "../../../../../../lib/services/db/DealDbService"; | |
import { | |
DealStatus, | |
} from "../../../../../../lib/services/db/DealDbService"; | |
import IDealCard from "../../../model/IDealCard"; | |
import IconButton from "@mui/material/IconButton"; | |
import KeyboardArrowLeft from "@mui/icons-material/KeyboardArrowLeft"; | |
import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight"; | |
import Menu from "@mui/material/Menu"; | |
import MenuItem from "@mui/material/MenuItem"; | |
import Paper from "@mui/material/Paper"; | |
import Typography from "@mui/material/Typography"; | |
import clsx from "clsx"; | |
import dayjs from "dayjs"; | |
import ioc from "../../../../../../lib/ioc"; | |
import { makeStyles } from "../../../../../../styles"; | |
export const CARD_MIN_HEIGHT = 150; | |
const useStyles = makeStyles()((theme) => ({ | |
root: { | |
display: "flex", | |
alignItems: "stretch", | |
justifyContent: "stretch", | |
flexDirection: "column", | |
minHeight: CARD_MIN_HEIGHT, | |
}, | |
container: { | |
position: "relative", | |
display: "flex", | |
alignItems: "stretch", | |
justifyContent: "stretch", | |
flex: 1, | |
}, | |
content: { | |
display: "flex", | |
alignItems: "stretch", | |
justifyContent: "stretch", | |
flexDirection: "column", | |
paddingTop: 6, | |
paddingBottom: 6, | |
gap: 6, | |
flex: 1, | |
}, | |
header: { | |
display: "flex", | |
alignItems: "stretch", | |
justifyContent: "stretch", | |
flexDirection: "column", | |
paddingLeft: theme.spacing(0.5), | |
paddingRight: theme.spacing(0.5), | |
}, | |
row: { | |
display: "flex", | |
alignItems: "center", | |
justifyContent: "space-between", | |
}, | |
noBorder: { | |
border: "none !important", | |
}, | |
bold: { | |
fontWeight: "bold !important", | |
}, | |
adjust: { | |
maxHeight: 10, | |
minHeight: 10, | |
flex: 1, | |
}, | |
button: { | |
backgroundColor: "#3F51B5", | |
color: "#ffffff", | |
fontSize: 12, | |
marginLeft: "auto", | |
marginRight: "auto", | |
"&:hover": { | |
color: "#3F51B5", | |
}, | |
}, | |
fieldContainer: { | |
display: "flex", | |
justifyContent: "space-between", | |
}, | |
fieldDesc: { | |
fontSize: 10, | |
opacity: 0.6, | |
marginLeft: 4, | |
}, | |
dateFieldDesc: { | |
fontSize: 8, | |
opacity: 0.6, | |
marginLeft: 4, | |
}, | |
field: { | |
fontSize: 12, | |
marginRight: 4, | |
fontWeight: 500, | |
}, | |
dateField: { | |
fontSize: 8, | |
}, | |
})); | |
interface IDealCardProps extends IDealCard { | |
id: string; | |
className?: string; | |
style?: React.CSSProperties; | |
onChangeStatus: (dealStatus: DealStatus) => void; | |
onDrag: (id: string) => void; | |
} | |
export const DealCard = forwardRef( | |
( | |
{ | |
className, | |
style, | |
contactDisplayName, | |
contactType, | |
contactNumber, | |
responsible, | |
requestType, | |
dealIndex, | |
dealType, | |
dealStatus, | |
price, | |
currency, | |
createdAt, | |
updatedAt, | |
apartmentId, | |
bidId, | |
id, | |
onChangeStatus, | |
onDrag, | |
}: IDealCardProps, | |
ref: React.Ref<HTMLDivElement> | |
) => { | |
const { classes } = useStyles(); | |
const [beforeAnchorEl, setBeforeAnchorEl] = | |
useState<HTMLButtonElement | null>(null); | |
const [afterAnchorEl, setAfterAnchorEl] = | |
useState<HTMLButtonElement | null>(null); | |
const { execute: handleChangeStatus, loading } = useAsyncAction<void>( | |
async (payload) => { | |
await onChangeStatus(payload); | |
}, | |
{ | |
onLoadStart: () => ioc.layoutService.setAppbarLoader(true), | |
onLoadEnd: () => ioc.layoutService.setAppbarLoader(false), | |
fallback: ioc.errorService.handleGlobalError, | |
} | |
); | |
const beforeCurrentStatus = useMemo(() => { | |
const currentStatusIdx = DEAL_STATUS_LIST.findIndex( | |
(value) => value === dealStatus | |
); | |
return DEAL_STATUS_LIST.filter( | |
(_, idx) => idx < currentStatusIdx | |
).reverse(); | |
}, [dealStatus]); | |
const afterCurrentStatus = useMemo(() => { | |
const currentStatusIdx = DEAL_STATUS_LIST.findIndex( | |
(value) => value === dealStatus | |
); | |
return DEAL_STATUS_LIST.filter((_, idx) => idx > currentStatusIdx); | |
}, [dealStatus]); | |
const createdDate = useMemo(() => { | |
return dayjs(createdAt).format("DD.MM.YYYY / HH:MM"); | |
}, [createdAt]); | |
const updatedData = useMemo(() => { | |
return dayjs(updatedAt).format("DD.MM.YYYY / HH:MM"); | |
}, [updatedAt]); | |
const readonly = useMemo(() => { | |
if (dealStatus === "Сделка сорвана") { | |
return true; | |
} | |
if (dealStatus === "Закрытая сделка") { | |
return true; | |
} | |
return false; | |
}, [dealStatus]); | |
const handleShowHistory = useCallback(() => { | |
if (apartmentId) { | |
ioc.routerService.push(`/apartment_view/${apartmentId}/history`); | |
return; | |
} | |
if (bidId) { | |
ioc.routerService.push(`/bid_view/${bidId}/history`); | |
return; | |
} | |
}, []); | |
return ( | |
<div | |
data-dealid={id} | |
className={clsx(classes.root, className)} | |
onDrag={() => onDrag(id)} | |
style={style} | |
ref={ref} | |
draggable={!readonly} | |
> | |
<Paper className={classes.container}> | |
<Box className={classes.content}> | |
<Box className={classes.row}> | |
<IconButton | |
onClick={({ currentTarget }) => { | |
setBeforeAnchorEl(currentTarget); | |
setAfterAnchorEl(null); | |
}} | |
disabled={!beforeCurrentStatus.length || loading || readonly} | |
size="small" | |
> | |
<KeyboardArrowLeft /> | |
</IconButton> | |
<Menu | |
anchorEl={beforeAnchorEl} | |
open={!!beforeAnchorEl} | |
onClose={() => setBeforeAnchorEl(null)} | |
> | |
{beforeCurrentStatus.map((status, idx) => ( | |
<MenuItem | |
key={`${status}-${idx}`} | |
disabled={idx !== 0} | |
onClick={() => { | |
handleChangeStatus(status); | |
setBeforeAnchorEl(null); | |
}} | |
> | |
{status} | |
</MenuItem> | |
))} | |
</Menu> | |
<Chip | |
label={responsible} | |
variant="filled" | |
color="#2196F3" | |
sx={{ | |
fontWeight: 500, | |
fontSize: 10, | |
}} | |
/> | |
<IconButton | |
onClick={({ currentTarget }) => { | |
setBeforeAnchorEl(null); | |
setAfterAnchorEl(currentTarget); | |
}} | |
disabled={!afterCurrentStatus.length || loading || readonly} | |
size="small" | |
> | |
<KeyboardArrowRight /> | |
</IconButton> | |
<Menu | |
anchorEl={afterAnchorEl} | |
open={!!afterAnchorEl} | |
onClose={() => setAfterAnchorEl(null)} | |
> | |
{afterCurrentStatus.map((status, idx) => ( | |
<MenuItem | |
key={`${status}-${idx}`} | |
disabled={idx !== 0} | |
onClick={() => { | |
handleChangeStatus(status); | |
setAfterAnchorEl(null); | |
}} | |
> | |
{status} | |
</MenuItem> | |
))} | |
</Menu> | |
</Box> | |
<Center sx={{ minHeight: 50 }}> | |
<Typography | |
fontSize={12} | |
textAlign="center" | |
maxWidth="80%" | |
paddingBottom="6px" | |
> | |
{contactDisplayName} | |
</Typography> | |
</Center> | |
{contactNumber && ( | |
<Box className={classes.content}> | |
<Box | |
className={classes.fieldContainer} | |
sx={{ borderBottom: 1, borderColor: "#E0E0E0" }} | |
> | |
<Typography className={classes.fieldDesc}> | |
Тип контакта | |
</Typography> | |
<Typography className={classes.field}> | |
{contactType} | |
</Typography> | |
</Box> | |
<Box | |
className={classes.fieldContainer} | |
sx={{ borderBottom: 1, borderColor: "#E0E0E0" }} | |
> | |
<Typography className={classes.fieldDesc}> | |
Номер телефона | |
</Typography> | |
<Typography className={classes.field}> | |
{contactNumber} | |
</Typography> | |
</Box> | |
</Box> | |
)} | |
<Center> | |
<Box | |
sx={{ | |
display: "flex", | |
flexWrap: "wrap", | |
justifyContent: "center", | |
maxWidth: "90%", | |
gap: 1, | |
}} | |
> | |
<Chip | |
label={`№ ${dealIndex}`} | |
variant="filled" | |
color="#E0E0E0" | |
/> | |
{dealType == "Аренда" ? ( | |
<Chip | |
label="Аренда" | |
variant="filled" | |
color="#AF4C5E" | |
sx={{ | |
color: "white", | |
}} | |
/> | |
) : ( | |
<Chip | |
label="Продажа" | |
variant="filled" | |
color="#4CAFAF" | |
sx={{ | |
color: "white", | |
}} | |
/> | |
)} | |
{requestType === "Заявка" ? ( | |
<Chip | |
label="Заявка" | |
variant="filled" | |
color="#AFAB4C" | |
sx={{ | |
color: "white", | |
}} | |
/> | |
) : ( | |
<Chip | |
label="Объект" | |
variant="filled" | |
color="#4CAF50" | |
sx={{ | |
color: "white", | |
}} | |
/> | |
)} | |
</Box> | |
</Center> | |
{price && ( | |
<Box className={classes.content}> | |
<Box className={classes.fieldContainer}> | |
<Typography className={classes.fieldDesc}> | |
Цена {currency || "$"} | |
</Typography> | |
<Typography className={classes.field}>{price}</Typography> | |
</Box> | |
</Box> | |
)} | |
<Box | |
className={classes.container} | |
sx={{ borderTop: 1, borderColor: "#E0E0E0" }} | |
> | |
<Center className={classes.content}> | |
<Typography className={classes.dateFieldDesc}> | |
Дата создания: | |
</Typography> | |
<Typography className={classes.dateField}> | |
{createdDate} | |
</Typography> | |
</Center> | |
<Center className={classes.content}> | |
<Typography className={classes.dateFieldDesc}> | |
Дата обновления: | |
</Typography> | |
<Typography className={classes.dateField}> | |
{updatedData} | |
</Typography> | |
</Center> | |
</Box> | |
<Button onClick={handleShowHistory} variant="outlined" sx={{ ml: 1, mr: 1 }}>Посмотреть историю</Button> | |
</Box> | |
</Paper> | |
<div className={classes.adjust} /> | |
</div> | |
); | |
} | |
) as React.FC<IDealCardProps>; | |
export default DealCard; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment