[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">
{timeSinceNow(createdAt)} ago
</span>{' '}
{replyLength > 0 && (
<>
<span className="font-medium text-slate-500">&middot;</span>{' '}
<button
className="font-medium text-slate-900"
type="button"
onClick={handleExpanded}>
{isExpanded ? `Hide replies` : `View replies (${replyLength})`}
</button>
</>
)}
{!disableReply && ( {!disableReply && (
<> <button
<span className="font-medium text-slate-500">&middot;</span>{' '} className="-my-1 rounded-md px-2 py-1 text-xs font-medium text-slate-500 hover:bg-slate-100 hover:text-slate-600"
<button type="button"
className="font-medium text-slate-900" onClick={() => setIsReplying(!isReplying)}>
type="button" Reply
onClick={() => setIsReplying(!isReplying)}> </button>
Reply )}
</button> {replyLength > 0 && (
</> <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={handleExpanded}>
{isExpanded
? `Hide ${replyLength === 1 ? 'reply' : 'replies'}`
: `Show ${replyLength} ${
replyLength === 1 ? 'reply' : 'replies'
}`}
</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,23 +227,29 @@ 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
<Button disabled={createCommentMutation.isLoading}
disabled={ label="Cancel"
!currentReply.length || size="sm"
createCommentMutation.isLoading || variant="tertiary"
deleteCommentMutation.isLoading onClick={() => {
} setIsReplying(false);
display="block" }}
isLabelHidden={false} />
isLoading={createCommentMutation.isLoading} <Button
label="Reply" disabled={
size="sm" !currentReply.length ||
variant="primary" createCommentMutation.isLoading ||
onClick={handleReply} deleteCommentMutation.isLoading
/> }
</div> isLabelHidden={false}
isLoading={createCommentMutation.isLoading}
label="Submit"
size="sm"
variant="primary"
onClick={handleReply}
/>
</div> </div>
</form> </form>
</div> </div>

@ -27,19 +27,21 @@ 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">
{comment.replies.map((reply) => ( <ul className="space-y-2" role="list">
<li key={reply.id}> {comment.replies.map((reply) => (
<CommentCard <li key={reply.id}>
comment={reply} <CommentCard
disableReply={true} comment={reply}
profileId={profileId} disableReply={true}
token={token} profileId={profileId}
/> token={token}
</li> />
))} </li>
</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,64 +83,59 @@ 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 && (
<> <button
{isCommentOwner && ( 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"
<span className="font-medium text-slate-500">&middot;</span>{' '} onClick={() => {
<button setIsReplyingComment(!isReplyingComment);
className="text-xs font-medium text-slate-500 hover:text-slate-600" setIsEditingComment(false);
type="button" }}>
onClick={() => setIsEditingComment(true)}> Reply
Edit </button>
</button>
</>
)}
{!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
</button>
</>
)}
</>
)} )}
{comment.children.length > 0 && ( {comment.children.length > 0 && (
<> <button
<span className="font-medium text-slate-500">&middot;</span>{' '} className="-my-1 rounded-md px-2 py-1 text-xs font-medium text-slate-500 hover:bg-slate-100 hover:text-slate-600"
<button type="button"
className="flex items-center space-x-1 rounded-md text-xs font-medium text-slate-500 hover:text-slate-600" onClick={() => setShowReplies(!showReplies)}>
type="button" <span>
onClick={() => setShowReplies(!showReplies)}> {showReplies
<span> ? `Hide ${
{showReplies comment.children.length === 1 ? 'reply' : 'replies'
? `Hide ${ }`
comment.children.length === 1 ? 'reply' : 'replies' : `Show ${comment.children.length} ${
}` comment.children.length === 1 ? 'reply' : 'replies'
: `Show ${comment.children.length} ${ }`}
comment.children.length === 1 ? 'reply' : 'replies' </span>
}`} </button>
</span> )}
</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 && (
<ResumeCommentReplyForm <div className="mt-2">
parentId={comment.id} <ResumeCommentReplyForm
resumeId={comment.resumeId} parentId={comment.id}
section={comment.section} resumeId={comment.resumeId}
setIsReplyingComment={setIsReplyingComment} section={comment.section}
/> setIsReplyingComment={setIsReplyingComment}
/>
</div>
)} )}
{/* Replies */} {/* Replies */}

@ -67,39 +67,35 @@ 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!', }),
}), {})}
{})} defaultValue={comment.description}
defaultValue={comment.description} disabled={commentUpdateMutation.isLoading}
errorMessage={errors.description?.message}
label=""
placeholder="Leave your comment here"
onChange={setFormValue}
/>
<div className="flex w-full justify-end space-x-2">
<Button
disabled={commentUpdateMutation.isLoading} disabled={commentUpdateMutation.isLoading}
errorMessage={errors.description?.message} label="Cancel"
label="" size="sm"
placeholder="Leave your comment here" variant="tertiary"
onChange={setFormValue} onClick={onCancel}
/>
<Button
disabled={!isDirty || commentUpdateMutation.isLoading}
isLoading={commentUpdateMutation.isLoading}
label="Submit"
size="sm"
type="submit"
variant="primary"
/> />
<div className="flex-row space-x-2">
<Button
disabled={commentUpdateMutation.isLoading}
label="Cancel"
size="sm"
variant="tertiary"
onClick={onCancel}
/>
<Button
disabled={!isDirty || commentUpdateMutation.isLoading}
isLoading={commentUpdateMutation.isLoading}
label="Confirm"
size="sm"
type="submit"
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