Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export const ChangeDiffComponent = () => {
</Card>
</Tab>
<Tab eventKey={MESSAGES} title={MESSAGES}>
<Messages/>
<Messages setKey={setKey}/>
</Tab>
</Tabs>
}
Expand Down
59 changes: 48 additions & 11 deletions packages/tdb-dashboard/src/components/Messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ import Card from 'react-bootstrap/Card'
import Form from 'react-bootstrap/Form'
import {Button} from "react-bootstrap"
import {ChangeRequest} from "../hooks/ChangeRequest"
import ProgressBar from 'react-bootstrap/ProgressBar'
import { useParams } from "react-router-dom"
import {
extractID,
getDays
} from "./utils"
import {VscCommentDiscussion} from "react-icons/vsc"
import {Loading} from "./Loading"
import {Review} from "./ReviewComponent"
import {COMMENT} from "./constants"
import Spinner from 'react-bootstrap/Spinner';
import * as CONST from "./constants"

const CommentSection = ({currentCRObject}) => {
// displays Previous Messages
const CommentSection = () => {
const { currentCRObject }= WOQLClientObj()

if (!currentCRObject.hasOwnProperty("messages"))
return <div className="mt-2">No messages to display ...</div>
Expand All @@ -35,19 +37,54 @@ const CommentSection = ({currentCRObject}) => {
return <Card className="mb-3 w-100 mt-2 p-5 border-secondary">{elements}</Card>
}

export const Messages = () => {
const {
currentCRObject
} = WOQLClientObj()
// displays textarea to write comments
export const MessageBox = ({ setMessage, message }) => {
return <Form.Control as="textarea"
rows={5}
value={message}
onChange={e => setMessage(e.target.value)}
style={{color: "white"}}
className="bg-dark border-secondary"
placeholder={"Add a new comment or message ..."}/>
}

// displays textarea to write comments & button to save comments
export const MessageComponent = ({setKey}) => {
const { currentCRObject, setCurrentCRObject }= WOQLClientObj()
const { updateChangeRequestStatus, loading } = ChangeRequest()
const [comment, setComment]=useState("")
const { id } = useParams()

/** handle Message */
async function handleMessage(comment) {
let id=extractID(currentCRObject["@id"])
// this call return the changeRequestObj Updated
let res=await updateChangeRequestStatus(comment, currentCRObject.status, id)
// we'll see if add need rebase check every time
res.needRebase = currentCRObject.needRebase
setCurrentCRObject(res)
if(setKey) setKey(CONST.MESSAGES)
setComment("")
}

return <React.Fragment>
<MessageBox setMessage={setComment} message={comment}/>
<Button className={"btn btn-sm bg-light text-dark float-right"}
onClick={(e) => handleMessage(comment)}>
{loading && <Spinner as="span" animation="border" size="sm" role="status" className="mr-1 mt-1" aria-hidden="true"/>}
{CONST.COMMENT}
</Button>
</React.Fragment>
}


export const Messages = ({setKey}) => {
return <React.Fragment>
<Review message={comment} setMessage={setComment} checked={COMMENT}/>
<MessageComponent setKey={setKey}/>
<br/>
<h5 className="fw-bold text-muted mt-5 mb-3">
<VscCommentDiscussion/> Previous Messages
</h5>
<CommentSection currentCRObject={currentCRObject}/>
<CommentSection/>
</React.Fragment>

}
224 changes: 64 additions & 160 deletions packages/tdb-dashboard/src/components/ReviewComponent.js
Original file line number Diff line number Diff line change
@@ -1,180 +1,92 @@
import React, {useState} from "react"
import React, {useEffect, useState} from "react"
import * as CONST from "./constants"
import Card from "react-bootstrap/Card"
import Button from 'react-bootstrap/Button'
import {useNavigate,useParams } from "react-router-dom"
import {WOQLClientObj} from "../init-woql-client"
import Form from "react-bootstrap/Form"
import Stack from "react-bootstrap/Stack"
import {status, extractID} from "./utils"
import {status} from "./utils"
import {ChangeRequest} from "../hooks/ChangeRequest"
import Spinner from 'react-bootstrap/Spinner';

const ActionButton = ({variant, title, content, onClick, loading, icon}) => {
return <Button
className="text-dark btn btn-sm fw-bold d-flex mt-3 float-end"
variant={variant}
title={title}
onClick={onClick}>
{loading && <Spinner
as="span"
animation="border"
size="sm"
role="status"
className="mr-1 mt-1"
aria-hidden="true"
/>}
<div className="d-flex">
{icon}
<label>{content}</label>
</div>
</Button>
}

/**
* @returns buttons to reject commit or approve based on user action
*/
const Actions = ({checked, message, setKey, setMessage}) => {

const {
currentCRObject,
setCurrentCRObject,
exitChangeRequestBranch
}= WOQLClientObj()

const {
updateChangeRequestStatus,
getChangeRequestList,
loading
} = ChangeRequest()

const {organization,dataProduct,id} = useParams()
const navigate = useNavigate() // to navigate

/** handle Message */
async function handleMessage() {
let id=extractID(currentCRObject["@id"])
// this call return the changeRequestObj Updated
let res=await updateChangeRequestStatus(message, currentCRObject.status, id)
// we'll see if add need rebase check every time
res.needRebase = currentCRObject.needRebase
setCurrentCRObject(res)
if(setKey) setKey(CONST.MESSAGES)
if(setMessage) setMessage("")
}

/** handle Merge */
async function handleMerge () {
let res=await updateChangeRequestStatus(message, CONST.MERGED, id)
if(res){
setCurrentCRObject(false)
exitChangeRequestBranch()
navigate(`/${organization}/${dataProduct}`)
}
}

/** handle Reject */
async function handleReject () {
let res=await updateChangeRequestStatus(message, CONST.REJECTED, id)
if(res){
setCurrentCRObject(false)
exitChangeRequestBranch()
navigate(`/${organization}/${dataProduct}`)
}
}

let chosen = CONST.REVIEW_OPTIONS.filter(arr => arr.title === checked)

if(checked === CONST.APPROVE) {
return <ActionButton variant="success"
loading={loading}
title={"Approve Changes"}
content={checked}
icon={chosen[0].icon}
onClick={handleMerge}/>
}
else if (checked === CONST.COMMENT) {
return <ActionButton variant="light"
title={"Leave a Comment or message"}
content={checked}
loading={loading}
icon={chosen[0].icon}
onClick={handleMessage}/>
}
else if(checked ===CONST.REJECT) {
return <ActionButton variant="danger"
title={"Reject Changes"}
content={checked}
loading={loading}
icon={chosen[0].icon}
onClick={handleReject}/>
}
return <div/>
}

/**
* @returns help texts
*/
function getHelpText (checked) {
const arr = CONST.REVIEW_OPTIONS.filter(arr => arr.title === checked)
if (!arr) return <div/>
return <small className="fw-light">{arr[0].helpText}</small>
}

function getChecked (checked, id) {
return checked === id ? true : false
import ButtonGroup from 'react-bootstrap/ButtonGroup';
import ToggleButton from 'react-bootstrap/ToggleButton';
import {Loading} from "../components/Loading"
import { MessageBox, MessageComponent } from "./Messages"
import {AiOutlineCheck, AiOutlineClose} from "react-icons/ai"

const ToggleActions = ({ message }) => {
const [action, setAction] = useState(false);
const { setCurrentCRObject, exitChangeRequestBranch }= WOQLClientObj()
const { updateChangeRequestStatus, loading } = ChangeRequest()
const { organization, dataProduct , id} = useParams()
const navigate = useNavigate()


useEffect(() => {
async function doAction() {
let status = action === CONST.APPROVE ? CONST.MERGED : CONST.REJECTED
let res=await updateChangeRequestStatus(message, status, id)
if(res){
setCurrentCRObject(false)
exitChangeRequestBranch()
navigate(`/${organization}/${dataProduct}`)
}
}
if(action) doAction()
}, [action])

const reviewButtons = [
{ name: CONST.APPROVE, value: CONST.APPROVE, className: "rounded-left", variant: "outline-success", icon: <AiOutlineCheck className="mr-1 mb-1 text-success"/> },
{ name: CONST.REJECT, value: CONST.REJECT , className: "rounded-right", variant: "outline-danger", icon: <AiOutlineClose className="mr-1 mb-1 text-danger"/> }
];

if(loading) return <Loading message={action === CONST.APPROVE ? `Approving Change Request ...` : `Rejecting Change Request ...`}/>

return <Stack directtion="horizontal" className="float-right">
<small className="text-muted fst-italic fw-light mr-2 ms-auto">
{`Approve or Reject Change Request`}
</small>
<ButtonGroup>
{reviewButtons.map((button) => (
<ToggleButton
key={button.name}
id={button.name}
type="radio"
variant={button.variant}
name={button.name}
value={button.value}
className={button.className}
checked={action === button.value}
onChange={(e) => setAction(e.currentTarget.value)}
>
{button.icon}{button.name}
</ToggleButton>
))}
</ButtonGroup>
</Stack>
}

/**
* @returns Check boxes for CR actions
*/
const FormCheck = ({checked, setChecked}) => {
return CONST.REVIEW_OPTIONS.map(arr => {
let id=arr.title
return <>
<Form.Check
checked={getChecked(checked, id)}
onChange={(e) => setChecked(id)}
className="mr-4"
type={"radio"}
id={id}
label={arr.title}
/>
</>
})
}

/**
* @returns view based on CR actions
*/
export const Review = ({message, setMessage, checked, setKey}) => {
export const Review = ({ message, setMessage }) => {
return <React.Fragment>
<Form.Control as="textarea"
rows={5}
value={message}
onChange={e => setMessage(e.target.value)}
style={{color: "white"}}
className="bg-dark border-secondary"
placeholder={"Add a new comment or message ..."}/>

<Actions checked={checked} message={message} setMessage={setMessage} setKey={setKey}/>
<MessageBox setMessage={setMessage} message={message}/>
<ToggleActions message={message}/>
</React.Fragment>
}

export const ReviewComponent = ({setKey}) => {
export const ReviewComponent = () => {
const {
userHasMergeRole,
currentCRObject
}= WOQLClientObj()

// set default action as COMMENT
const [checked, setChecked]=useState(CONST.APPROVE)
// feedback constants
const [message, setMessage]=useState("")

if(!userHasMergeRole) {
// for collaborator or reader role
return <Review message={message} setMessage={setMessage} checked={checked}/>
return <MessageComponent/>
}

return <Card className="bg-transparent border border-dark m-3">
Expand All @@ -186,15 +98,7 @@ export const ReviewComponent = ({setKey}) => {
</Stack>
</Card.Header>
<Card.Body>

<div className="d-flex mb-1">
<Stack direction="horizontal" gap={3} className="text-right w-100">
<FormCheck checked={checked} setChecked={setChecked}/>
<span className="text-light ms-auto">{getHelpText(checked)}</span>
</Stack>
</div>

<Review message={message} setMessage={setMessage} checked={checked} setKey={setKey}/>
<Review message={message} setMessage={setMessage}/>
</Card.Body>
</Card>

Expand Down
26 changes: 0 additions & 26 deletions packages/tdb-dashboard/src/components/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import {BsBookmarkPlus} from "react-icons/bs"
import * as PATH from "../routing/constants"
import {GrGraphQl} from "react-icons/gr"
import {BiGitPullRequest} from "react-icons/bi"
import {TbMessageCircle} from "react-icons/tb"
import {AiOutlineCheck, AiOutlineClose} from "react-icons/ai"

//User Messages
export const SERVER_LOADING_MESSAGE = "Setting up TerminusCMS ... "
Expand Down Expand Up @@ -479,30 +477,6 @@ export const REJECT="Reject"
export const UPDATE_BRANCH="Update Change Request"
export const NEED_REBASE="needRebase"

// review component constants
export const REVIEW_OPTIONS = [
{
title: COMMENT,
helpText: "Submit a comment or a message ...",
icon: <TbMessageCircle className="mt-1 mr-1"/>
},
{
title: APPROVE,
helpText: "Submit a comment or a message and approve changes for merge ...",
icon: <AiOutlineCheck className="mt-1 mr-1"/>
},
{
title: REJECT,
helpText: "Submit a comment or a message and reject changes ...",
icon: <AiOutlineClose className="mt-1 mr-1"/>
}
]

/* {
title: COMMENT,
helpText: "Submit a comment or a message ...",
icon: <TbMessageCircle className="mt-1 mr-1"/>
},*/

// Document types
export const THEME="Theme"
Expand Down
Loading