| | |
| | | import { Draggable } from '@hello-pangea/dnd'; |
| | | import { Box, Card, Typography } from '@mui/material'; |
| | | import { ReferenceField, useRedirect } from 'react-admin'; |
| | | import { CompanyAvatar } from '../companies/CompanyAvatar'; |
| | | import { Deal } from '../types'; |
| | | |
| | | export const MissionCard = ({ deal, index }) => { |
| | | if (!deal) return null; |
| | | export const MissionCard = ({ mission, index }) => { |
| | | if (!mission) return null; |
| | | |
| | | return ( |
| | | <Draggable draggableId={String(deal.id)} index={index}> |
| | | <Draggable draggableId={String(mission.id)} index={index}> |
| | | {(provided, snapshot) => ( |
| | | <DealCardContent |
| | | <MissionCardContent |
| | | provided={provided} |
| | | snapshot={snapshot} |
| | | deal={deal} |
| | | mission={mission} |
| | | /> |
| | | )} |
| | | </Draggable> |
| | | ); |
| | | }; |
| | | |
| | | export const DealCardContent = ({ |
| | | export const MissionCardContent = ({ |
| | | provided, |
| | | snapshot, |
| | | deal, |
| | | mission, |
| | | }) => { |
| | | const redirect = useRedirect(); |
| | | const handleClick = () => { |
| | | redirect(`/deals/${deal.id}/show`, undefined, undefined, undefined, { |
| | | redirect(`/mission/${mission.id}/show`, undefined, undefined, undefined, { |
| | | _scrollToTop: false, |
| | | }); |
| | | }; |
| | |
| | | <Box padding={1} display="flex"> |
| | | <ReferenceField |
| | | source="company_id" |
| | | record={deal} |
| | | record={mission} |
| | | reference="companies" |
| | | link={false} |
| | | > |
| | | <CompanyAvatar width={20} height={20} /> |
| | | </ReferenceField> |
| | | <Box sx={{ marginLeft: 1 }}> |
| | | <Typography variant="body2" gutterBottom> |
| | | {deal.name} |
| | | {mission.groupNo} |
| | | </Typography> |
| | | <Typography variant="caption" color="textSecondary"> |
| | | {deal.amount.toLocaleString('en-US', { |
| | | notation: 'compact', |
| | | style: 'currency', |
| | | currency: 'USD', |
| | | currencyDisplay: 'narrowSymbol', |
| | | minimumSignificantDigits: 3, |
| | | })} |
| | | {deal.category ? `, ${deal.category}` : ''} |
| | | 1 |
| | | </Typography> |
| | | </Box> |
| | | </Box> |
| | |
| | | import { Droppable } from '@hello-pangea/dnd'; |
| | | import { Box, Stack, Typography } from '@mui/material'; |
| | | |
| | | import { Deal } from '../types'; |
| | | import { DealCard } from './MissionCard'; |
| | | import { useConfigurationContext } from '../root/ConfigurationContext'; |
| | | import { findDealLabel } from './deal'; |
| | | import { MissionCard } from './MissionCard'; |
| | | |
| | | export const MissionColumn = ({ stage, deals, }) => { |
| | | const totalAmount = deals.reduce((sum, deal) => sum + deal.amount, 0); |
| | | export const MissionColumn = ({ stage, missions, }) => { |
| | | |
| | | const { dealStages } = useConfigurationContext(); |
| | | return ( |
| | | <Box |
| | | sx={{ |
| | |
| | | > |
| | | <Stack alignItems="center"> |
| | | <Typography variant="subtitle1"> |
| | | {findDealLabel(dealStages, stage)} |
| | | 1 |
| | | {/* {findDealLabel(dealStages, stage)} */} |
| | | </Typography> |
| | | <Typography |
| | | variant="subtitle1" |
| | | color="text.secondary" |
| | | fontSize="small" |
| | | > |
| | | {totalAmount.toLocaleString('en-US', { |
| | | notation: 'compact', |
| | | style: 'currency', |
| | | currency: 'USD', |
| | | currencyDisplay: 'narrowSymbol', |
| | | minimumSignificantDigits: 3, |
| | | })} |
| | | 2 |
| | | </Typography> |
| | | </Stack> |
| | | <Droppable droppableId={stage}> |
| | |
| | | }, |
| | | }} |
| | | > |
| | | {deals.map((deal, index) => ( |
| | | <DealCard key={deal.id} deal={deal} index={index} /> |
| | | {missions.map((mission, idx) => ( |
| | | <MissionCard key={mission.id} mission={mission} index={idx} /> |
| | | ))} |
| | | {droppableProvided.placeholder} |
| | | </Box> |
| | |
| | | import * as Common from '@/utils/common'; |
| | | // import { MissionEmpty } from "./MissionEmpty"; |
| | | import MissionShow from "./MissionShow"; |
| | | import { MissionListContent } from "./MissionListContent"; |
| | | |
| | | const MissionList = () => { |
| | | |
| | |
| | | const MissionLayout = () => { |
| | | const location = useLocation(); |
| | | const matchShow = matchPath('/mission/:id/show', location.pathname); |
| | | console.log(matchShow); |
| | | |
| | | |
| | | const { data, isPending, filterValues } = useListContext(); |
| | | if (isPending) return null; |
| | |
| | | </TopToolbar> |
| | | )} /> |
| | | <Card> |
| | | {/* <DealListContent /> */} |
| | | <MissionListContent /> |
| | | </Card> |
| | | {/* <DealArchivedList /> */} |
| | | |
| | |
| | | import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; |
| | | import { DragDropContext } from '@hello-pangea/dnd'; |
| | | import { Box } from '@mui/material'; |
| | | import isEqual from 'lodash/isEqual'; |
| | | import { useEffect, useState } from 'react'; |
| | | import { DataProvider, useDataProvider, useListContext } from 'react-admin'; |
| | | import { useDataProvider, useListContext } from 'react-admin'; |
| | | |
| | | import { Deal } from '../types'; |
| | | import { DealColumn } from './MissionColumn'; |
| | | import { DealsByStage, getDealsByStage } from './stages'; |
| | | import { useConfigurationContext } from '../root/ConfigurationContext'; |
| | | import { MissionColumn } from './MissionColumn'; |
| | | |
| | | export const MissionListContent = () => { |
| | | const { dealStages } = useConfigurationContext(); |
| | | const { data: unorderedDeals, isPending, refetch } = useListContext(); |
| | | const { data, isPending, refetch } = useListContext(); |
| | | const dataProvider = useDataProvider(); |
| | | |
| | | const [dealsByStage, setDealsByStage] = useState( |
| | | getDealsByStage([], dealStages) |
| | | ); |
| | | // const [dealsByStage, setDealsByStage] = useState( |
| | | // getDealsByStage([], dealStages) |
| | | // ); |
| | | |
| | | useEffect(() => { |
| | | if (unorderedDeals) { |
| | | const newDealsByStage = getDealsByStage(unorderedDeals, dealStages); |
| | | if (!isEqual(newDealsByStage, dealsByStage)) { |
| | | setDealsByStage(newDealsByStage); |
| | | } |
| | | } |
| | | // if (data) { |
| | | // const newDealsByStage = getDealsByStage(unorderedDeals, dealStages); |
| | | // if (!isEqual(newDealsByStage, dealsByStage)) { |
| | | // setDealsByStage(newDealsByStage); |
| | | // } |
| | | // } |
| | | // eslint-disable-next-line react-hooks/exhaustive-deps |
| | | }, [unorderedDeals]); |
| | | }, [data]); |
| | | |
| | | if (isPending) return null; |
| | | |
| | | const onDragEnd = result => { |
| | | const { destination, source } = result; |
| | | |
| | | if (!destination) { |
| | | return; |
| | | } |
| | | // if (!destination) { |
| | | // return; |
| | | // } |
| | | |
| | | if ( |
| | | destination.droppableId === source.droppableId && |
| | | destination.index === source.index |
| | | ) { |
| | | return; |
| | | } |
| | | // if ( |
| | | // destination.droppableId === source.droppableId && |
| | | // destination.index === source.index |
| | | // ) { |
| | | // return; |
| | | // } |
| | | |
| | | const sourceStage = source.droppableId; |
| | | const destinationStage = destination.droppableId; |
| | | const sourceDeal = dealsByStage[sourceStage][source.index]; |
| | | const destinationDeal = dealsByStage[destinationStage][ |
| | | destination.index |
| | | ] ?? { |
| | | stage: destinationStage, |
| | | index: undefined, // undefined if dropped after the last item |
| | | }; |
| | | // const sourceStage = source.droppableId; |
| | | // const destinationStage = destination.droppableId; |
| | | // const sourceDeal = dealsByStage[sourceStage][source.index]; |
| | | // const destinationDeal = dealsByStage[destinationStage][ |
| | | // destination.index |
| | | // ] ?? { |
| | | // stage: destinationStage, |
| | | // index: undefined, // undefined if dropped after the last item |
| | | // }; |
| | | |
| | | // compute local state change synchronously |
| | | setDealsByStage( |
| | | updateDealStageLocal( |
| | | sourceDeal, |
| | | { stage: sourceStage, index: source.index }, |
| | | { stage: destinationStage, index: destination.index }, |
| | | dealsByStage |
| | | ) |
| | | ); |
| | | // // compute local state change synchronously |
| | | // setDealsByStage( |
| | | // updateDealStageLocal( |
| | | // sourceDeal, |
| | | // { stage: sourceStage, index: source.index }, |
| | | // { stage: destinationStage, index: destination.index }, |
| | | // dealsByStage |
| | | // ) |
| | | // ); |
| | | |
| | | // persist the changes |
| | | updateDealStage(sourceDeal, destinationDeal, dataProvider).then(() => { |
| | | refetch(); |
| | | }); |
| | | // // persist the changes |
| | | // updateDealStage(sourceDeal, destinationDeal, dataProvider).then(() => { |
| | | // refetch(); |
| | | // }); |
| | | }; |
| | | |
| | | const columns = ['a', 'b'] |
| | | |
| | | return ( |
| | | <DragDropContext onDragEnd={onDragEnd}> |
| | | <Box display="flex"> |
| | | {dealStages.map(stage => ( |
| | | <DealColumn |
| | | stage={stage.value} |
| | | deals={dealsByStage[stage.value]} |
| | | key={stage.value} |
| | | {columns.map(column => ( |
| | | <MissionColumn |
| | | stage={column} |
| | | missions={data} |
| | | key={column} |
| | | /> |
| | | ))} |
| | | </Box> |