Skip to content

Commit

Permalink
Comment deletion functionality (#154)
Browse files Browse the repository at this point in the history
* introduce Dialog component

* comment deletion functionality

* remove autoFocus
  • Loading branch information
rtrembecky authored Nov 10, 2023
1 parent 5dcb80b commit d67490d
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 54 deletions.
5 changes: 2 additions & 3 deletions src/components/Clickable/Clickable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import {ButtonHTMLAttributes, ComponentProps, FC, ReactNode} from 'react'

import styles from './Clickable.module.scss'

interface ButtonProps {
type ButtonProps = {
onClick?: () => void
disabled?: boolean
children: ReactNode
type?: ButtonHTMLAttributes<HTMLButtonElement>['type']
}
} & Pick<ButtonHTMLAttributes<HTMLButtonElement>, 'type'>

export const Button: FC<ButtonProps> = ({children, onClick, disabled, type}) => {
return (
Expand Down
30 changes: 30 additions & 0 deletions src/components/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {Dialog as MuiDialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from '@mui/material'
import {FC, ReactNode} from 'react'

type DialogProps = {
open: boolean
close: () => void
title?: ReactNode
contentText?: ReactNode
actions?: ReactNode
}

// inspired by: https://mui.com/material-ui/react-dialog/#alerts
export const Dialog: FC<DialogProps> = ({open, close, title, contentText, actions}) => {
return (
<MuiDialog
open={open}
onClose={close}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
{title && <DialogTitle id="alert-dialog-title">{title}</DialogTitle>}
{contentText && (
<DialogContent>
<DialogContentText id="alert-dialog-description">{contentText}</DialogContentText>
</DialogContent>
)}
{actions && <DialogActions>{actions}</DialogActions>}
</MuiDialog>
)
}
129 changes: 78 additions & 51 deletions src/components/Problems/Discussion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import clsx from 'clsx'
import {FC, useState} from 'react'

import {Comment, CommentState} from '@/types/api/competition'
import {Profile} from '@/types/api/personal'
import {AuthContainer} from '@/utils/AuthContainer'
import {useHasPermissions} from '@/utils/useHasPermissions'

import {Button} from '../Clickable/Clickable'
import {Dialog} from '../Dialog/Dialog'
import {Loading} from '../Loading/Loading'
import styles from './Discussion.module.scss'
import {SideContainer} from './SideContainer'
Expand All @@ -23,25 +25,26 @@ export const Discussion: FC<DiscussionProps> = ({problemId, problemNumber, close
const [commentText, setCommentText] = useState('')
const [hiddenResponseText, setHiddenResponseText] = useState('')
const [hiddenResponseDialogId, sethiddenResponseDialogId] = useState(-1)
const [deleteDialogId, setDeleteDialogId] = useState<number | undefined>()

const queryKey = ['competition', 'problem', problemId, 'comments']
const {data: commentsData, isLoading: commentsIsLoading} = useQuery({
queryKey,
queryFn: () => axios.get<Comment[]>(`/api/competition/problem/${problemId}/comments`),
})
const comments = commentsData?.data.map((comment) => ({
id: comment.id,
can_edit: comment.edit_allowed,
text: comment.text,
state: comment.state,
hidden_response: comment.hidden_response,
posted_by: comment.posted_by_name,
}))
const comments = commentsData?.data

const {hasPermissions} = useHasPermissions()

const {isAuthed} = AuthContainer.useContainer()

const {data} = useQuery({
queryKey: ['personal', 'profiles', 'myprofile'],
queryFn: () => axios.get<Profile>(`/api/personal/profiles/myprofile`),
enabled: isAuthed,
})
const userId = data?.data.id

const queryClient = useQueryClient()

const invalidateCommentsAndCount = async () => {
Expand Down Expand Up @@ -77,7 +80,7 @@ export const Discussion: FC<DiscussionProps> = ({problemId, problemNumber, close
},
})

const {mutate: deleteComment} = useMutation({
const {mutate: confirmDeleteComment} = useMutation({
mutationFn: (id: number) => axios.delete(`/api/competition/comment/${id}`),
onSuccess: () => {
invalidateCommentsAndCount()
Expand All @@ -91,57 +94,81 @@ export const Discussion: FC<DiscussionProps> = ({problemId, problemNumber, close
setHiddenResponseText(e.currentTarget.value)
}

const close = () => setDeleteDialogId(undefined)
const agree = () => {
deleteDialogId !== undefined ? confirmDeleteComment(deleteDialogId) : undefined
close()
}

return (
<SideContainer title={'Diskusia - úloha ' + problemNumber} onClose={closeDiscussion}>
{/* delete comment dialog */}
<Dialog
open={deleteDialogId !== undefined}
close={close}
title="Vymazať komentár?"
contentText="Komentár bude nenávratne vymazaný."
actions={
<>
<Button onClick={close}>Zavrieť</Button>
<Button onClick={agree}>Potvrdiť</Button>
</>
}
/>
<div className={styles.container}>
<div className={styles.comments}>
{commentsIsLoading && <Loading />}
{comments &&
comments.map((comment) => (
<div
className={clsx(styles.comment, comment.state !== CommentState.Published && styles.notPublished)}
key={comment.id}
>
<div className={styles.title}>
<div>{comment.posted_by}</div>
</div>
<div>{comment.text}</div>
{comment.hidden_response && (
<>
<div className={styles.title}>
<div>Vedúci:</div>
comments.map((comment) => {
const isPostedByMe = userId === comment.posted_by

return (
<div
className={clsx(styles.comment, comment.state !== CommentState.Published && styles.notPublished)}
key={comment.id}
>
<div className={styles.title}>
<div>{comment.posted_by_name}</div>
</div>
<div>{comment.text}</div>
{comment.hidden_response && (
<>
<div className={styles.title}>
<div>Vedúci:</div>
</div>
<div>{comment.hidden_response}</div>
</>
)}
{comment.state === CommentState.WaitingForReview && <div>* komentár čaká na schválenie</div>}
{comment.state === CommentState.Hidden && <div>* tento komentár nie je verejný</div>}
{hiddenResponseDialogId === comment.id ? (
<div className={styles.inputContainer}>
<textarea
className={styles.textArea}
value={hiddenResponseText}
onChange={handleHiddenResponseChange}
/>
<div className={styles.commentActions}>
<Button onClick={() => hideComment({id: comment.id, hiddenResponseText})}>Odoslať</Button>
</div>
</div>
<div>{comment.hidden_response}</div>
</>
)}
{comment.state === CommentState.WaitingForReview && <div>* komentár čaká na schválenie</div>}
{comment.state === CommentState.Hidden && <div>* tento komentár nie je verejný</div>}
{hiddenResponseDialogId === comment.id ? (
<div className={styles.inputContainer}>
<textarea
className={styles.textArea}
value={hiddenResponseText}
onChange={handleHiddenResponseChange}
/>
) : (
<div className={styles.commentActions}>
<Button onClick={() => hideComment({id: comment.id, hiddenResponseText})}>Odoslať</Button>
{comment.state !== CommentState.Published && hasPermissions && (
<Button onClick={() => publishComment(comment.id)}>Zverejniť</Button>
)}
{comment.state !== CommentState.Hidden && hasPermissions && (
<Button onClick={() => sethiddenResponseDialogId(comment.id)}>Skryť</Button>
)}
{/* veduci moze zmazat svoj komentar v hocijakom stave, ucastnik moze zmazat svoj nepublishnuty komentar */}
{isPostedByMe && (hasPermissions || comment.state !== CommentState.Published) && (
<Button onClick={() => setDeleteDialogId(comment.id)}>Vymazať</Button>
)}
</div>
</div>
) : (
<div className={styles.commentActions}>
{comment.state !== CommentState.Published && hasPermissions && (
<Button onClick={() => publishComment(comment.id)}>Zverejniť</Button>
)}
{comment.state !== CommentState.Hidden && hasPermissions && (
<Button onClick={() => sethiddenResponseDialogId(comment.id)}>Skryť</Button>
)}
{comment.state === CommentState.WaitingForReview && !hasPermissions && (
<Button onClick={() => deleteComment(comment.id)}>Vymazať</Button>
)}
</div>
)}
</div>
))}
)}
</div>
)
})}
</div>
<div className={styles.submitInputContainer}>
<textarea className={styles.textArea} value={commentText} onChange={handleCommentChange} />
Expand Down
1 change: 1 addition & 0 deletions src/types/api/personal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export interface SchoolProfile {
}

export interface Profile {
id: number
first_name: string
last_name: string
nickname: string | null
Expand Down

0 comments on commit d67490d

Please sign in to comment.