mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
first draft of the comments ui (#2440)
This commit is contained in:
parent
8dac08c5aa
commit
51ad239553
@ -0,0 +1,44 @@
|
||||
import { FC } from 'react';
|
||||
import { Box, Button, styled, TextField } from '@mui/material';
|
||||
import { StyledAvatar } from './StyledAvatar';
|
||||
|
||||
const AddCommentWrapper = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
gap: theme.spacing(2),
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(1),
|
||||
}));
|
||||
|
||||
export const AddCommentField: FC<{
|
||||
imageUrl: string;
|
||||
commentText: string;
|
||||
onAddComment: () => void;
|
||||
onTypeComment: (text: string) => void;
|
||||
}> = ({ imageUrl, commentText, onTypeComment, onAddComment }) => (
|
||||
<>
|
||||
<AddCommentWrapper>
|
||||
<StyledAvatar src={imageUrl} />
|
||||
<TextField
|
||||
variant="outlined"
|
||||
placeholder="Add your comment here"
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
onChange={e => onTypeComment(e.target.value)}
|
||||
value={commentText}
|
||||
/>
|
||||
</AddCommentWrapper>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={onAddComment}
|
||||
disabled={
|
||||
commentText.trim().length === 0 ||
|
||||
commentText.trim().length > 1000
|
||||
}
|
||||
>
|
||||
Comment
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
);
|
@ -0,0 +1,47 @@
|
||||
import { FC } from 'react';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import { Box, styled, Typography } from '@mui/material';
|
||||
import TimeAgo from 'react-timeago';
|
||||
import { StyledAvatar } from './StyledAvatar';
|
||||
import { IChangeRequestComment } from '../../changeRequest.types';
|
||||
|
||||
const ChangeRequestCommentWrapper = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
gap: theme.spacing(2),
|
||||
marginTop: theme.spacing(2),
|
||||
}));
|
||||
const CommentPaper = styled(Paper)(({ theme }) => ({
|
||||
width: '100%',
|
||||
padding: theme.spacing(2),
|
||||
backgroundColor: theme.palette.tertiary.light,
|
||||
}));
|
||||
|
||||
const CommentHeader = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: theme.palette.divider,
|
||||
paddingBottom: theme.spacing(1),
|
||||
}));
|
||||
|
||||
export const ChangeRequestComment: FC<{ comment: IChangeRequestComment }> = ({
|
||||
comment,
|
||||
}) => (
|
||||
<ChangeRequestCommentWrapper>
|
||||
<StyledAvatar src={comment.createdBy.imageUrl} />
|
||||
<CommentPaper variant="outlined">
|
||||
<CommentHeader>
|
||||
<Box>
|
||||
<strong>{comment.createdBy.username}</strong>{' '}
|
||||
<Typography color="text.secondary" component="span">
|
||||
commented{' '}
|
||||
<TimeAgo
|
||||
minPeriod={60}
|
||||
date={new Date(comment.createdAt)}
|
||||
/>
|
||||
</Typography>
|
||||
</Box>
|
||||
</CommentHeader>
|
||||
<Box sx={{ paddingTop: 2 }}>{comment.text}</Box>
|
||||
</CommentPaper>
|
||||
</ChangeRequestCommentWrapper>
|
||||
);
|
@ -0,0 +1,7 @@
|
||||
import { Avatar, styled } from '@mui/material';
|
||||
|
||||
export const StyledAvatar = styled(Avatar)(({ theme }) => ({
|
||||
height: '30px',
|
||||
width: '30px',
|
||||
marginTop: theme.spacing(1),
|
||||
}));
|
@ -1,5 +1,5 @@
|
||||
import { Alert, styled } from '@mui/material';
|
||||
import { FC, useContext } from 'react';
|
||||
import { Alert, Button, styled, TextField, Typography } from '@mui/material';
|
||||
import { FC, useContext, useState } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest';
|
||||
import { ChangeRequestHeader } from './ChangeRequestHeader/ChangeRequestHeader';
|
||||
@ -19,6 +19,14 @@ import PermissionButton from 'component/common/PermissionButton/PermissionButton
|
||||
import { APPLY_CHANGE_REQUEST } from 'component/providers/AccessProvider/permissions';
|
||||
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import {
|
||||
StyledAvatar,
|
||||
StyledCard,
|
||||
} from './ChangeRequestHeader/ChangeRequestHeader.styles';
|
||||
import TimeAgo from 'react-timeago';
|
||||
import { InputProps as StandardInputProps } from '@mui/material/Input/Input';
|
||||
import { ChangeRequestComment } from './ChangeRequestComments/ChangeRequestComment';
|
||||
import { AddCommentField } from './ChangeRequestComments/AddCommentField';
|
||||
|
||||
const StyledAsideBox = styled(Box)(({ theme }) => ({
|
||||
width: '30%',
|
||||
@ -48,13 +56,14 @@ export const ChangeRequestOverview: FC = () => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const { user } = useAuthUser();
|
||||
const { isAdmin } = useContext(AccessContext);
|
||||
const [commentText, setCommentText] = useState('');
|
||||
|
||||
const id = useRequiredPathParam('id');
|
||||
const { data: changeRequest, refetchChangeRequest } = useChangeRequest(
|
||||
projectId,
|
||||
id
|
||||
);
|
||||
const { changeState } = useChangeRequestApi();
|
||||
const { changeState, addComment } = useChangeRequestApi();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
|
||||
if (!changeRequest) {
|
||||
@ -77,6 +86,21 @@ export const ChangeRequestOverview: FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const onAddComment = async () => {
|
||||
try {
|
||||
await addComment(projectId, id, commentText);
|
||||
setCommentText('');
|
||||
refetchChangeRequest();
|
||||
setToastData({
|
||||
type: 'success',
|
||||
title: 'Success',
|
||||
text: 'Comment added',
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
};
|
||||
|
||||
const isSelfReview =
|
||||
changeRequest?.createdBy.id === user?.id &&
|
||||
changeRequest.state === 'In review' &&
|
||||
@ -109,6 +133,18 @@ export const ChangeRequestOverview: FC = () => {
|
||||
<StyledInnerContainer>
|
||||
Changes
|
||||
<ChangeRequest changeRequest={changeRequest} />
|
||||
{changeRequest.comments?.map(comment => (
|
||||
<ChangeRequestComment
|
||||
key={comment.id}
|
||||
comment={comment}
|
||||
/>
|
||||
))}
|
||||
<AddCommentField
|
||||
imageUrl={changeRequest?.createdBy?.imageUrl}
|
||||
commentText={commentText}
|
||||
onAddComment={onAddComment}
|
||||
onTypeComment={setCommentText}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={isSelfReview}
|
||||
show={
|
||||
|
@ -68,7 +68,7 @@ const DraftBannerContent: FC<{
|
||||
const StickyBanner = styled(Box)(({ theme }) => ({
|
||||
position: 'sticky',
|
||||
top: -1,
|
||||
zIndex: 200 /* has to lower than header.zIndex */,
|
||||
zIndex: 250 /* has to lower than header.zIndex and higher than body.zIndex */,
|
||||
borderTop: `1px solid ${theme.palette.warning.border}`,
|
||||
borderBottom: `1px solid ${theme.palette.warning.border}`,
|
||||
backgroundColor: theme.palette.warning.light,
|
||||
|
@ -10,6 +10,7 @@ export interface IChangeRequest {
|
||||
createdAt: Date;
|
||||
features: IChangeRequestFeature[];
|
||||
approvals: IChangeRequestApproval[];
|
||||
comments: IChangeRequestComment[];
|
||||
conflict?: string;
|
||||
}
|
||||
|
||||
@ -30,6 +31,13 @@ export interface IChangeRequestApproval {
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface IChangeRequestComment {
|
||||
text: string;
|
||||
createdBy: Pick<IUser, 'username' | 'imageUrl'>;
|
||||
createdAt: Date;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface IChangeRequestBase {
|
||||
id: number;
|
||||
action: ChangeRequestAction;
|
||||
|
@ -98,12 +98,31 @@ export const useChangeRequestApi = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const addComment = async (
|
||||
projectId: string,
|
||||
changeSetId: string,
|
||||
text: string
|
||||
) => {
|
||||
const path = `/api/admin/projects/${projectId}/change-requests/${changeSetId}/comments`;
|
||||
const req = createRequest(path, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ text }),
|
||||
});
|
||||
|
||||
try {
|
||||
return await makeRequest(req.caller, req.id);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
addChangeRequest,
|
||||
changeState,
|
||||
discardChangeRequestEvent,
|
||||
updateChangeRequestEnvironmentConfig,
|
||||
discardDraft,
|
||||
addComment,
|
||||
errors,
|
||||
loading,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user