const contract = props.contract || "taskchain.ws-protocol-63"; const methods = [ "create_new_project", "edit_name", "change_owner", "delete_project", "get_owners_projects", "get_members_projects", "get_project", "get_statuses", "add_status", "reorder_statuses", "remove_status", "add_member", "remove_member", "create_task", "get_task", "change_task_name", "edit_task_description", "assign_task", "remove_assignee", "move_task", "delete_task_by_id", "comment_on_task", "delete_comment_by_id", "edit_comment", "create_label", "get_label", "get_labels", "delete_label", "change_label_text", "change_label_color", "add_label_to_task", "remove_label_from_task", "add_checklist_element", "edit_checklist_element", "mark_checkmark", "delete_checkmark", "upload_file", "remove_file", "set_priority", "reset_priority" ]; const accountId = context.accountId; State.init( { boards: [], selectedProjectId: undefined, bootStrapping: true, loggedIn: false, showAddBoardDialog: false, functionLoader: false, boardName: "", showEditBoardDialog: false, editedBoardId: undefined, editedBoardName: "", showAddTaskDialog: false, taskTitle: "", taskDescription: "", statusData: undefined, createBoardStatus: [], showEditTaskDialog: false, editTaskId: undefined, editTaskTitle: "", editTaskDescription: "", editTaskTitleMissing: false, editTaskDescriptionMissing: false, editTaskColumn: "", editTaskTitleStatus: [], editTaskDescriptionStatus: [], editTaskDialogTaskId: undefined, showDeleteTaskDialog: false, deleteTaskId: undefined, deleteTaskStatus: [], editBoardNameStatus: [], deleteBoardId: undefined, deleteBoardStatus: [], addBoardNameMissing: false, editBoardNameMissing: false, enableEditBoardName: false, addTaskTitleMissing: false, addTaskDescriptionMissing: false } ); const componentOwnerId = props.componentOwnerId || "calimero.testnet"; const createBoard = () => { if (!state.boardName || state.boardName.trim() === '') { setAddBoardNameMissing(true); return; } try { const boardName = state.boardName; State.update({ functionLoader: true }); State.update( { createBoardStatus: [...state.createBoardStatus, {name: boardName, status: "Saving..."}] } ); const newBoard = [ null, boardName, ]; let newBoardsArray = []; const storageBoards = Storage.privateGet( "tempBoards" + contract ); if (storageBoards && JSON.parse(storageBoards).length > 0) { newBoardsArray = JSON.parse(storageBoards); newBoardsArray.push(newBoard); } else { newBoardsArray.push(newBoard); } const jsonStringArray = JSON.stringify(newBoardsArray); Storage.privateSet( "tempBoards" + contract, jsonStringArray ); setBoards(); Near.fakCalimeroCall( contract, "create_new_project", { name: boardName, owner: accountId, members: [ accountId ], statuses: [ "TO DO", "IN PROGRESS", "DONE" ] } ).then( () => { let foundStatus = state.createBoardStatus.find(b => b.name === boardName); foundStatus.status = "Saved"; const filteredStatuses = state.createBoardStatus.filter(b => b.name !== boardName); State.update( { createBoardStatus: [...filteredStatuses, foundStatus] } ); setTimeout(() => cleanBoardCreateStatus(boardName), 3000); setTimeout(getBoards, 3000); } ); } catch (e) { let foundStatus = state.createBoardStatus.find(b => b.name === boardName); foundStatus.status = "Error"; const filteredStatuses = state.createBoardStatus.filter(b => b.name !== boardName); State.update( { createBoardStatus: [...filteredStatuses, foundStatus] } ); Storage.privateSet( "tempBoards" + contract, "" ); } State.update({ functionLoader: false }); onChangeShowAddBoardDialog(false); }; const setBoards = () => { try { let boards = []; Near.asyncCalimeroView( contract, "get_members_projects", { member_id: context.accountId } ).then((b) => { boards = b; const storageBoards = Storage.privateGet( "tempBoards" + contract ); if (storageBoards && JSON.parse(storageBoards).length > 0) { const storageArray = JSON.parse(storageBoards); let newBoards = boards; for (let newBoard of storageArray) { if (boards.find(b => b[0] === newBoard[0])) { let editBoard = boards.find(b => b[0] === newBoard[0]); editBoard[1] = newBoard[1]; newBoards = boards.map(b => { if (b[0] === newBoard[0]) { return editBoard; } else { return b; } }); } else if (!boards.some(b => b[1] === newBoard[1])) { newBoards = [...newBoards, newBoard]; } } State.update({boards: newBoards}); } else { setBoards(); } }) } catch (e) { console.log(e); Storage.privateSet("tempBoards" + contract, ""); } } const cleanBoardCreateStatus = (name) => { let foundStatus = state.createBoardStatus.find(b => b.name === name); foundStatus.status = ""; const filteredStatuses = state.createBoardStatus.filter(b => b.name !== name); State.update( { createBoardStatus: [...filteredStatuses, foundStatus] } ); } const cleanBoardEditStatus = (id) => { const filteredStatuses = state.editBoardNameStatus.filter(b => b.id !== id); State.update( { editBoardNameStatus: [...filteredStatuses] } ); } const editBoardName = () => { onChangeEnableEditBoardName(false); let oldBoard = state.boards.find(b => b[0] === state.editedBoardId); if ((!state.editedBoardName || state.editedBoardName.trim() === '')) { setEditBoardNameMissing(true); setTimeout(() => setEditBoardName(oldBoard[1]), 3000) setTimeout(() => setEditBoardNameMissing(false), 3000) return; } if (state.editedBoardName === oldBoard[1]) { return; } try { State.update( { editBoardNameStatus: [...state.editBoardNameStatus, {id: state.editedBoardId, status: "Saving..."}] } ); const newBoard = [ state.editedBoardId, state.editedBoardName, ]; let newBoardsArray = []; const storageBoards = Storage.privateGet( "tempBoards" + contract ); if (storageBoards && JSON.parse(storageBoards).length > 0) { newBoardsArray = JSON.parse(storageBoards); newBoardsArray.push(newBoard); } else { newBoardsArray.push(newBoard); } const jsonStringArray = JSON.stringify(newBoardsArray); Storage.privateSet( "tempBoards" + contract, jsonStringArray ); setBoards(); Near.fakCalimeroCall( contract, "edit_name", { project_id: state.editedBoardId, new_name: state.editedBoardName } ).then( () => { let foundStatus = state.editBoardNameStatus.find(b => b.id === state.editedBoardId); foundStatus.status = "Saved"; const filteredStatuses = state.editBoardNameStatus.filter(b => b.id !== state.editedBoardId); State.update( { editBoardNameStatus: [...filteredStatuses, foundStatus] } ); setTimeout(() => cleanBoardEditStatus(state.editedBoardId), 3000); } ); } catch (e) { let foundStatus = state.editBoardNameStatus.find(b => b.id === state.editedBoardId); foundStatus.status = "Error saving"; const filteredStatuses = state.editBoardNameStatus.filter(b => b.id !== state.editedBoardId); State.update( { editBoardNameStatus: [...filteredStatuses, foundStatus] } ); Storage.privateSet( "tempBoards" + contract, "" ); setTimeout(getBoards, 3000); } }; const deleteBoard = () => { const oldBoards = state.boards; const boardId = state.deleteBoardId; const newDeleteBoardStatus = [...state.deleteBoardStatus, {boardId: boardId, status: `Deleting board: ${boardId}`}] State.update({ deleteBoardStatus: newDeleteBoardStatus }) try { const newBoards = state.boards.filter(board => board[0] !== boardId) State.update({ boards: newBoards }); Near.fakCalimeroCall( contract, "delete_project", { project_id: boardId, } ).then(() => { let foundStatus = newDeleteBoardStatus.find(b => b.boardId === boardId); foundStatus.status = `Deleted board: ${boardId}`; const filteredStatuses = newDeleteBoardStatus.filter(b => b.boardId !== boardId); State.update({ deleteBoardStatus: [...filteredStatuses, foundStatus] }) setTimeout(() => cleanDeleteBoardStatus(boardId), 3000); }) } catch (e) { let foundStatus = newDeleteBoardStatus.find(b => b.boardId === boardId); foundStatus.status = `Error deleting board: ${board}`; const filteredStatuses = newDeleteBoardStatus.filter(b => b.boardId !== boardId); State.update({ deleteBoardStatus: [...filteredStatuses, foundStatus] }) State.update( { boards: oldBoards } ); } onChangeShowEditBoardDialog(false, undefined, undefined) onChangeShowDeleteBoardDialog(false, undefined) } const cleanDeleteBoardStatus = (id) => { const filteredStatuses = state.deleteTaskStatus.filter(b => b.boardId !== id); State.update({ deleteBoardStatus: filteredStatuses }) } const onChangeBoardName = ({ target }) => { setAddBoardNameMissing(false); State.update({ boardName: target.value }); }; const onEditTaskTitle= ({ target }) => { setEditTaskTitleMissing(false); State.update({ editTaskTitle: target.value }); }; const onEditTaskDescription= ({ target }) => { setEditTaskDescriptionMissing(false); State.update({ editTaskDescription: target.value }); }; const setEditTask= (id, title, description, column) => { State.update({ editTaskId: id, editTaskTitle: title, editTaskDescription: description, editTaskColumn: column, }); }; const setAddBoardNameMissing = ( missing ) => { State.update( { addBoardNameMissing: missing } ); } const setAddTaskTitleMissing = ( missing ) => { State.update( { addTaskTitleMissing: missing } ); }; const setAddTaskDescriptionMissing = ( missing ) => { State.update( { addTaskDescriptionMissing: missing } ); }; const onEditBoardName = ({ target }) => { State.update({ editedBoardName: target.value }); } const onChangeTaskTitle = ({ target }) => { setAddTaskTitleMissing(false); State.update({ taskTitle: target.value }); }; const onChangeTaskDescription = ({ target }) => { setAddTaskDescriptionMissing(false); State.update({ taskDescription: target.value }); }; const onBoardSelected = (projectId) => { State.update( { selectedProjectId: projectId } ); }; const onChangeShowAddBoardDialog = ( open ) => { setAddBoardNameMissing(false); State.update( { showAddBoardDialog: open, boardName: "" } ); }; const setEditTaskTitleMissing = ( missing ) => { State.update( { editTaskTitleMissing: missing } ); }; const setEditTaskDescriptionMissing = ( missing ) => { State.update( { editTaskDescriptionMissing: missing } ); }; const onChangeDeleteTaskStatus = ( status ) => { State.update( { deleteTaskStatus: status } ); }; const onChangeShowEditBoardDialog = ( open, id, name ) => { State.update( { showEditBoardDialog: open, editedBoardId: id, editedBoardName: name } ); }; const onChangeEditBoardName = ( {target} ) => { State.update( { editedBoardName: target.value } ); } const setEditBoardName = (name) => { State.update( { editedBoardName: name } ); } const setEditBoardNameMissing = ( missing ) => { State.update( { editBoardNameMissing: missing } ); }; const onChangeShowAddTaskDialog = ( open ) => { setAddTaskTitleMissing(false); setAddTaskDescriptionMissing(false); State.update( { showAddTaskDialog: open, taskTitle: "", taskDescription: "", } ); }; const onChangeEnableEditBoardName = ( open ) => { State.update( { enableEditBoardName: open } ); }; const onChangeShowEditTaskDialog = ( open, id ) => { setEditTaskTitleMissing(false); setEditTaskDescriptionMissing(false); State.update( { showEditTaskDialog: open, editTaskDialogTaskId: id } ); }; const onChangeShowDeleteTaskDialog = ( open, id ) => { State.update( { showDeleteTaskDialog: open, deleteTaskId: id, } ); }; const onChangeShowDeleteBoardDialog = ( open, id ) => { State.update( { showDeleteBoardDialog: open, deleteBoardId: id, } ); }; const onChangeFunctionLoader = ( loading ) => { State.update( { functionLoader: loading } ); }; const updateStatusData = (data) => { State.update({ statusData: data}); }; const clearTaskTitle = () => { State.update({ taskTitle: ""}); }; const clearTaskDescription = () => { State.update({ taskDescription: ""}); }; const bootStrapApp = () => { Near.hasValidCalimeroFak( contract ).then( ( result ) => { console.log( "hasValidCalimeroFak", result ); if ( result ) { State.update( { bootStrapping: false, loggedIn: true } ); getBoards(); } else { State.update( { bootStrapping: false, loggedIn: false } ); } } ); }; const getBoards = () => { Near.asyncCalimeroView( contract, "get_members_projects", { member_id: context.accountId } ).then( ( boards ) => { State.update( { boards, selectedProjectId: boards.length > 0 ? boards[ 0 ][ 0 ] : undefined, bootStrapping: false, } ); } ); }; if ( state.bootStrapping ) { bootStrapApp(); return <Widget src={`${componentOwnerId}/widget/Calimero.TaskChain.Loading`} />; } const getStatuses = () => { Near.asyncCalimeroView( contract, "get_statuses", { project_id: state.selectedProjectId } ).then( ( data ) => { updateStatusData(data) } ); }; const PageContainer = styled.div` display: flex; flex-direction: column; width: 100%; height: 100%; background-color: #0E0E10; padding-left: 60px; padding-right: 60px; padding-top: 26px; gap: 24px; `; const TaskChainLogo = styled.a` display: flex; flex-direction: space-between; align-items: center width: 100%; height: 100vh; background-color: #0E0E10; color: #FFFFFF; font-family: Inter; font-weight: 700; font-size: 20.92px; line-height: 31.39px; :hover { text-decoration: none; } `; const HeaderContainer = styled.div` display: flex; flex-direction: row; justify-content: space-between; align-items: center; width: 100%; `; const BoardContainer = styled.div` padding-top: 8px; display: flex; flex-direction: row; justify-content: center; `; const DeleteStatusContainer = styled.div` display: flex; flex-direction: column; `; const Divider = styled.div` width: 100%; height: 1px; background-color: #282933; `; const Text = styled.text` color: white; `; return ( (!state.loggedIn) ? <Widget src={`${componentOwnerId}/widget/Calimero.TaskChain.JoinApp`} props={ { appName: "TaskChain", contractName: contract, methodNames: methods, componentOwnerId } } /> : <PageContainer> <HeaderContainer> <Widget src={`${componentOwnerId}/widget/Calimero.TaskChain.TaskChainLogo`} /> <DeleteStatusContainer> {state.deleteTaskStatus.length > 0 && deleteTaskStatus.map(d => ( <Text key={d.id}> {d.status} </Text> )) } {state.deleteBoardStatus.length > 0 && deleteBoardStatus.map(b => ( <Text key={b.id}> {b.status} </Text> )) } </DeleteStatusContainer> </HeaderContainer> <Divider>.</Divider> <BoardContainer> <Widget src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.SideMenu`} props={{ componentOwnerId, boards: state.boards, selectedProjectId: state.selectedProjectId, createBoard, onBoardSelected, showAddBoardDialog: state.showAddBoardDialog, onChangeShowAddBoardDialog, functionLoader: state.functionLoader, onChangeBoardName, boardName: state.boardName, editBoardName, showEditBoardDialog: state.showEditBoardDialog, onChangeShowEditBoardDialog, onEditBoardName, editedBoardId: state.editedBoardId, editedBoardName: state.editedBoardName, onChangeEditBoardName, createBoardStatus: state.createBoardStatus, editBoardNameStatus: state.editBoardNameStatus, onChangeShowDeleteBoardDialog, showDeleteBoardDialog: state.showDeleteBoardDialog, deleteBoard }} /> {state.selectedProjectId && ( <Widget src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Board`} props={{ componentOwnerId, contract, projectId: state.selectedProjectId, showAddTaskDialog: state.showAddTaskDialog, taskTitle: state.taskTitle, taskDescription: state.taskDescription, onChangeTaskTitle, onChangeTaskDescription, onChangeShowAddTaskDialog, functionLoader, statusData, updateStatusData, onChangeFunctionLoader, clearTaskTitle, clearTaskDescription, showEditTaskDialog, onChangeShowEditTaskDialog, onEditTaskTitle, onEditTaskDescription, setEditTask, editTaskTitle: state.editTaskTitle, editTaskDescription: state.editTaskDescription, editTaskId: state.editTaskId, editTaskColumn: state.editTaskColumn, editTaskDialogTaskId: state.editTaskDialogTaskId, onChangeShowDeleteTaskDialog, showEditTaskDialog: state.showEditTaskDialog, showDeleteTaskDialog: state.showDeleteTaskDialog, deleteTaskId: state.deleteTaskId, deleteTaskStatus: state.deleteTaskStatus, onChangeDeleteTaskStatus, }} /> )} </BoardContainer> </PageContainer> )