[portal][refactor] make comments appearance more consistent across offers and resume reviews

pull/512/head
Yangshun Tay 2 years ago
parent 2979950c19
commit 8e15d49ad0

@ -186,7 +186,7 @@ export default function ProfileComments({
display="block" display="block"
isLabelHidden={false} isLabelHidden={false}
isLoading={createCommentMutation.isLoading} isLoading={createCommentMutation.isLoading}
label="Comment" label="Submit"
size="sm" size="sm"
variant="primary" variant="primary"
onClick={() => handleComment(currentReply)} onClick={() => handleComment(currentReply)}

@ -1,10 +1,10 @@
import { formatDistanceToNow } from 'date-fns';
import { signIn, useSession } from 'next-auth/react'; import { signIn, useSession } from 'next-auth/react';
import { useState } from 'react'; import { useState } from 'react';
import { Button, Dialog, TextArea, useToast } from '@tih/ui'; import { Button, Dialog, TextArea, useToast } from '@tih/ui';
import ProfilePhotoHolder from '~/components/offers/profile/ProfilePhotoHolder'; import ProfilePhotoHolder from '~/components/offers/profile/ProfilePhotoHolder';
import { timeSinceNow } from '~/utils/offers/time';
import { trpc } from '~/utils/trpc'; import { trpc } from '~/utils/trpc';
import type { Reply } from '~/types/offers'; import type { Reply } from '~/types/offers';
@ -135,45 +135,45 @@ export default function CommentCard({
)} )}
</div> </div>
<div className="w-full"> <div className="w-full">
<div className="text-sm"> <div className="flex flex-row items-center space-x-2">
<p className="font-medium text-slate-900"> <p className="text-sm font-medium text-slate-900">
{user?.name ?? 'unknown user'} {user?.name ?? 'Unknown user'}
</p> </p>
<span className="font-medium text-slate-500">&middot;</span>
<div className="text-xs text-slate-500">
{formatDistanceToNow(createdAt, {
addSuffix: true,
})}
</div>
</div> </div>
<div className="mt-1 text-sm text-slate-700"> <div className="mt-1 text-sm text-slate-700">
<p className="break-all">{message}</p> <p className="break-all">{message}</p>
</div> </div>
<div className="mt-2 space-x-2 text-xs"> <div className="-ml-2 mt-1 flex h-6 items-center text-xs">
<span className="font-medium text-slate-500"> {!disableReply && (
{timeSinceNow(createdAt)} ago
</span>{' '}
{replyLength > 0 && (
<>
<span className="font-medium text-slate-500">&middot;</span>{' '}
<button <button
className="font-medium text-slate-900" className="-my-1 rounded-md px-2 py-1 text-xs font-medium text-slate-500 hover:bg-slate-100 hover:text-slate-600"
type="button" type="button"
onClick={handleExpanded}> onClick={() => setIsReplying(!isReplying)}>
{isExpanded ? `Hide replies` : `View replies (${replyLength})`} Reply
</button> </button>
</>
)} )}
{!disableReply && ( {replyLength > 0 && (
<>
<span className="font-medium text-slate-500">&middot;</span>{' '}
<button <button
className="font-medium text-slate-900" className="-my-1 rounded-md px-2 py-1 text-xs font-medium text-slate-500 hover:bg-slate-100 hover:text-slate-600"
type="button" type="button"
onClick={() => setIsReplying(!isReplying)}> onClick={handleExpanded}>
Reply {isExpanded
? `Hide ${replyLength === 1 ? 'reply' : 'replies'}`
: `Show ${replyLength} ${
replyLength === 1 ? 'reply' : 'replies'
}`}
</button> </button>
</>
)} )}
{deletable && ( {deletable && (
<> <>
<span className="font-medium text-slate-500">&middot;</span>{' '}
<button <button
className="font-medium text-slate-900" className="-my-1 rounded-md px-2 py-1 text-xs font-medium text-slate-500 hover:bg-slate-100 hover:text-slate-600"
disabled={deleteCommentMutation.isLoading} disabled={deleteCommentMutation.isLoading}
type="button" type="button"
onClick={() => setIsDialogOpen(true)}> onClick={() => setIsDialogOpen(true)}>
@ -210,8 +210,9 @@ export default function CommentCard({
)} )}
</div> </div>
{!disableReply && isReplying && ( {!disableReply && isReplying && (
<div className="mt-4 mr-2"> <div className="mt-2">
<form <form
className="space-y-2"
onSubmit={(event) => { onSubmit={(event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -226,24 +227,30 @@ export default function CommentCard({
value={currentReply} value={currentReply}
onChange={(value) => setCurrentReply(value)} onChange={(value) => setCurrentReply(value)}
/> />
<div className="mt-2 flex w-full justify-end"> <div className="flex w-full justify-end space-x-2">
<div className="w-fit"> <Button
disabled={createCommentMutation.isLoading}
label="Cancel"
size="sm"
variant="tertiary"
onClick={() => {
setIsReplying(false);
}}
/>
<Button <Button
disabled={ disabled={
!currentReply.length || !currentReply.length ||
createCommentMutation.isLoading || createCommentMutation.isLoading ||
deleteCommentMutation.isLoading deleteCommentMutation.isLoading
} }
display="block"
isLabelHidden={false} isLabelHidden={false}
isLoading={createCommentMutation.isLoading} isLoading={createCommentMutation.isLoading}
label="Reply" label="Submit"
size="sm" size="sm"
variant="primary" variant="primary"
onClick={handleReply} onClick={handleReply}
/> />
</div> </div>
</div>
</form> </form>
</div> </div>
)} )}

@ -27,8 +27,9 @@ export default function ExpandableCommentCard({
token={token} token={token}
/> />
{comment.replies && comment.replies.length > 0 && isExpanded && ( {comment.replies && comment.replies.length > 0 && isExpanded && (
<div className="pt-4"> <div className="pl-[52px] pt-2">
<ul className="space-y-4 pl-14" role="list"> <div className="border-l-2 border-slate-200 pl-2">
<ul className="space-y-2" role="list">
{comment.replies.map((reply) => ( {comment.replies.map((reply) => (
<li key={reply.id}> <li key={reply.id}>
<CommentCard <CommentCard
@ -41,6 +42,7 @@ export default function ExpandableCommentCard({
))} ))}
</ul> </ul>
</div> </div>
</div>
)} )}
</div> </div>
); );

@ -50,7 +50,7 @@ export default function ResumeCommentListItem({
<div className="flex w-full flex-col space-y-1"> <div className="flex w-full flex-col space-y-1">
{/* Name and creation time */} {/* Name and creation time */}
<div className="flex flex-row items-center space-x-2"> <div className="flex flex-row items-center space-x-2">
<p className={clsx('text-sm font-medium text-slate-800')}> <p className="text-sm font-medium text-slate-900">
{comment.user.name ?? 'Reviewer ABC'} {comment.user.name ?? 'Reviewer ABC'}
</p> </p>
{isCommentOwner && ( {isCommentOwner && (
@ -83,40 +83,23 @@ export default function ResumeCommentListItem({
)} )}
{/* Upvote and edit */} {/* Upvote and edit */}
<div className="flex flex-row space-x-2 pt-1 align-middle"> <div className="mt-1 flex h-6 items-center">
<ResumeCommentVoteButtons commentId={comment.id} userId={userId} /> <ResumeCommentVoteButtons commentId={comment.id} userId={userId} />
{/* Action buttons; only present for authenticated user when not editing/replying */} {/* Action buttons; only present for authenticated user when not editing/replying */}
{userId && !isEditingComment && !isReplyingComment && ( {userId && !comment.parentId && (
<>
{isCommentOwner && (
<>
<span className="font-medium text-slate-500">&middot;</span>{' '}
<button <button
className="text-xs font-medium text-slate-500 hover:text-slate-600" className="-my-1 rounded-md px-2 py-1 text-xs font-medium text-slate-500 hover:bg-slate-100 hover:text-slate-600"
type="button" type="button"
onClick={() => setIsEditingComment(true)}> onClick={() => {
Edit setIsReplyingComment(!isReplyingComment);
</button> setIsEditingComment(false);
</> }}>
)}
{!comment.parentId && (
<>
<span className="font-medium text-slate-500">&middot;</span>{' '}
<button
className="text-xs font-medium text-slate-500 hover:text-slate-600"
type="button"
onClick={() => setIsReplyingComment(true)}>
Reply Reply
</button> </button>
</>
)}
</>
)} )}
{comment.children.length > 0 && ( {comment.children.length > 0 && (
<>
<span className="font-medium text-slate-500">&middot;</span>{' '}
<button <button
className="flex items-center space-x-1 rounded-md text-xs font-medium text-slate-500 hover:text-slate-600" className="-my-1 rounded-md px-2 py-1 text-xs font-medium text-slate-500 hover:bg-slate-100 hover:text-slate-600"
type="button" type="button"
onClick={() => setShowReplies(!showReplies)}> onClick={() => setShowReplies(!showReplies)}>
<span> <span>
@ -129,18 +112,30 @@ export default function ResumeCommentListItem({
}`} }`}
</span> </span>
</button> </button>
</> )}
{isCommentOwner && (
<button
className="-my-1 rounded-md px-2 py-1 text-xs font-medium text-slate-500 hover:bg-slate-100 hover:text-slate-600"
type="button"
onClick={() => {
setIsEditingComment(!isEditingComment);
setIsReplyingComment(false);
}}>
Edit
</button>
)} )}
</div> </div>
{/* Reply Form */} {/* Reply Form */}
{isReplyingComment && ( {isReplyingComment && (
<div className="mt-2">
<ResumeCommentReplyForm <ResumeCommentReplyForm
parentId={comment.id} parentId={comment.id}
resumeId={comment.resumeId} resumeId={comment.resumeId}
section={comment.section} section={comment.section}
setIsReplyingComment={setIsReplyingComment} setIsReplyingComment={setIsReplyingComment}
/> />
</div>
)} )}
{/* Replies */} {/* Replies */}

@ -67,8 +67,7 @@ export default function ResumeCommentEditForm({
}; };
return ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form className="space-y-2" onSubmit={handleSubmit(onSubmit)}>
<div className="flex-column mt-1 space-y-2">
<TextArea <TextArea
{...(register('description', { {...(register('description', {
required: 'Comments cannot be empty!', required: 'Comments cannot be empty!',
@ -81,8 +80,7 @@ export default function ResumeCommentEditForm({
placeholder="Leave your comment here" placeholder="Leave your comment here"
onChange={setFormValue} onChange={setFormValue}
/> />
<div className="flex w-full justify-end space-x-2">
<div className="flex-row space-x-2">
<Button <Button
disabled={commentUpdateMutation.isLoading} disabled={commentUpdateMutation.isLoading}
label="Cancel" label="Cancel"
@ -90,17 +88,15 @@ export default function ResumeCommentEditForm({
variant="tertiary" variant="tertiary"
onClick={onCancel} onClick={onCancel}
/> />
<Button <Button
disabled={!isDirty || commentUpdateMutation.isLoading} disabled={!isDirty || commentUpdateMutation.isLoading}
isLoading={commentUpdateMutation.isLoading} isLoading={commentUpdateMutation.isLoading}
label="Confirm" label="Submit"
size="sm" size="sm"
type="submit" type="submit"
variant="primary" variant="primary"
/> />
</div> </div>
</div>
</form> </form>
); );
} }

@ -83,15 +83,16 @@ export default function ResumeCommentReplyForm({
required: 'Reply cannot be empty!', required: 'Reply cannot be empty!',
}), }),
{})} {})}
autoFocus={true}
defaultValue="" defaultValue=""
disabled={commentReplyMutation.isLoading} disabled={commentReplyMutation.isLoading}
errorMessage={errors.description?.message} errorMessage={errors.description?.message}
label="" isLabelHidden={true}
placeholder="Leave your reply here" label="Reply to comment"
placeholder="Type your reply here"
onChange={setFormValue} onChange={setFormValue}
/> />
<div className="flex w-full justify-end space-x-2">
<div className="flex-row space-x-2">
<Button <Button
disabled={commentReplyMutation.isLoading} disabled={commentReplyMutation.isLoading}
label="Cancel" label="Cancel"
@ -99,11 +100,10 @@ export default function ResumeCommentReplyForm({
variant="tertiary" variant="tertiary"
onClick={onCancel} onClick={onCancel}
/> />
<Button <Button
disabled={!isDirty || commentReplyMutation.isLoading} disabled={!isDirty || commentReplyMutation.isLoading}
isLoading={commentReplyMutation.isLoading} isLoading={commentReplyMutation.isLoading}
label="Confirm" label="Submit"
size="sm" size="sm"
type="submit" type="submit"
variant="primary" variant="primary"

@ -2,40 +2,6 @@ import { getMonth, getYear } from 'date-fns';
import type { MonthYear } from '~/components/shared/MonthYearPicker'; import type { MonthYear } from '~/components/shared/MonthYearPicker';
export function timeSinceNow(date: Date | number | string) {
const seconds = Math.floor(
new Date().getTime() / 1000 - new Date(date).getTime() / 1000,
);
let interval = seconds / 31536000;
if (interval > 1) {
const time: number = Math.floor(interval);
return time === 1 ? `${time} year` : `${time} years`;
}
interval = seconds / 2592000;
if (interval > 1) {
const time: number = Math.floor(interval);
return time === 1 ? `${time} month` : `${time} months`;
}
interval = seconds / 86400;
if (interval > 1) {
const time: number = Math.floor(interval);
return time === 1 ? `${time} day` : `${time} days`;
}
interval = seconds / 3600;
if (interval > 1) {
const time: number = Math.floor(interval);
return time === 1 ? `${time} hour` : `${time} hours`;
}
interval = seconds / 60;
if (interval > 1) {
const time: number = Math.floor(interval);
return time === 1 ? `${time} minute` : `${time} minutes`;
}
const time: number = Math.floor(interval);
return time === 1 ? `${time} second` : `${time} seconds`;
}
export function formatDate(value: Date | number | string) { export function formatDate(value: Date | number | string) {
const date = new Date(value); const date = new Date(value);
const month = date.toLocaleString('default', { month: 'short' }); const month = date.toLocaleString('default', { month: 'short' });

Loading…
Cancel
Save