const onClose = props.onClose; const componentOwnerId = props.componentOwnerId; const selectedProjectId = props.selectedProjectId; const users = props.users; const selectedUser = props.selectedUser; const setSelectedUser = props.setSelectedUser; const addActionStatus = props.addActionStatus; const contract = props.contract; const editedBoard = props.editedBoard; const updateBoardName = props.updateBoardName; const deleteBoard = props.deleteBoard; const onChangeShowEditBoardDialog = props.onChangeShowEditBoardDialog; const OverlayContainer = styled.div` left: 12px; right: 12px; bottom: 0px; top: 40px; position: absolute; z-index: 20; display: flex; background-color: rgba(0, 0, 0, 0.5); justify-content: center; padding-top: 100px; `; const PopupContainer = styled.div` position: relative; background-color: #1d1d21; padding: 1rem; width: 489px; height: fit-content; `; const Header = styled.div` display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-bottom: 24px; `; const ActionsContainer = styled.div` display: flex; flex-direction: row; gap: 2px; align-items: center; items-center: center; `; const Text = styled.div` display: flex; column-gap: 0.5rem; align-items: center; color: #fff; font-family: Helvetica Neue; font-size: 24px; font-style: normal; font-weight: 500; line-height: 120% margin-bottom: 1rem; `; const Name = styled.input` color: #fff; font-family: Helvetica Neue; font-size: 20px; font-weight: 500; line-height: 24px; letter-spacing: 0em; text-align: left; width: 70%; height: 40px; padding: 8px 60px 8px 0px; border-radius: 4px; background-color: transparent; :focus { outline-color: #d0fc42; outline-style: solid; outline-width: 1px; } ::placeholder { color: #d0fc42; } border: none; `; const CloseButton = styled.div` color: #6b7280; :hover { color: #d0fc42; } cursor: pointer; `; const Divider = styled.div` width: 100%; height: 1px; background-color: #282933; margin-top: 22px; margin-bottom: 16px; `; const SuccessIcon = styled.div` color: #00ff66; `; const ErrorIcon = styled.div` color: #dc3545; `; const FieldContainer = styled.div` position: relative; width: 100%; `; const StatusIcon = styled.div` padding-right: 60px; `; const MissingTitle = styled.div` position: absolute; left: 16px; top: 40px; font-family: Helvetica Neue; font-size: 16px; font-weight: 400; line-height: 24px; letter-spacing: 0em; color: red; `; const FunctionButton = styled.button` background-color: red; :hover { opacity: 0.8; } color: #fff; border-radius: 4px; padding-top: 0.5rem; padding-bottom: 0.5rem; border: none; width: 100%; `; const DeleteColumnContainer = styled.div` position: relative; background-color: #1d1d21; padding: 1rem; width: 489px; height: fit-content; `; const CloseDeleteColumnButton = styled.div` background-color: transparent; display: flex; justify-content: center; color: #6b7280; :hover { color: #fff; } cursor: pointer; padding-top: 0.5rem; padding-bottom: 0.5rem; border: none; width: 100%; `; const TextContainer = styled.div` display: flex; justify-content: center; flex-direction: column; width: 100%; text-align: center; margin-bottom: 1rem; margin-top: 1rem; `; const [selectedOption, setSelectedOption] = useState(1); const [ isRemoveMemberConfirmationVisible, setIsRemoveMemberConfirmationVisible, ] = useState(false); const [removingMember, setRemovingMember] = useState(undefined); const [boardMembers, setBoardMembers] = useState([]); const [addMemberStatus, setAddMemberStatus] = useState(undefined); const [addMemberInputOpen, setMemberInputOpen] = useState(false); const [editedBoardName, setEditedBoardName] = useState(editedBoard[1]); const [editBoardNameStatus, setEditBoardNameStatus] = useState(null); const [editBoardNameMissing, setEditBoardNameMissing] = useState(false); const [showDeleteBoardDialog, setShowDeleteBoardDialog] = useState(false); const onChangeEditBoardName = ({ target }) => { setEditedBoardName(target.value); }; const onChangeShowDeleteBoardDialog = (open) => { setShowDeleteBoardDialog(open); }; const editBoardName = useCallback(async () => { if (!editedBoardName || editedBoardName.trim() === '') { setEditBoardNameMissing(true); setTimeout(() => setEditedBoardName(editedBoard[1]), 3000); setTimeout(() => setEditBoardNameMissing(false), 3000); return; } if (editedBoardName === editedBoard[1]) { return; } try { setEditBoardNameStatus('Saving...'); const newBoard = [editedBoard[0], editedBoardName]; updateBoardName(newBoard); Near.fakCalimeroCall(contract, 'edit_name', { project_id: editedBoard[0], new_name: editedBoardName, }).then(() => { setEditBoardNameStatus('Saved'); setTimeout(() => setEditBoardNameStatus(null), 3000); }); } catch (e) { setEditBoardNameStatus('Error saving'); } }, [editedBoardName, editedBoard]); const handleDeleteBoard = useCallback(() => { const actionStatusPrefix = 'Delete board'; let newActionStatus = { id: actionStatusPrefix + editedBoard[0], status: `Deleting board: ${editedBoard[1]}`, seen: false, }; try { addActionStatus(newActionStatus); deleteBoard(editedBoard[0]); Near.fakCalimeroCall(contract, 'delete_project', { project_id: editedBoard[0], }).then(() => { newActionStatus.status = `Deleted board: ${editedBoard[1]}`; addActionStatus(newActionStatus); }); } catch (e) { newActionStatus.status = `Error deleting board: ${editedBoard[1]}`; addActionStatus(newActionStatus); } onChangeShowEditBoardDialog(false, undefined); onChangeShowDeleteBoardDialog(false); }, [editedBoard]); const removeBoardMember = useCallback( (member) => { setBoardMembers(boardMembers.filter((m) => m.id !== member.id)); }, [boardMembers, setBoardMembers], ); const onSelectRemovingMember = (member) => { setIsRemoveMemberConfirmationVisible(true); setRemovingMember(member); }; const removeMember = useCallback(() => { const actionStatusPrefix = 'Remove member'; let newActionStatus = { id: actionStatusPrefix + editedBoard[0] + removingMember.id, status: `Removing member ${removingMember.id} from board ${editedBoardName}`, seen: false, }; try { addActionStatus(newActionStatus); removeBoardMember(removingMember); Near.fakCalimeroCall(contract, 'remove_member', { project_id: selectedProjectId, member: removingMember.id, }).then(() => { newActionStatus.status = `Removed member ${removingMember.id} from board ${editedBoardName}`; addActionStatus(newActionStatus); }); } catch (e) { console.log('removeMember', e); newActionStatus.status = `Error removing member ${removingMember.id} from board ${editedBoardName}`; addActionStatus(newActionStatus); } setIsRemoveMemberConfirmationVisible(false); }, [removingMember]); const addMember = useCallback( async (accountId) => { setMemberInputOpen(false); const oldMembers = JSON.parse(JSON.stringify(boardMembers)); try { setAddMemberStatus('Saving...'); Near.fakCalimeroCall(contract, 'add_member', { project_id: selectedProjectId, new_member: accountId, }).then(() => { setAddMemberStatus('Saved'); setTimeout(() => setAddMemberStatus(undefined), 3000); }); setSelectedUser(undefined); setBoardMembers([...oldMembers, { id: accountId }]); } catch (e) { setBoardMembers(oldMembers); setAddMemberStatus('Error'); } }, [boardMembers, selectedProjectId, contract], ); const getMembers = useCallback( async (projectId) => { try { Near.asyncCalimeroView(contract, 'get_members', { project_id: projectId, }).then((members) => { setBoardMembers(members.map((member) => ({ id: member }))); }); } catch (e) { console.log('getMembers', e); } }, [contract], ); useEffect(() => { if (selectedProjectId) { getMembers(selectedProjectId); } }, [selectedProjectId]); const BoardDetailsContent = () => ( <OverlayContainer> <PopupContainer> <Header> <FieldContainer> <Name onChange={onChangeEditBoardName} value={editedBoardName} onBlur={editBoardName} placeholder="Add Title" /> {editBoardNameMissing && <MissingTitle>Missing name</MissingTitle>} </FieldContainer> <ActionsContainer> {editBoardNameStatus && ( <StatusIcon> {editBoardNameStatus === 'Saving...' && ( <Widget src={`${componentOwnerId}/widget/Calimero.TaskChain.Loader.Loader`} props={{ size: 16 }} /> )} {editBoardNameStatus === 'Saved' && ( <SuccessIcon> <i className="bi bi-check"></i> </SuccessIcon> )} {editBoardNameStatus === 'Error' && ( <ErrorIcon> <i className="bi bi-x-circle"></i> </ErrorIcon> )} </StatusIcon> )} <CloseButton onClick={onClose}> <i className="bi bi-x-circle"></i> </CloseButton> </ActionsContainer> </Header> <Widget src={`${componentOwnerId}/widget/Calimero.TaskChain.Selector`} props={{ selectorOptions: [ { id: 1, title: 'About', icon: 'bi bi-info-circle-fill' }, { id: 2, title: `Members ${boardMembers.length}`, icon: 'bi bi-people-fill', }, ], onClick: setSelectedOption, selected: selectedOption, }} /> <Divider /> <Widget src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.EditBoardOptions`} props={{ onChangeShowDeleteBoardDialog, editedBoardId: editedBoard[0], addMemberInputOpen, componentOwnerId, setSelectedUser, setMemberInputOpen, selectedUser, boardMembers, selectedOption, users: users.filter( (user) => !boardMembers.some((member) => member.id === user.id), ), addMember, addMemberStatus, onSelectRemovingMember, }} /> </PopupContainer> </OverlayContainer> ); const ConfirmationDialog = ({ message, onConfirm, confirmLabel, onClose, closeLabel, }) => ( <OverlayContainer> <PopupContainer> <DeleteColumnContainer> <TextContainer> <Text>{message}</Text> </TextContainer> <FunctionButton onClick={onConfirm}>{confirmLabel}</FunctionButton> <CloseDeleteColumnButton onClick={onClose}> {closeLabel} </CloseDeleteColumnButton> </DeleteColumnContainer> </PopupContainer> </OverlayContainer> ); const RemoveMemberContent = () => ( <ConfirmationDialog message={`Are you sure you want to remove member ${removingMember.id}?`} onConfirm={removeMember} confirmLabel="Remove" onClose={() => setIsRemoveMemberConfirmationVisible(false)} closeLabel="Close" /> ); const DeleteBoardContent = () => ( <ConfirmationDialog message="Are you sure you want to delete this board?" onConfirm={handleDeleteBoard} confirmLabel="Delete" onClose={() => setShowDeleteBoardDialog(false)} closeLabel="Close" /> ); if (isRemoveMemberConfirmationVisible) { return <RemoveMemberContent />; } else if (showDeleteBoardDialog) { return <DeleteBoardContent />; } else { return <BoardDetailsContent />; }