[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"
isLabelHidden={false}
isLoading={createCommentMutation.isLoading}
label="Comment"
label="Submit"
size="sm"
variant="primary"
onClick={() => handleComment(currentReply)}

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

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

@ -50,7 +50,7 @@ export default function ResumeCommentListItem({
<div className="flex w-full flex-col space-y-1">
{/* Name and creation time */}
<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'}
</p>
{isCommentOwner && (
@ -83,64 +83,59 @@ export default function ResumeCommentListItem({
)}
{/* 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} />
{/* Action buttons; only present for authenticated user when not editing/replying */}
{userId && !isEditingComment && !isReplyingComment && (
<>
{isCommentOwner && (
<>
<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={() => setIsEditingComment(true)}>
Edit
</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>
</>
)}
</>
{userId && !comment.parentId && (
<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={() => {
setIsReplyingComment(!isReplyingComment);
setIsEditingComment(false);
}}>
Reply
</button>
)}
{comment.children.length > 0 && (
<>
<span className="font-medium text-slate-500">&middot;</span>{' '}
<button
className="flex items-center space-x-1 rounded-md text-xs font-medium text-slate-500 hover:text-slate-600"
type="button"
onClick={() => setShowReplies(!showReplies)}>
<span>
{showReplies
? `Hide ${
comment.children.length === 1 ? 'reply' : 'replies'
}`
: `Show ${comment.children.length} ${
comment.children.length === 1 ? 'reply' : 'replies'
}`}
</span>
</button>
</>
<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={() => setShowReplies(!showReplies)}>
<span>
{showReplies
? `Hide ${
comment.children.length === 1 ? 'reply' : 'replies'
}`
: `Show ${comment.children.length} ${
comment.children.length === 1 ? 'reply' : 'replies'
}`}
</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>
{/* Reply Form */}
{isReplyingComment && (
<ResumeCommentReplyForm
parentId={comment.id}
resumeId={comment.resumeId}
section={comment.section}
setIsReplyingComment={setIsReplyingComment}
/>
<div className="mt-2">
<ResumeCommentReplyForm
parentId={comment.id}
resumeId={comment.resumeId}
section={comment.section}
setIsReplyingComment={setIsReplyingComment}
/>
</div>
)}
{/* Replies */}

@ -67,39 +67,35 @@ export default function ResumeCommentEditForm({
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex-column mt-1 space-y-2">
<TextArea
{...(register('description', {
required: 'Comments cannot be empty!',
}),
{})}
defaultValue={comment.description}
<form className="space-y-2" onSubmit={handleSubmit(onSubmit)}>
<TextArea
{...(register('description', {
required: 'Comments cannot be empty!',
}),
{})}
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}
errorMessage={errors.description?.message}
label=""
placeholder="Leave your comment here"
onChange={setFormValue}
label="Cancel"
size="sm"
variant="tertiary"
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>
</form>
);

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

@ -2,40 +2,6 @@ import { getMonth, getYear } from 'date-fns';
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) {
const date = new Date(value);
const month = date.toLocaleString('default', { month: 'short' });

Loading…
Cancel
Save