Merge branch 'main' into hongpo/update-question-filter

pull/409/head
hpkoh 3 years ago
commit 59d9218a27

@ -85,8 +85,8 @@ function ProfileJewel() {
{({ active }) => (
<Link
className={clsx(
active ? 'bg-gray-100' : '',
'block px-4 py-2 text-sm text-gray-700',
active ? 'bg-slate-100' : '',
'block px-4 py-2 text-sm text-slate-700',
)}
href={item.href}
onClick={item.onClick}>
@ -178,9 +178,9 @@ export default function AppShell({ children }: Props) {
{/* Content area */}
<div className="flex h-screen flex-1 flex-col overflow-hidden">
<header className="w-full">
<div className="relative z-10 flex h-16 flex-shrink-0 border-b border-gray-200 bg-white shadow-sm">
<div className="relative z-10 flex h-16 flex-shrink-0 border-b border-slate-200 bg-white shadow-sm">
<button
className="focus:ring-primary-500 border-r border-gray-200 px-4 text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset md:hidden"
className="focus:ring-primary-500 border-r border-slate-200 px-4 text-slate-500 focus:outline-none focus:ring-2 focus:ring-inset md:hidden"
type="button"
onClick={() => setMobileMenuOpen(true)}>
<span className="sr-only">Open sidebar</span>

@ -34,7 +34,7 @@ export default function MobileNavigation({
leave="transition-opacity ease-linear duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0">
<div className="fixed inset-0 bg-gray-600 bg-opacity-75" />
<div className="fixed inset-0 bg-slate-600 bg-opacity-75" />
</Transition.Child>
<div className="fixed inset-0 z-40 flex">
<Transition.Child

@ -9,12 +9,12 @@ export function Breadcrumbs({ stepLabels, currentStep }: BreadcrumbsProps) {
{stepLabels.map((label, index) => (
<div key={label} className="flex space-x-1">
{index === currentStep ? (
<p className="text-sm text-purple-700">{label}</p>
<p className="text-primary-700 text-sm">{label}</p>
) : (
<p className="text-sm text-gray-400">{label}</p>
<p className="text-sm text-slate-400">{label}</p>
)}
{index !== stepLabels.length - 1 && (
<p className="text-sm text-gray-400">{'>'}</p>
<p className="text-sm text-slate-400">{'>'}</p>
)}
</div>
))}

@ -2,11 +2,11 @@ export default function OffersTitle() {
return (
<>
<div className="flex items-end justify-center">
<h1 className="mt-16 text-center text-4xl font-bold text-indigo-600">
<h1 className="text-primary-600 mt-16 text-center text-4xl font-bold">
Tech Handbook Offers Repo
</h1>
</div>
<div className="mt-2 text-center text-2xl font-normal text-indigo-500">
<div className="text-primary-500 mt-2 text-center text-2xl font-normal">
Reveal profile stories behind offers
</div>
<div className="items-top flex justify-center text-xl font-normal">

@ -1,9 +1,9 @@
import { useRouter } from 'next/router';
import { useState } from 'react';
import { setTimeout } from 'timers';
import { CheckIcon, DocumentDuplicateIcon } from '@heroicons/react/20/solid';
import { BookmarkSquareIcon, EyeIcon } from '@heroicons/react/24/outline';
import { Button, TextInput } from '@tih/ui';
// Import { useState } from 'react';
// import { setTimeout } from 'timers';
import { DocumentDuplicateIcon } from '@heroicons/react/20/solid';
import { EyeIcon } from '@heroicons/react/24/outline';
import { Button, TextInput, useToast } from '@tih/ui';
import {
copyProfileLink,
@ -20,31 +20,31 @@ export default function OffersProfileSave({
profileId,
token,
}: OfferProfileSaveProps) {
const [linkCopied, setLinkCopied] = useState(false);
const [isSaving, setSaving] = useState(false);
const [isSaved, setSaved] = useState(false);
const { showToast } = useToast();
// Const [isSaving, setSaving] = useState(false);
// const [isSaved, setSaved] = useState(false);
const router = useRouter();
const saveProfile = () => {
setSaving(true);
setTimeout(() => {
setSaving(false);
setSaved(true);
}, 5);
};
// Const saveProfile = () => {
// setSaving(true);
// setTimeout(() => {
// setSaving(false);
// setSaved(true);
// }, 5);
// };
return (
<div className="flex w-full justify-center">
<div className="max-w-2xl text-center">
<h5 className="mb-6 text-4xl font-bold text-gray-900">
<h5 className="mb-6 text-4xl font-bold text-slate-900">
Save for future edits
</h5>
<p className="mb-2 text-gray-900">We value your privacy.</p>
<p className="mb-5 text-gray-900">
<p className="mb-2 text-slate-900">We value your privacy.</p>
<p className="mb-5 text-slate-900">
To keep you offer profile strictly anonymous, only people who have the
link below can edit it.
</p>
<div className="mb-5 grid grid-cols-12 gap-4">
<div className="mb-20 grid grid-cols-12 gap-4">
<div className="col-span-11">
<TextInput
disabled={true}
@ -59,17 +59,15 @@ export default function OffersProfileSave({
label="Copy"
variant="primary"
onClick={() => {
copyProfileLink(profileId, token), setLinkCopied(true);
copyProfileLink(profileId, token);
showToast({
title: `Profile edit link copied to clipboard!`,
variant: 'success',
});
}}
/>
</div>
<div className="mb-20">
{linkCopied && (
<p className="text-purple-700">Link copied to clipboard!</p>
)}
</div>
<p className="mb-5 text-gray-900">
{/* <p className="mb-5 text-slate-900">
If you do not want to keep the edit link, you can opt to save this
profile under your user account. It will still only be editable by
you.
@ -83,7 +81,7 @@ export default function OffersProfileSave({
variant="primary"
onClick={saveProfile}
/>
</div>
</div> */}
<div>
<Button
icon={EyeIcon}

@ -26,11 +26,11 @@ function YoeSection() {
const backgroundFields = formState.errors.background;
return (
<>
<h6 className="mb-2 text-left text-xl font-medium text-gray-400">
<h6 className="mb-2 text-left text-xl font-medium text-slate-400">
Years of Experience (YOE)
</h6>
<div className="mb-5 rounded-lg border border-gray-200 px-10 py-5">
<div className="mb-5 rounded-lg border border-slate-200 px-10 py-5">
<div className="mb-2 grid grid-cols-3 space-x-3">
<FormTextInput
errorMessage={backgroundFields?.totalYoe?.message}
@ -245,10 +245,10 @@ function CurrentJobSection() {
return (
<>
<h6 className="mb-2 text-left text-xl font-medium text-gray-400">
<h6 className="mb-2 text-left text-xl font-medium text-slate-400">
Current / Previous Job
</h6>
<div className="mb-5 rounded-lg border border-gray-200 px-10 py-5">
<div className="mb-5 rounded-lg border border-slate-200 px-10 py-5">
<div className="mb-5">
<FormRadioList
defaultValue={JobType.FULLTIME}
@ -282,10 +282,10 @@ function EducationSection() {
const { register } = useFormContext();
return (
<>
<h6 className="mb-2 text-left text-xl font-medium text-gray-400">
<h6 className="mb-2 text-left text-xl font-medium text-slate-400">
Education
</h6>
<div className="mb-5 rounded-lg border border-gray-200 px-10 py-5">
<div className="mb-5 rounded-lg border border-slate-200 px-10 py-5">
<div className="mb-5 grid grid-cols-2 space-x-3">
<FormSelect
display="block"
@ -319,10 +319,10 @@ function EducationSection() {
export default function BackgroundForm() {
return (
<div>
<h5 className="mb-2 text-center text-4xl font-bold text-gray-900">
<h5 className="mb-2 text-center text-4xl font-bold text-slate-900">
Help us better gauge your offers
</h5>
<h6 className="text-md mx-10 mb-8 text-center font-light text-gray-600">
<h6 className="text-md mx-10 mb-8 text-center font-light text-slate-600">
This section is mostly optional, but your background information helps
us benchmark your offers.
</h6>

@ -62,7 +62,7 @@ function FullTimeOfferDetailsForm({
}, [watchCurrency, index, setValue]);
return (
<div className="my-5 rounded-lg border border-gray-200 px-10 py-5">
<div className="my-5 rounded-lg border border-slate-200 px-10 py-5">
<div className="mb-5 grid grid-cols-2 space-x-3">
<FormSelect
display="block"
@ -289,7 +289,7 @@ function InternshipOfferDetailsForm({
const offerFields = formState.errors.offers?.[index];
return (
<div className="my-5 rounded-lg border border-gray-200 px-10 py-5">
<div className="my-5 rounded-lg border border-slate-200 px-10 py-5">
<div className="mb-5 grid grid-cols-2 space-x-3">
<FormSelect
display="block"
@ -503,7 +503,7 @@ export default function OfferDetailsForm({
return (
<div className="mb-5">
<h5 className="mb-8 text-center text-4xl font-bold text-gray-900">
<h5 className="mb-8 text-center text-4xl font-bold text-slate-900">
Fill in your offer details
</h5>
<div className="flex w-full justify-center">

@ -30,7 +30,7 @@ export default function EducationCard({
)}
</div>
{(startDate || endDate) && (
<div className="font-light text-gray-400">
<div className="font-light text-slate-400">
<p>{`${startDate || 'N/A'} - ${endDate || 'N/A'}`}</p>
</div>
)}

@ -44,12 +44,12 @@ export default function OfferCard({
</div>
</div>
{!duration && receivedMonth && (
<div className="font-light text-gray-400">
<div className="font-light text-slate-400">
<p>{receivedMonth}</p>
</div>
)}
{duration && (
<div className="font-light text-gray-400">
<div className="font-light text-slate-400">
<p>{`${duration} months`}</p>
</div>
)}
@ -83,7 +83,7 @@ export default function OfferCard({
</div>
))}
{totalCompensation && (
<div className="ml-6 flex flex-row font-light text-gray-400">
<div className="ml-6 flex flex-row font-light text-slate-400">
<p>
Base / year: {base} Stocks / year: {stocks} Bonus / year:{' '}
{bonus}

@ -1,7 +1,13 @@
import { signIn, useSession } from 'next-auth/react';
import { useState } from 'react';
import { ClipboardDocumentIcon, ShareIcon } from '@heroicons/react/24/outline';
import { Button, HorizontalDivider, Spinner, TextArea } from '@tih/ui';
import {
Button,
HorizontalDivider,
Spinner,
TextArea,
useToast,
} from '@tih/ui';
import ExpandableCommentCard from '~/components/offers/profile/comments/ExpandableCommentCard';
@ -30,6 +36,7 @@ export default function ProfileComments({
const { data: session, status } = useSession();
const [currentReply, setCurrentReply] = useState<string>('');
const [replies, setReplies] = useState<Array<Reply>>();
const { showToast } = useToast();
const commentsQuery = trpc.useQuery(
['offers.comments.getComments', { profileId }],
@ -51,6 +58,10 @@ export default function ProfileComments({
});
function handleComment(message: string) {
if (!currentReply.length) {
return;
}
if (isEditable) {
// If it is with edit permission, send comment to API with username = null
createCommentMutation.mutate(
@ -104,7 +115,13 @@ export default function ProfileComments({
label="Copy profile edit link"
size="sm"
variant="secondary"
onClick={() => copyProfileLink(profileId, token)}
onClick={() => {
copyProfileLink(profileId, token);
showToast({
title: `Profile edit link copied to clipboard!`,
variant: 'success',
});
}}
/>
)}
<Button
@ -115,7 +132,13 @@ export default function ProfileComments({
label="Copy public link"
size="sm"
variant="secondary"
onClick={() => copyProfileLink(profileId)}
onClick={() => {
copyProfileLink(profileId);
showToast({
title: `Public profile link copied to clipboard!`,
variant: 'success',
});
}}
/>
</div>
<h2 className="mt-2 mb-6 text-2xl font-bold">Discussions</h2>
@ -131,7 +154,7 @@ export default function ProfileComments({
<div className="mt-2 flex w-full justify-end">
<div className="w-fit">
<Button
disabled={commentsQuery.isLoading}
disabled={commentsQuery.isLoading || !currentReply.length}
display="block"
isLabelHidden={false}
isLoading={createCommentMutation.isLoading}

@ -8,9 +8,9 @@ export default function ProfilePhotoHolder({
const sizeMap = { lg: '16', sm: '12' };
return (
<span
className={`inline-block h-${sizeMap[size]} w-${sizeMap[size]} overflow-hidden rounded-full bg-gray-100`}>
className={`inline-block h-${sizeMap[size]} w-${sizeMap[size]} overflow-hidden rounded-full bg-slate-100`}>
<svg
className="h-full w-full text-gray-300"
className="h-full w-full text-slate-300"
fill="currentColor"
viewBox="0 0 24 24">
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />

@ -43,6 +43,10 @@ export default function CommentCard({
});
function handleReply() {
if (!currentReply.length) {
return;
}
if (token && token.length > 0) {
// If it is with edit permission, send comment to API with username = null
createCommentMutation.mutate(
@ -96,12 +100,12 @@ export default function CommentCard({
</div>
<div className="mt-2 mb-2 flex flex-row ">{message}</div>
<div className="flex flex-row items-center justify-start space-x-4 ">
<div className="flex flex-col text-sm font-light text-gray-400">{`${timeSinceNow(
<div className="flex flex-col text-sm font-light text-slate-400">{`${timeSinceNow(
createdAt,
)} ago`}</div>
{replyLength > 0 && (
<div
className="flex cursor-pointer flex-col text-sm text-purple-600 hover:underline"
className="text-primary-600 flex cursor-pointer flex-col text-sm hover:underline"
onClick={handleExpanded}>
{isExpanded ? `Hide replies` : `View replies (${replyLength})`}
</div>
@ -132,6 +136,7 @@ export default function CommentCard({
<div className="mt-2 flex w-full justify-end">
<div className="w-fit">
<Button
disabled={!currentReply.length}
display="block"
isLabelHidden={false}
isLoading={createCommentMutation.isLoading}

@ -13,9 +13,9 @@ export default function OfferTableRow({
return (
<tr
key={id}
className="border-b bg-white hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-600">
className="border-b bg-white hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-800 dark:hover:bg-slate-600">
<th
className="whitespace-nowrap py-4 px-6 font-medium text-gray-900 dark:text-white"
className="whitespace-nowrap py-4 px-6 font-medium text-slate-900 dark:text-white"
scope="row">
{company.name}
</th>
@ -25,7 +25,7 @@ export default function OfferTableRow({
<td className="py-4 px-6">{formatDate(monthYearReceived)}</td>
<td className="space-x-4 py-4 px-6">
<Link
className="font-medium text-indigo-600 hover:underline dark:text-indigo-500"
className="text-primary-600 dark:text-primary-500 font-medium hover:underline"
href={`/offers/profile/${profileId}`}>
View Profile
</Link>

@ -109,7 +109,7 @@ export default function OffersTable({
function renderHeader() {
return (
<thead className="bg-gray-50 text-xs uppercase text-gray-700 dark:bg-gray-700 dark:text-gray-400">
<thead className="bg-slate-50 text-xs uppercase text-slate-700">
<tr>
{[
'Company',
@ -145,7 +145,7 @@ export default function OffersTable({
<Spinner display="block" size="lg" />
</div>
) : (
<table className="w-full text-left text-sm text-gray-500 dark:text-gray-400">
<table className="w-full text-left text-sm text-slate-500">
{renderHeader()}
<tbody>
{offers.map((offer) => (

@ -19,13 +19,13 @@ export default function OffersTablePagination({
<nav
aria-label="Table navigation"
className="flex items-center justify-between p-4">
<span className="text-sm font-normal text-gray-500 dark:text-gray-400">
<span className="text-sm font-normal text-slate-500">
Showing
<span className="font-semibold text-gray-900 dark:text-white">
<span className="font-semibold text-slate-900">
{` ${startNumber} - ${endNumber} `}
</span>
{`of `}
<span className="font-semibold text-gray-900 dark:text-white">
<span className="font-semibold text-slate-900">
{pagination.totalItems}
</span>
</span>

@ -30,7 +30,7 @@ export default function ContributeQuestionCard({
return (
<div>
<button
className="flex flex-col items-stretch justify-center gap-2 rounded-md border border-slate-300 bg-white p-4 text-left hover:bg-gray-100"
className="flex flex-col items-stretch justify-center gap-2 rounded-md border border-slate-300 bg-white p-4 text-left hover:bg-slate-100"
type="button"
onClick={handleOpenContribute}>
<TextInput

@ -48,7 +48,7 @@ export default function ContributeQuestionDialog({
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0">
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
<div className="fixed inset-0 bg-slate-500 bg-opacity-75 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
@ -66,7 +66,7 @@ export default function ContributeQuestionDialog({
<div className="mt-3 w-full sm:mt-0 sm:text-left">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-900">
className="text-lg font-medium leading-6 text-slate-900">
Contribute question
</Dialog.Title>
<div className="w-full">

@ -212,7 +212,7 @@ export default function BaseQuestionCard({
className={`group flex gap-4 rounded-md border border-slate-300 bg-white p-4 ${hoverClass}`}>
{cardContent}
{showDeleteButton && (
<div className="invisible self-center fill-red-700 group-hover:visible">
<div className="fill-danger-700 invisible self-center group-hover:visible">
<Button
icon={TrashIcon}
isLabelHidden={true}

@ -186,13 +186,13 @@ export default function ContributeQuestionForm({
</div>
<div className="flex gap-x-2">
<button
className="inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
className="focus:ring-primary-500 inline-flex w-full justify-center rounded-md border border-slate-300 bg-white px-4 py-2 text-base font-medium text-slate-700 shadow-sm hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
type="button"
onClick={onDiscard}>
Discard
</button>
<Button
className="bg-primary-600 hover:bg-primary-700 focus:ring-primary-500 inline-flex w-full justify-center rounded-md border border-transparent px-4 py-2 text-base font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:bg-gray-400 sm:ml-3 sm:w-auto sm:text-sm"
className="bg-primary-600 hover:bg-primary-700 focus:ring-primary-500 inline-flex w-full justify-center rounded-md border border-transparent px-4 py-2 text-base font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:bg-slate-400 sm:ml-3 sm:w-auto sm:text-sm"
disabled={!canSubmit}
label="Contribute"
type="submit"

@ -14,8 +14,8 @@ export default function ResumeFilterPill({
return (
<button
className={clsx(
'rounded-xl border border-indigo-500 border-transparent px-2 py-1 text-xs font-medium focus:bg-indigo-500 focus:text-white',
isSelected ? 'bg-indigo-500 text-white' : 'bg-white text-indigo-500',
'border-primary-500 focus:bg-primary-500 rounded-xl border border-transparent px-2 py-1 text-xs font-medium focus:text-white',
isSelected ? 'bg-primary-500 text-white' : 'text-primary-500 bg-white',
)}
type="button"
onClick={onClick}>

@ -22,7 +22,7 @@ export default function ResumeListItem({ href, resumeInfo }: Props) {
<div className="grid grid-cols-8 gap-4 border-b border-slate-200 p-4 hover:bg-slate-100">
<div className="col-span-4">
{resumeInfo.title}
<div className="mt-2 flex items-center justify-start text-xs text-indigo-500">
<div className="text-primary-500 mt-2 flex items-center justify-start text-xs">
<div className="flex">
<BriefcaseIcon
aria-hidden="true"

@ -1,4 +1,5 @@
import clsx from 'clsx';
import { formatDistanceToNow } from 'date-fns';
import { useState } from 'react';
import { ChevronUpIcon } from '@heroicons/react/20/solid';
import { FaceSmileIcon } from '@heroicons/react/24/outline';
@ -26,12 +27,7 @@ export default function ResumeCommentListItem({
const [showReplies, setShowReplies] = useState(true);
return (
<div
className={clsx(
'min-w-fit rounded-md bg-white ',
!comment.parentId &&
'w-11/12 border-2 border-indigo-300 p-2 drop-shadow-md',
)}>
<div className="min-w-fit">
<div className="flex flex-row space-x-2 p-1 align-top">
{/* Image Icon */}
{comment.user.image ? (
@ -58,23 +54,22 @@ export default function ResumeCommentListItem({
<div className="flex flex-row items-center space-x-1">
<p
className={clsx(
'font-medium text-black',
'font-medium text-gray-800',
!!comment.parentId && 'text-sm',
)}>
{comment.user.name ?? 'Reviewer ABC'}
</p>
<p className="text-xs font-medium text-indigo-800">
<p className="text-primary-800 text-xs font-medium">
{isCommentOwner ? '(Me)' : ''}
</p>
<ResumeUserBadges userId={comment.user.userId} />
</div>
<div className="px-2 text-xs text-gray-600">
{comment.createdAt.toLocaleString('en-US', {
dateStyle: 'medium',
timeStyle: 'short',
<div className="px-2 text-xs text-slate-600">
{formatDistanceToNow(comment.createdAt, {
addSuffix: true,
})}
</div>
</div>
@ -86,10 +81,12 @@ export default function ResumeCommentListItem({
setIsEditingComment={setIsEditingComment}
/>
) : (
<ResumeExpandableText
key={comment.description}
text={comment.description}
/>
<div className="text-gray-800">
<ResumeExpandableText
key={comment.description}
text={comment.description}
/>
</div>
)}
{/* Upvote and edit */}
@ -101,7 +98,7 @@ export default function ResumeCommentListItem({
<>
{isCommentOwner && (
<button
className="px-1 text-xs text-indigo-800 hover:text-indigo-400"
className="text-primary-800 hover:text-primary-400 px-1 text-xs"
type="button"
onClick={() => setIsEditingComment(true)}>
Edit
@ -110,7 +107,7 @@ export default function ResumeCommentListItem({
{!comment.parentId && (
<button
className="px-1 text-xs text-indigo-800 hover:text-indigo-400"
className="text-primary-800 hover:text-primary-400 px-1 text-xs"
type="button"
onClick={() => setIsReplyingComment(true)}>
Reply
@ -134,7 +131,7 @@ export default function ResumeCommentListItem({
{comment.children.length > 0 && (
<div className="min-w-fit space-y-1 pt-2">
<button
className="flex items-center space-x-1 rounded-md text-xs font-medium text-indigo-800 hover:text-indigo-300"
className="text-primary-800 hover:text-primary-300 flex items-center space-x-1 rounded-md text-xs font-medium"
type="button"
onClick={() => setShowReplies(!showReplies)}>
<ChevronUpIcon
@ -143,16 +140,24 @@ export default function ResumeCommentListItem({
!showReplies && 'rotate-180 transform',
)}
/>
<span>{showReplies ? 'Hide replies' : 'Show replies'}</span>
<span>
{showReplies
? `Hide ${
comment.children.length === 1 ? 'reply' : 'replies'
}`
: `Show ${comment.children.length} ${
comment.children.length === 1 ? 'reply' : 'replies'
}`}
</span>
</button>
{showReplies && (
<div className="flex flex-row">
<div className="relative flex flex-col px-2 py-2">
<div className="flex-grow border-r border-gray-300" />
<div className="flex-grow border-r border-slate-300" />
</div>
<div className="flex flex-col space-y-1">
<div className="flex flex-1 flex-col space-y-1">
{comment.children.map((child) => {
return (
<ResumeCommentListItem

@ -83,14 +83,14 @@ export default function ResumeCommentsForm({
};
return (
<div className="h-[calc(100vh-13rem)] overflow-y-auto">
<h2 className="text-xl font-semibold text-gray-800">Add your review</h2>
<p className="text-gray-800">
<div className="h-[calc(100vh-13rem)] overflow-y-auto pb-4">
<h2 className="text-xl font-semibold text-slate-800">Add your review</h2>
<p className="text-slate-800">
Please fill in at least one section to submit your review
</p>
<form
className="w-full space-y-8 divide-y divide-gray-200"
className="w-full space-y-8 divide-y divide-slate-200"
onSubmit={handleSubmit(onSubmit)}>
<div className="mt-4 space-y-4">
<TextArea

@ -1,7 +1,9 @@
import clsx from 'clsx';
import { useSession } from 'next-auth/react';
import {
BookOpenIcon,
BriefcaseIcon,
ChatBubbleLeftRightIcon,
CodeBracketSquareIcon,
FaceSmileIcon,
IdentificationIcon,
@ -9,24 +11,20 @@ import {
} from '@heroicons/react/24/outline';
import { ResumesSection } from '@prisma/client';
import { Spinner } from '@tih/ui';
import { Button } from '@tih/ui';
import { trpc } from '~/utils/trpc';
import { RESUME_COMMENTS_SECTIONS } from './resumeCommentConstants';
import ResumeCommentListItem from './ResumeCommentListItem';
import ResumeSignInButton from '../shared/ResumeSignInButton';
import type { ResumeComment } from '~/types/resume-comments';
type ResumeCommentsListProps = Readonly<{
resumeId: string;
setShowCommentsForm: (show: boolean) => void;
}>;
export default function ResumeCommentsList({
resumeId,
setShowCommentsForm,
}: ResumeCommentsListProps) {
const { data: sessionData } = useSession();
@ -50,31 +48,14 @@ export default function ResumeCommentsList({
}
};
const renderButton = () => {
if (sessionData === null) {
return <ResumeSignInButton text="to join discussion" />;
}
return (
<Button
className="-mb-2"
display="block"
label="Add your review"
variant="tertiary"
onClick={() => setShowCommentsForm(true)}
/>
);
};
return (
<div className="space-y-3">
{renderButton()}
{commentsQuery.isLoading ? (
<div className="col-span-10 pt-4">
<Spinner display="block" size="lg" />
</div>
) : (
<div className="m-2 flow-root h-[calc(100vh-17rem)] w-full flex-col space-y-4 overflow-y-auto overflow-x-hidden pt-14 pb-6">
<div className="mb-8 flow-root h-[calc(100vh-13rem)] w-full flex-col space-y-4 overflow-y-auto overflow-x-hidden">
{RESUME_COMMENTS_SECTIONS.map(({ label, value }) => {
const comments = commentsQuery.data
? commentsQuery.data.filter((comment: ResumeComment) => {
@ -85,25 +66,43 @@ export default function ResumeCommentsList({
return (
<div key={value} className="mb-4 space-y-4">
<div className="flex flex-row items-center space-x-2 text-indigo-800">
<div className="text-primary-800 flex flex-row items-center space-x-2">
{renderIcon(value)}
<div className="w-fit text-lg font-medium">{label}</div>
</div>
{commentCount > 0 ? (
comments.map((comment) => {
return (
<ResumeCommentListItem
key={comment.id}
comment={comment}
userId={sessionData?.user?.id}
/>
);
})
) : (
<div>There are no comments for this section yet!</div>
)}
<div className="w-full space-y-4 pr-4">
<div
className={clsx(
'space-y-2 rounded-md border-2 bg-white px-4 py-3 drop-shadow-md',
commentCount ? 'border-slate-300' : 'border-slate-300',
)}>
{commentCount > 0 ? (
comments.map((comment) => {
return (
<ResumeCommentListItem
key={comment.id}
comment={comment}
userId={sessionData?.user?.id}
/>
);
})
) : (
<div className="flex flex-row items-center text-sm">
<ChatBubbleLeftRightIcon className="mr-2 h-6 w-6 text-slate-500" />
<div className="text-slate-500">
There are no comments for this section yet!
</div>
</div>
)}
</div>
</div>
<div className="relative flex flex-row pr-6 pt-2">
<div className="flex-grow border-t border-gray-300" />
</div>
</div>
);
})}

@ -1,40 +0,0 @@
import { useState } from 'react';
import ResumeCommentsForm from './ResumeCommentsForm';
import ResumeCommentsList from './ResumeCommentsList';
type CommentsSectionProps = {
resumeId: string;
};
export default function ResumeCommentsSection({
resumeId,
}: CommentsSectionProps) {
const [showCommentsForm, setShowCommentsForm] = useState(false);
return (
<>
<div className="relative p-2 lg:hidden">
<div aria-hidden="true" className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300" />
</div>
<div className="relative flex justify-center">
<span className="bg-gray-50 px-3 text-lg font-medium text-gray-900">
Reviews
</span>
</div>
</div>
{showCommentsForm ? (
<ResumeCommentsForm
resumeId={resumeId}
setShowCommentsForm={setShowCommentsForm}
/>
) : (
<ResumeCommentsList
resumeId={resumeId}
setShowCommentsForm={setShowCommentsForm}
/>
)}
</>
);
}

@ -86,18 +86,18 @@ export default function ResumeCommentVoteButtons({
'h-4 w-4',
commentVotesQuery.data?.userVote?.value === Vote.UPVOTE ||
upvoteAnimation
? 'fill-indigo-500'
: 'fill-gray-400',
? 'fill-primary-500'
: 'fill-slate-400',
userId &&
!downvoteAnimation &&
!upvoteAnimation &&
'hover:fill-indigo-500',
'hover:fill-primary-500',
upvoteAnimation && 'animate-[bounce_0.5s_infinite] cursor-default',
)}
/>
</button>
<div className="flex min-w-[1rem] justify-center text-xs">
<div className="flex min-w-[1rem] justify-center text-xs font-semibold text-gray-700">
{commentVotesQuery.data?.numVotes ?? 0}
</div>
@ -115,12 +115,12 @@ export default function ResumeCommentVoteButtons({
'h-4 w-4',
commentVotesQuery.data?.userVote?.value === Vote.DOWNVOTE ||
downvoteAnimation
? 'fill-red-500'
: 'fill-gray-400',
? 'fill-danger-500'
: 'fill-slate-400',
userId &&
!downvoteAnimation &&
!upvoteAnimation &&
'hover:fill-red-500',
'hover:fill-danger-500',
downvoteAnimation &&
'animate-[bounce_0.5s_infinite] cursor-default',
)}

@ -7,16 +7,16 @@ export function CallToAction() {
<section className="relative overflow-hidden py-32" id="get-started-today">
<Container className="relative">
<div className="mx-auto max-w-lg text-center">
<h2 className="font-display text-3xl tracking-tight text-gray-900 sm:text-4xl">
<h2 className="font-display text-3xl tracking-tight text-slate-900 sm:text-4xl">
Resume review can start right now.
</h2>
<p className="mt-4 text-lg tracking-tight text-gray-600">
<p className="mt-4 text-lg tracking-tight text-slate-600">
It's free! Take charge of your resume game by learning from the top
engineers in the field.
</p>
<Link href="/resumes/browse">
<button
className="mt-4 rounded-md bg-indigo-500 py-2 px-3 text-sm font-medium text-white"
className="bg-primary-500 mt-4 rounded-md py-2 px-3 text-sm font-medium text-white"
type="button">
Start browsing now
</button>

@ -7,7 +7,7 @@ export function Hero() {
<Container className="pb-36 pt-20 text-center lg:pt-32">
<h1 className="font-display mx-auto max-w-4xl text-5xl font-medium tracking-tight text-slate-900 sm:text-7xl">
Resume review{' '}
<span className="relative whitespace-nowrap text-indigo-500">
<span className="text-primary-500 relative whitespace-nowrap">
<svg
aria-hidden="true"
className="absolute top-2/3 left-0 h-[0.58em] w-full fill-blue-300/70"
@ -26,18 +26,18 @@ export function Hero() {
<div className="mt-10 flex justify-center gap-x-4">
<Link href="/resumes/browse">
<button
className="rounded-md bg-indigo-500 py-2 px-3 text-sm font-medium text-white"
className="bg-primary-500 rounded-md py-2 px-3 text-sm font-medium text-white"
type="button">
Start browsing now
</button>
</Link>
<Link href="https://www.youtube.com/watch?v=dQw4w9WgXcQ">
<button
className="group inline-flex items-center justify-center rounded-md py-2 px-4 text-sm ring-1 ring-slate-200 hover:text-slate-900 hover:ring-slate-300 focus:outline-none focus-visible:outline-indigo-600 focus-visible:ring-slate-300 active:bg-slate-100 active:text-slate-600"
className="focus-visible:outline-primary-600 group inline-flex items-center justify-center rounded-md py-2 px-4 text-sm ring-1 ring-slate-200 hover:text-slate-900 hover:ring-slate-300 focus:outline-none focus-visible:ring-slate-300 active:bg-slate-100 active:text-slate-600"
type="button">
<svg
aria-hidden="true"
className="h-3 w-3 flex-none fill-indigo-600 group-active:fill-current">
className="fill-primary-600 h-3 w-3 flex-none group-active:fill-current">
<path d="m9.997 6.91-7.583 3.447A1 1 0 0 1 1 9.447V2.553a1 1 0 0 1 1.414-.91L9.997 5.09c.782.355.782 1.465 0 1.82Z" />
</svg>
<span className="ml-3">Watch video</span>

@ -49,7 +49,7 @@ export function PrimaryFeatures() {
return (
<section
className="relative overflow-hidden bg-gradient-to-r from-indigo-400 to-indigo-700 pt-20 pb-28 sm:py-32"
className="from-primary-400 to-primary-700 relative overflow-hidden bg-gradient-to-r pt-20 pb-28 sm:py-32"
id="features">
<Container className="relative">
<div className="max-w-2xl md:mx-auto md:text-center xl:max-w-none">

@ -94,7 +94,7 @@ function QuoteIcon(props: QuoteProps) {
export function Testimonials() {
return (
<section
className="bg-gradient-to-r from-indigo-700 to-indigo-400 py-20 sm:py-32"
className="from-primary-700 to-primary-400 bg-gradient-to-r py-20 sm:py-32"
id="testimonials">
<Container>
<div className="mx-auto max-w-2xl md:text-center">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 KiB

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1006 KiB

After

Width:  |  Height:  |  Size: 909 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 KiB

After

Width:  |  Height:  |  Size: 396 KiB

@ -36,7 +36,7 @@ export default function ResumeExpandableText({
</span>
{descriptionOverflow && (
<p
className="mt-1 cursor-pointer text-xs text-indigo-500 hover:text-indigo-300"
className="text-primary-500 hover:text-primary-300 mt-1 cursor-pointer text-xs"
onClick={onSeeActionClicked}>
{isExpanded ? 'See Less' : 'See More'}
</p>

@ -8,10 +8,10 @@ type Props = Readonly<{
export default function ResumeSignInButton({ text, className }: Props) {
return (
<div className={clsx('flex justify-center pt-4', className)}>
<div className={clsx('flex justify-center', className)}>
<p>
<a
className="text-primary-800 hover:text-primary-500"
className="text-indigo-500 hover:text-indigo-600"
href="/api/auth/signin"
onClick={(event) => {
event.preventDefault();

@ -9,6 +9,7 @@ type Props = Readonly<{
isLabelHidden?: boolean;
onSelect: (option: TypeaheadOption) => void;
placeHolder?: string;
required?: boolean;
}>;
export default function CompaniesTypeahead({
@ -16,6 +17,7 @@ export default function CompaniesTypeahead({
onSelect,
isLabelHidden,
placeHolder,
required,
}: Props) {
const [query, setQuery] = useState('');
const companies = trpc.useQuery([
@ -42,6 +44,7 @@ export default function CompaniesTypeahead({
})) ?? []
}
placeholder={placeHolder}
required={required}
onQueryChange={setQuery}
onSelect={onSelect}
/>

@ -0,0 +1,31 @@
export const JobTitleLabels = {
'ai-ml-engineer': 'AI/ML Engineer',
'algorithms-engineer': 'Algorithms Engineer',
'android-engineer': 'Android Software Engineer',
'applications-engineer': 'Applications Engineer',
'back-end-engineer': 'Back End Engineer',
'business-engineer': 'Business Engineer',
'data-engineer': 'Data Engineer',
'devops-engineer': 'DevOps Engineer',
'enterprise-engineer': 'Enterprise Engineer',
'front-end-engineer': 'Front End Engineer',
'hardware-engineer': 'Hardware Engineer',
'ios-engineer': 'iOS Software Engineer',
'mobile-engineer': 'Mobile Software Engineer (iOS + Android)',
'networks-engineer': 'Networks Engineer',
'partner-engineer': 'Partner Engineer',
'production-engineer': 'Production Engineer',
'research-engineer': 'Research Engineer',
'sales-engineer': 'Sales Engineer',
'security-engineer': 'Security Engineer',
'site-reliability-engineer': 'Site Reliability Engineer (SRE)',
'software-engineer': 'Software Engineer',
'systems-engineer': 'Systems Engineer',
'test-engineer': 'QA/Test Engineer (SDET)',
};
export type JobTitleType = keyof typeof JobTitleLabels;
export function getLabelForJobTitleType(jobTitle: JobTitleType): string {
return JobTitleLabels[jobTitle];
}

@ -0,0 +1,48 @@
import { useState } from 'react';
import type { TypeaheadOption } from '@tih/ui';
import { Typeahead } from '@tih/ui';
import { JobTitleLabels } from './JobTitles';
type Props = Readonly<{
disabled?: boolean;
isLabelHidden?: boolean;
onSelect: (option: TypeaheadOption) => void;
placeHolder?: string;
required?: boolean;
}>;
export default function JobTitlesTypeahead({
disabled,
onSelect,
isLabelHidden,
placeHolder,
required,
}: Props) {
const [query, setQuery] = useState('');
const options = Object.entries(JobTitleLabels)
.map(([slug, label]) => ({
id: slug,
label,
value: slug,
}))
.filter(
({ label }) =>
label.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) > -1,
);
return (
<Typeahead
disabled={disabled}
isLabelHidden={isLabelHidden}
label="Job Title"
noResultsMessage="No available job titles."
nullable={true}
options={options}
placeholder={placeHolder}
required={required}
onQueryChange={setQuery}
onSelect={onSelect}
/>
);
}

@ -2,7 +2,7 @@ import { Head, Html, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html className="h-full bg-gray-50">
<Html className="h-full bg-slate-50">
<Head />
<body className="h-full overflow-hidden">
<Main />

@ -12,7 +12,7 @@ export default function OffersHomePage() {
return (
<main className="flex-1 overflow-y-auto">
<div className="grid-rows grid h-1/2 bg-gray-100">
<div className="grid-rows grid h-1/2 bg-slate-100">
<OffersTitle />
<div className="flex items-start justify-center">
<div className="mt-4 flex items-center">

@ -11,6 +11,7 @@ import type {
OfferDisplayData,
} from '~/components/offers/types';
import { useToast } from '~/../../../packages/ui/dist';
import { convertMoneyToString } from '~/utils/offers/currency';
import { getProfilePath } from '~/utils/offers/link';
import { formatDate } from '~/utils/offers/time';
@ -19,6 +20,7 @@ import { trpc } from '~/utils/trpc';
import type { Profile, ProfileAnalysis, ProfileOffer } from '~/types/offers';
export default function OfferProfile() {
const { showToast } = useToast();
const ErrorPage = (
<Error statusCode={404} title="Requested profile does not exist." />
);
@ -131,11 +133,18 @@ export default function OfferProfile() {
const trpcContext = trpc.useContext();
const deleteMutation = trpc.useMutation(['offers.profile.delete'], {
onError: () => {
alert('Error deleting profile'); // TODO: replace with toast
showToast({
title: `Error deleting offers profile.`,
variant: 'failure',
});
},
onSuccess: () => {
trpcContext.invalidateQueries(['offers.profile.listOne']);
router.push('/offers');
showToast({
title: `Offers profile successfully deleted!`,
variant: 'success',
});
},
});

@ -50,7 +50,7 @@ export default function ListPage() {
{lists.map((list) => (
<li
key={list.id}
className={`flex items-center hover:bg-gray-50 ${
className={`flex items-center hover:bg-slate-50 ${
selectedList === list.id ? 'bg-primary-100' : ''
}`}>
<button
@ -83,7 +83,7 @@ export default function ListPage() {
className={`${
active
? 'bg-violet-500 text-white'
: 'text-gray-900'
: 'text-slate-900'
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
type="button">
Delete

@ -3,7 +3,7 @@ import formatDistanceToNow from 'date-fns/formatDistanceToNow';
import Error from 'next/error';
import Head from 'next/head';
import { useRouter } from 'next/router';
import { useSession } from 'next-auth/react';
import { signIn, useSession } from 'next-auth/react';
import { useState } from 'react';
import {
AcademicCapIcon,
@ -14,9 +14,10 @@ import {
PencilSquareIcon,
StarIcon,
} from '@heroicons/react/20/solid';
import { Spinner } from '@tih/ui';
import { Button, Spinner } from '@tih/ui';
import ResumeCommentsSection from '~/components/resumes/comments/ResumeCommentsSection';
import ResumeCommentsForm from '~/components/resumes/comments/ResumeCommentsForm';
import ResumeCommentsList from '~/components/resumes/comments/ResumeCommentsList';
import ResumePdf from '~/components/resumes/ResumePdf';
import ResumeExpandableText from '~/components/resumes/shared/ResumeExpandableText';
@ -59,6 +60,7 @@ export default function ResumeReviewPage() {
session?.user?.id != null && session.user.id === detailsQuery.data?.userId;
const [isEditMode, setIsEditMode] = useState(false);
const [showCommentsForm, setShowCommentsForm] = useState(false);
const onStarButtonClick = () => {
if (session?.user?.id == null) {
@ -81,6 +83,32 @@ export default function ResumeReviewPage() {
setIsEditMode(true);
};
const renderReviewButton = () => {
if (session === null) {
return (
<div className=" flex h-10 justify-center rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-[400] hover:cursor-pointer hover:bg-slate-50">
<a
href="/api/auth/signin"
onClick={(event) => {
event.preventDefault();
signIn();
}}>
Sign in to join discussion
</a>
</div>
);
}
return (
<Button
className="h-10 py-2"
display="block"
label="Add your review"
variant="tertiary"
onClick={() => setShowCommentsForm(true)}
/>
);
};
if (isEditMode && detailsQuery.data != null) {
return (
<SubmitResumeForm
@ -120,12 +148,21 @@ export default function ResumeReviewPage() {
</Head>
<main className="h-[calc(100vh-2rem)] flex-1 space-y-2 overflow-y-auto py-4 px-8 xl:px-12 2xl:pr-16">
<div className="flex justify-between">
<h1 className="text-2xl font-semibold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">
<h1 className="pr-2 text-2xl font-semibold leading-7 text-slate-900 sm:truncate sm:text-3xl sm:tracking-tight">
{detailsQuery.data.title}
</h1>
<div className="flex gap-4">
<div className="flex gap-4 xl:pr-4">
{userIsOwner && (
<button
className="p h-10 rounded-md border border-slate-300 bg-white py-1 px-2 text-center"
type="button"
onClick={onEditButtonClick}>
<PencilSquareIcon className="text-primary-600 hover:text-primary-300 h-6 w-6" />
</button>
)}
<button
className="isolate inline-flex h-10 items-center space-x-4 rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 disabled:hover:bg-white"
className="isolate inline-flex h-10 items-center space-x-4 rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-sm hover:bg-slate-50 disabled:hover:bg-white"
disabled={starMutation.isLoading || unstarMutation.isLoading}
type="button"
onClick={onStarButtonClick}>
@ -141,7 +178,7 @@ export default function ResumeReviewPage() {
className={clsx(
detailsQuery.data?.stars.length
? 'text-orange-400'
: 'text-gray-400',
: 'text-slate-400',
)}
/>
)}
@ -152,42 +189,36 @@ export default function ResumeReviewPage() {
{detailsQuery.data?._count.stars}
</span>
</button>
{userIsOwner && (
<button
className="p h-10 rounded-md border border-gray-300 bg-white py-1 px-2 text-center"
type="button"
onClick={onEditButtonClick}>
<PencilSquareIcon className="h-6 w-6 text-indigo-600 hover:text-indigo-300" />
</button>
)}
<div className="hidden xl:block">{renderReviewButton()}</div>
</div>
</div>
<div className="flex flex-col lg:mt-0 lg:flex-row lg:flex-wrap lg:space-x-8">
<div className="mt-2 flex items-center text-sm text-gray-500">
<div className="mt-2 flex items-center text-sm text-slate-600 xl:mt-1">
<BriefcaseIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
{detailsQuery.data.role}
</div>
<div className="flex items-center pt-2 text-sm text-gray-500">
<div className="flex items-center pt-2 text-sm text-slate-600 xl:pt-1">
<MapPinIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
{detailsQuery.data.location}
</div>
<div className="flex items-center pt-2 text-sm text-gray-500">
<div className="flex items-center pt-2 text-sm text-slate-600 xl:pt-1">
<AcademicCapIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
{detailsQuery.data.experience}
</div>
<div className="flex items-center pt-2 text-sm text-gray-500">
<div className="flex items-center pt-2 text-sm text-slate-600 xl:pt-1">
<CalendarIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
{`Uploaded ${formatDistanceToNow(detailsQuery.data.createdAt, {
addSuffix: true,
@ -195,10 +226,10 @@ export default function ResumeReviewPage() {
</div>
</div>
{detailsQuery.data.additionalInfo && (
<div className="flex items-start whitespace-pre-wrap pt-2 text-sm text-gray-500">
<div className="flex items-start whitespace-pre-wrap pt-2 text-sm text-slate-600 xl:pt-1">
<InformationCircleIcon
aria-hidden="true"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
className="mr-1.5 h-5 w-5 flex-shrink-0 text-indigo-400"
/>
<ResumeExpandableText
key={detailsQuery.data.additionalInfo}
@ -206,12 +237,35 @@ export default function ResumeReviewPage() {
/>
</div>
)}
<div className="flex w-full flex-col gap-6 py-4 lg:flex-row">
<div className="w-full lg:w-[780px]">
<div className="flex w-full flex-col gap-6 py-4 xl:flex-row xl:py-0">
<div className="w-full xl:w-1/2">
<ResumePdf url={detailsQuery.data.url} />
</div>
<div className="grow">
<ResumeCommentsSection resumeId={resumeId as string} />
<div className="relative p-2 xl:hidden">
<div
aria-hidden="true"
className="absolute inset-0 flex items-center">
<div className="w-full border-t border-slate-300" />
</div>
<div className="relative flex justify-center">
<span className="bg-slate-50 px-3 text-lg font-medium text-slate-900">
Reviews
</span>
</div>
</div>
<div className="mb-4 xl:hidden">{renderReviewButton()}</div>
{showCommentsForm ? (
<ResumeCommentsForm
resumeId={resumeId as string}
setShowCommentsForm={setShowCommentsForm}
/>
) : (
<ResumeCommentsList resumeId={resumeId as string} />
)}
</div>
</div>
</main>

@ -113,7 +113,7 @@ export default function ResumeHomePage() {
useEffect(() => {
setCurrentPage(1);
}, [userFilters, sortOrder]);
}, [userFilters, sortOrder, searchValue]);
const allResumesQuery = trpc.useQuery(
[
@ -126,6 +126,7 @@ export default function ResumeHomePage() {
searchValue: useDebounceValue(searchValue, DEBOUNCE_DELAY),
skip,
sortOrder,
take: PAGE_LIMIT,
},
],
{
@ -144,6 +145,7 @@ export default function ResumeHomePage() {
searchValue: useDebounceValue(searchValue, DEBOUNCE_DELAY),
skip,
sortOrder,
take: PAGE_LIMIT,
},
],
{
@ -163,6 +165,7 @@ export default function ResumeHomePage() {
searchValue: useDebounceValue(searchValue, DEBOUNCE_DELAY),
skip,
sortOrder,
take: PAGE_LIMIT,
},
],
{
@ -279,11 +282,11 @@ export default function ResumeHomePage() {
leaveTo="translate-x-full">
<Dialog.Panel className="relative ml-auto flex h-full w-full max-w-xs flex-col overflow-y-scroll bg-white py-4 pb-12 shadow-xl">
<div className="flex items-center justify-between px-4">
<h2 className="text-lg font-medium text-gray-900">
<h2 className="text-lg font-medium text-slate-900">
Shortcuts
</h2>
<button
className="-mr-2 flex h-10 w-10 items-center justify-center rounded-md bg-white p-2 text-gray-400"
className="-mr-2 flex h-10 w-10 items-center justify-center rounded-md bg-white p-2 text-slate-400"
type="button"
onClick={() => setMobileFiltersOpen(false)}>
<span className="sr-only">Close menu</span>
@ -291,9 +294,9 @@ export default function ResumeHomePage() {
</button>
</div>
<form className="mt-4 border-t border-gray-200">
<form className="mt-4 border-t border-slate-200">
<ul
className="flex flex-wrap justify-start gap-4 px-4 py-3 font-medium text-gray-900"
className="flex flex-wrap justify-start gap-4 px-4 py-3 font-medium text-slate-900"
role="list">
{SHORTCUTS.map((shortcut) => (
<li key={shortcut.name}>
@ -310,12 +313,12 @@ export default function ResumeHomePage() {
<Disclosure
key={filter.id}
as="div"
className="border-t border-gray-200 px-4 py-6">
className="border-t border-slate-200 px-4 py-6">
{({ open }) => (
<>
<h3 className="-mx-2 -my-3 flow-root">
<Disclosure.Button className="flex w-full items-center justify-between bg-white px-2 py-3 text-gray-400 hover:text-gray-500">
<span className="font-medium text-gray-900">
<Disclosure.Button className="flex w-full items-center justify-between bg-white px-2 py-3 text-slate-400 hover:text-slate-500">
<span className="font-medium text-slate-900">
{filter.label}
</span>
<span className="ml-6 flex items-center">
@ -338,7 +341,7 @@ export default function ResumeHomePage() {
{filter.options.map((option) => (
<div
key={option.value}
className="[&>div>div:nth-child(2)>label]:font-normal [&>div>div:nth-child(1)>input]:text-indigo-600 [&>div>div:nth-child(1)>input]:ring-indigo-500">
className="[&>div>div:nth-child(1)>input]:text-primary-600 [&>div>div:nth-child(1)>input]:ring-primary-500 [&>div>div:nth-child(2)>label]:font-normal">
<CheckboxInput
label={option.label}
value={userFilters[filter.id].includes(
@ -368,16 +371,16 @@ export default function ResumeHomePage() {
</Transition.Root>
</div>
<main className="h-[calc(100vh-4rem)] flex-auto overflow-y-scroll px-8 pt-6 pb-4">
<main className="h-[calc(100vh-4rem)] flex-auto px-8 pb-4">
<div className="flex justify-start">
<div className="hidden w-1/6 pt-2 lg:block">
<div className="fixed top-0 bottom-0 mt-24 hidden w-64 overflow-auto lg:block">
<h3 className="text-md font-medium tracking-tight text-gray-900">
Shortcuts
</h3>
<div className="w-100 pt-4 sm:pr-0 md:pr-4">
<form>
<ul
className="flex w-11/12 flex-wrap justify-start gap-2 pb-6 text-sm font-medium text-gray-900"
className="flex w-11/12 flex-wrap justify-start gap-2 pb-6 text-sm font-medium text-slate-900"
role="list">
{SHORTCUTS.map((shortcut) => (
<li key={shortcut.name}>
@ -389,19 +392,19 @@ export default function ResumeHomePage() {
</li>
))}
</ul>
<h3 className="text-md font-medium tracking-tight text-gray-900">
<h3 className="text-md font-medium tracking-tight text-slate-900">
Explore these filters
</h3>
{filters.map((filter) => (
<Disclosure
key={filter.id}
as="div"
className="border-b border-gray-200 py-6">
className="border-b border-slate-200 py-6">
{({ open }) => (
<>
<h3 className="-my-3 flow-root">
<Disclosure.Button className="flex w-full items-center justify-between py-3 text-sm text-gray-400 hover:text-gray-500">
<span className="font-medium text-gray-900">
<Disclosure.Button className="flex w-full items-center justify-between py-3 text-sm text-slate-400 hover:text-slate-500">
<span className="font-medium text-slate-900">
{filter.label}
</span>
<span className="ml-6 flex items-center">
@ -428,7 +431,7 @@ export default function ResumeHomePage() {
{filter.options.map((option) => (
<div
key={option.value}
className="[&>div>div:nth-child(2)>label]:font-normal [&>div>div:nth-child(1)>input]:text-indigo-600 [&>div>div:nth-child(1)>input]:ring-indigo-500">
className="[&>div>div:nth-child(1)>input]:text-primary-600 [&>div>div:nth-child(1)>input]:ring-primary-500 [&>div>div:nth-child(2)>label]:font-normal">
<CheckboxInput
label={option.label}
value={userFilters[filter.id].includes(
@ -453,8 +456,8 @@ export default function ResumeHomePage() {
</form>
</div>
</div>
<div className="w-full">
<div className="lg:border-grey-200 flex flex-wrap items-center justify-between pb-2 lg:border-b">
<div className="relative lg:left-64 lg:w-[calc(100%-16rem)]">
<div className="lg:border-grey-200 sticky top-0 z-10 flex flex-wrap items-center justify-between bg-gray-50 pt-6 pb-2 lg:border-b">
<div className="border-grey-200 mb-4 flex w-full justify-between border-b pb-2 lg:mb-0 lg:w-auto lg:border-none lg:pb-0">
<div>
<Tabs
@ -477,10 +480,9 @@ export default function ResumeHomePage() {
onChange={onTabChange}
/>
</div>
<div>
<button
className="ml-4 rounded-md bg-indigo-500 py-2 px-3 text-sm font-medium text-white lg:hidden"
className="bg-primary-500 ml-4 rounded-md py-2 px-3 text-sm font-medium text-white lg:hidden"
type="button"
onClick={onSubmitResume}>
Submit Resume
@ -489,17 +491,16 @@ export default function ResumeHomePage() {
</div>
<div className="flex flex-wrap items-center justify-start gap-8">
<div className="w-64">
<form>
<TextInput
label=""
placeholder="Search Resumes"
startAddOn={MagnifyingGlassIcon}
startAddOnType="icon"
type="text"
value={searchValue}
onChange={setSearchValue}
/>
</form>
<TextInput
isLabelHidden={true}
label="search"
placeholder="Search Resumes"
startAddOn={MagnifyingGlassIcon}
startAddOnType="icon"
type="text"
value={searchValue}
onChange={setSearchValue}
/>
</div>
<div>
<DropdownMenu align="end" label={SORT_OPTIONS[sortOrder]}>
@ -513,16 +514,15 @@ export default function ResumeHomePage() {
</DropdownMenu>
</div>
<button
className="-m-2 text-gray-400 hover:text-gray-500 lg:hidden"
className="-m-2 text-slate-400 hover:text-slate-500 lg:hidden"
type="button"
onClick={() => setMobileFiltersOpen(true)}>
<span className="sr-only">Filters</span>
<FunnelIcon aria-hidden="true" className="h-6 w-6" />
</button>
<div>
<button
className="hidden w-36 rounded-md bg-indigo-500 py-2 px-3 text-sm font-medium text-white lg:block"
className="bg-primary-500 hidden w-36 rounded-md py-2 px-3 text-sm font-medium text-white lg:block"
type="button"
onClick={onSubmitResume}>
Submit Resume
@ -550,20 +550,26 @@ export default function ResumeHomePage() {
{getEmptyDataText(tabsValue, searchValue, userFilters)}
</div>
) : (
<>
<ResumeListItems resumes={getTabResumes()} />
{getTabTotalPages() > 1 && (
<div className="mt-4 flex justify-center">
<Pagination
current={currentPage}
end={getTabTotalPages()}
label="pagination"
start={1}
onSelect={(page) => setCurrentPage(page)}
/>
<div className="h-[calc(100vh-9rem)] pb-10 lg:h-[calc(100vh-6rem)]">
<div className="h-[85%] overflow-y-auto">
<div>
<ResumeListItems resumes={getTabResumes()} />
</div>
)}
</>
</div>
<div className="flex h-[15%] items-center justify-center">
{getTabTotalPages() > 1 && (
<div>
<Pagination
current={currentPage}
end={getTabTotalPages()}
label="pagination"
start={1}
onSelect={(page) => setCurrentPage(page)}
/>
</div>
)}
</div>
</div>
)}
</div>
</div>

@ -14,6 +14,7 @@ import {
CheckboxInput,
Dialog,
Select,
Spinner,
TextArea,
TextInput,
} from '@tih/ui';
@ -73,7 +74,7 @@ export default function SubmitResumeForm({
>(null);
const [isDialogShown, setIsDialogShown] = useState(false);
const { data: session, status } = useSession();
const { status } = useSession();
const router = useRouter();
const trpcContext = trpc.useContext();
const resumeUpsertMutation = trpc.useMutation('resumes.resume.user.upsert');
@ -122,12 +123,10 @@ export default function SubmitResumeForm({
// Route user to sign in if not logged in
useEffect(() => {
if (status !== 'loading') {
if (session?.user?.id == null) {
router.push('/api/auth/signin');
}
if (status === 'unauthenticated') {
router.push('/api/auth/signin');
}
}, [router, session, status]);
}, [router, status]);
const onSubmit: SubmitHandler<IFormInput> = async (data) => {
setIsLoading(true);
@ -221,7 +220,7 @@ export default function SubmitResumeForm({
}, [errors?.file, invalidFileUploadError]);
const onValueChange = (section: InputKeys, value: string) => {
setValue(section, value.trim(), { shouldTouch: false });
setValue(section, value.trim(), { shouldDirty: true });
};
return (
@ -229,180 +228,199 @@ export default function SubmitResumeForm({
<Head>
<title>Upload a Resume</title>
</Head>
<main className="h-[calc(100vh-4rem)] flex-1 overflow-y-auto">
<section
aria-labelledby="primary-heading"
className="flex h-full min-w-0 flex-1 flex-col lg:order-last">
{/* Reset Dialog component */}
<Dialog
isShown={isDialogShown}
primaryButton={
<Button
display="block"
label="OK"
variant="primary"
onClick={onClickResetDialog}
/>
}
secondaryButton={
<Button
display="block"
label="Cancel"
variant="tertiary"
onClick={() => setIsDialogShown(false)}
/>
}
title={
isNewForm
? 'Are you sure you want to clear?'
: 'Are you sure you want to leave?'
}
onClose={() => setIsDialogShown(false)}>
Note that your current input will not be saved!
</Dialog>
<form
className="mt-8 w-full max-w-screen-lg space-y-6 self-center rounded-lg bg-white p-10 shadow-lg"
onSubmit={handleSubmit(onSubmit)}>
<h1 className="mb-4 text-center text-2xl font-semibold">
{isNewForm ? 'Upload a resume' : 'Update details'}
</h1>
{/* Title Section */}
<TextInput
{...register('title', { required: true })}
disabled={isLoading}
label="Title"
placeholder={TITLE_PLACEHOLDER}
required={true}
onChange={(val) => setValue('title', val)}
/>
<div className="flex gap-8">
<Select
{...register('role', { required: true })}
defaultValue={undefined}
{status === 'loading' && (
<div className="w-full pt-4">
{' '}
<Spinner display="block" size="lg" />{' '}
</div>
)}
{status === 'authenticated' && (
<main className="h-[calc(100vh-4rem)] flex-1 overflow-y-auto">
<section
aria-labelledby="primary-heading"
className="flex h-full min-w-0 flex-1 flex-col lg:order-last">
{/* Reset Dialog component */}
<Dialog
isShown={isDialogShown}
primaryButton={
<Button
display="block"
label="OK"
variant="primary"
onClick={onClickResetDialog}
/>
}
secondaryButton={
<Button
display="block"
label="Cancel"
variant="tertiary"
onClick={() => setIsDialogShown(false)}
/>
}
title={
isNewForm
? 'Are you sure you want to clear?'
: 'Are you sure you want to leave?'
}
onClose={() => setIsDialogShown(false)}>
Note that your current input will not be saved!
</Dialog>
<form
className="mt-8 w-full max-w-screen-lg space-y-6 self-center rounded-lg bg-white p-10 shadow-lg"
onSubmit={handleSubmit(onSubmit)}>
<h1 className="mb-4 text-center text-2xl font-semibold">
{isNewForm ? 'Upload a resume' : 'Update details'}
</h1>
{/* Title Section */}
<TextInput
{...(register('title', { required: true }), {})}
defaultValue={initFormDetails?.title}
disabled={isLoading}
label="Role"
options={ROLES}
placeholder=" "
errorMessage={
errors.title?.message != null
? 'Title cannot be empty'
: undefined
}
label="Title"
placeholder={TITLE_PLACEHOLDER}
required={true}
onChange={(val) => setValue('role', val)}
onChange={(val) => onValueChange('title', val)}
/>
<div className="flex gap-8">
<Select
{...register('role', { required: true })}
defaultValue={undefined}
disabled={isLoading}
label="Role"
options={ROLES}
placeholder=" "
required={true}
onChange={(val) => setValue('role', val)}
/>
<Select
{...register('experience', { required: true })}
disabled={isLoading}
label="Experience Level"
options={EXPERIENCES}
placeholder=" "
required={true}
onChange={(val) => setValue('experience', val)}
/>
</div>
<Select
{...register('experience', { required: true })}
{...register('location', { required: true })}
disabled={isLoading}
label="Experience Level"
options={EXPERIENCES}
label="Location"
options={LOCATIONS}
placeholder=" "
required={true}
onChange={(val) => setValue('experience', val)}
onChange={(val) => setValue('location', val)}
/>
</div>
<Select
{...register('location', { required: true })}
disabled={isLoading}
label="Location"
options={LOCATIONS}
placeholder=" "
required={true}
onChange={(val) => setValue('location', val)}
/>
{/* Upload resume form */}
{isNewForm && (
<div className="space-y-2">
<p className="text-sm font-medium text-slate-700">
Upload resume (PDF format)
<span aria-hidden="true" className="text-danger-500">
{' '}
*
</span>
</p>
<div
{...getRootProps()}
className={clsx(
fileUploadError ? 'border-danger-600' : 'border-gray-300',
'flex cursor-pointer justify-center rounded-md border-2 border-dashed bg-gray-100 py-4',
)}>
<div className="space-y-1 text-center">
{resumeFile == null ? (
<ArrowUpCircleIcon className="m-auto h-10 w-10 text-indigo-500" />
) : (
<p
className="cursor-pointer underline underline-offset-1 hover:text-indigo-600"
onClick={onClickDownload}>
{resumeFile.name}
{/* Upload resume form */}
{isNewForm && (
<div className="space-y-2">
<p className="text-sm font-medium text-slate-700">
Upload resume (PDF format)
<span aria-hidden="true" className="text-danger-500">
{' '}
*
</span>
</p>
<div
{...getRootProps()}
className={clsx(
fileUploadError
? 'border-danger-600'
: 'border-slate-300',
'flex cursor-pointer justify-center rounded-md border-2 border-dashed bg-slate-100 py-4',
)}>
<div className="space-y-1 text-center">
{resumeFile == null ? (
<ArrowUpCircleIcon className="text-primary-500 m-auto h-10 w-10" />
) : (
<p
className="hover:text-primary-600 cursor-pointer underline underline-offset-1"
onClick={onClickDownload}>
{resumeFile.name}
</p>
)}
<div className="flex items-center text-sm">
<label
className="focus-within:ring-primary-500 rounded-md focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2"
htmlFor="file-upload">
<span className="font-medium">Drop file here</span>
<span className="mr-1 ml-1 font-light">or</span>
<span className="text-primary-600 hover:text-primary-400 cursor-pointer font-medium">
{resumeFile == null
? 'Select file'
: 'Replace file'}
</span>
<input
{...register('file', { required: true })}
{...getInputProps()}
accept="application/pdf"
className="sr-only"
disabled={isLoading}
id="file-upload"
name="file-upload"
type="file"
/>
</label>
</div>
<p className="text-xs text-slate-500">
PDF up to {FILE_SIZE_LIMIT_MB}MB
</p>
)}
<div className="flex items-center text-sm">
<label
className="rounded-md focus-within:outline-none focus-within:ring-2 focus-within:ring-indigo-500 focus-within:ring-offset-2"
htmlFor="file-upload">
<span className="font-medium">Drop file here</span>
<span className="mr-1 ml-1 font-light">or</span>
<span className="cursor-pointer font-medium text-indigo-600 hover:text-indigo-400">
{resumeFile == null ? 'Select file' : 'Replace file'}
</span>
<input
{...register('file', { required: true })}
{...getInputProps()}
accept="application/pdf"
className="sr-only"
disabled={isLoading}
id="file-upload"
name="file-upload"
type="file"
/>
</label>
</div>
<p className="text-xs text-gray-500">
PDF up to {FILE_SIZE_LIMIT_MB}MB
</p>
</div>
{fileUploadError && (
<p className="text-danger-600 text-sm">{fileUploadError}</p>
)}
</div>
{fileUploadError && (
<p className="text-danger-600 text-sm">{fileUploadError}</p>
)}
</div>
)}
{/* Additional Info Section */}
<TextArea
{...(register('additionalInfo'), {})}
disabled={isLoading}
label="Additional Information"
placeholder={ADDITIONAL_INFO_PLACEHOLDER}
onChange={(val) => onValueChange('additionalInfo', val)}
/>
{/* Submission Guidelines */}
{isNewForm && (
<>
<SubmissionGuidelines />
<CheckboxInput
{...register('isChecked', { required: true })}
disabled={isLoading}
label="I have read and will follow the guidelines stated."
onChange={(val) => setValue('isChecked', val)}
/>
</>
)}
{/* Clear and Submit Buttons */}
<div className="flex justify-end gap-4">
<Button
addonPosition="start"
disabled={isLoading}
label={isNewForm ? 'Clear' : 'Cancel'}
variant="tertiary"
onClick={onClickClear}
/>
<Button
addonPosition="start"
)}
{/* Additional Info Section */}
<TextArea
{...(register('additionalInfo'),
{ defaultValue: initFormDetails?.additionalInfo })}
disabled={isLoading}
isLoading={isLoading}
label="Submit"
type="submit"
variant="primary"
label="Additional Information"
placeholder={ADDITIONAL_INFO_PLACEHOLDER}
onChange={(val) => onValueChange('additionalInfo', val)}
/>
</div>
</form>
</section>
</main>
{/* Submission Guidelines */}
{isNewForm && (
<>
<SubmissionGuidelines />
<CheckboxInput
{...register('isChecked', { required: true })}
disabled={isLoading}
label="I have read and will follow the guidelines stated."
onChange={(val) => setValue('isChecked', val)}
/>
</>
)}
{/* Clear and Submit Buttons */}
<div className="flex justify-end gap-4">
<Button
addonPosition="start"
disabled={isLoading}
label={isNewForm ? 'Clear' : 'Cancel'}
variant="tertiary"
onClick={onClickClear}
/>
<Button
addonPosition="start"
disabled={isLoading}
isLoading={isLoading}
label="Submit"
type="submit"
variant="primary"
/>
</div>
</form>
</section>
</main>
)}
</>
);
}

@ -5,12 +5,15 @@ import { useToast } from '@tih/ui';
import { HorizontalDivider } from '@tih/ui';
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
import type { Month, MonthYear } from '~/components/shared/MonthYearPicker';
import MonthYearPicker from '~/components/shared/MonthYearPicker';
export default function HomePage() {
const [selectedCompany, setSelectedCompany] =
useState<TypeaheadOption | null>(null);
const [selectedJobTitle, setSelectedJobTitle] =
useState<TypeaheadOption | null>(null);
const [monthYear, setMonthYear] = useState<MonthYear>({
month: (new Date().getMonth() + 1) as Month,
year: new Date().getFullYear(),
@ -30,6 +33,11 @@ export default function HomePage() {
/>
<pre>{JSON.stringify(selectedCompany, null, 2)}</pre>
<HorizontalDivider />
<JobTitlesTypeahead
onSelect={(option) => setSelectedJobTitle(option)}
/>
<pre>{JSON.stringify(selectedJobTitle, null, 2)}</pre>
<HorizontalDivider />
<MonthYearPicker value={monthYear} onChange={setMonthYear} />
<HorizontalDivider />
<Button

@ -34,14 +34,14 @@ export default function TodoList() {
<div className="mx-auto px-4 py-8 sm:px-6 lg:px-8">
<div className="sm:flex sm:items-center">
<div className="sm:flex-auto">
<h1 className="text-xl font-semibold text-gray-900">Todos</h1>
<p className="mt-2 text-sm text-gray-700">
<h1 className="text-xl font-semibold text-slate-900">Todos</h1>
<p className="mt-2 text-sm text-slate-700">
A list of all Todos added by everyone.
</p>
</div>
<div className="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<Link
className="inline-flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto"
className="bg-primary-600 hover:bg-primary-700 focus:ring-primary-500 inline-flex items-center justify-center rounded-md border border-transparent px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 sm:w-auto"
href="/todos/new">
Add Todo
</Link>
@ -54,40 +54,40 @@ export default function TodoList() {
<div className="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<table className="min-w-full divide-y divide-gray-300">
<thead className="bg-gray-50">
<tr className="divide-x divide-gray-200">
<table className="min-w-full divide-y divide-slate-300">
<thead className="bg-slate-50">
<tr className="divide-x divide-slate-200">
<th
className="py-3.5 pl-4 pr-4 text-left text-sm font-semibold text-gray-900 sm:pl-6"
className="py-3.5 pl-4 pr-4 text-left text-sm font-semibold text-slate-900 sm:pl-6"
scope="col">
Description
</th>
<th
className="px-4 py-3.5 text-left text-sm font-semibold text-gray-900"
className="px-4 py-3.5 text-left text-sm font-semibold text-slate-900"
scope="col">
Creator
</th>
<th
className="px-4 py-3.5 text-left text-sm font-semibold text-gray-900"
className="px-4 py-3.5 text-left text-sm font-semibold text-slate-900"
scope="col">
Last Updated
</th>
<th
className="py-3.5 pl-4 pr-4 text-left text-sm font-semibold text-gray-900 sm:pr-6"
className="py-3.5 pl-4 pr-4 text-left text-sm font-semibold text-slate-900 sm:pr-6"
scope="col">
Status
</th>
<th
className="py-3.5 pl-4 pr-4 text-left text-sm font-semibold text-gray-900 sm:pr-6"
className="py-3.5 pl-4 pr-4 text-left text-sm font-semibold text-slate-900 sm:pr-6"
scope="col">
Actions
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 bg-white">
<tbody className="divide-y divide-slate-200 bg-white">
{todosQuery.data?.map((todo) => (
<tr key={todo.id} className="divide-x divide-gray-200">
<td className="whitespace-nowrap py-4 pl-4 pr-4 text-sm text-gray-500 sm:pl-6">
<tr key={todo.id} className="divide-x divide-slate-200">
<td className="whitespace-nowrap py-4 pl-4 pr-4 text-sm text-slate-500 sm:pl-6">
{todo.id === currentlyEditingTodo ? (
<form
ref={formRef}
@ -120,7 +120,7 @@ export default function TodoList() {
}}>
<input
autoFocus={true}
className="block w-full min-w-0 flex-1 rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
className="focus:border-primary-500 focus:ring-primary-500 block w-full min-w-0 flex-1 rounded-md border-slate-300 sm:text-sm"
defaultValue={todo.text}
name="text"
type="text"
@ -130,19 +130,19 @@ export default function TodoList() {
todo.text
)}
</td>
<td className="whitespace-nowrap p-4 text-sm text-gray-500">
<td className="whitespace-nowrap p-4 text-sm text-slate-500">
{todo.user.name}
</td>
<td className="whitespace-nowrap p-4 text-sm text-gray-500">
<td className="whitespace-nowrap p-4 text-sm text-slate-500">
{todo.updatedAt.toLocaleString('en-US', {
dateStyle: 'long',
timeStyle: 'medium',
})}
</td>
<td className="whitespace-nowrap py-4 pl-4 pr-4 text-sm text-gray-500 sm:pr-6">
<td className="whitespace-nowrap py-4 pl-4 pr-4 text-sm text-slate-500 sm:pr-6">
<input
checked={todo.status === 'COMPLETE'}
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
className="text-primary-600 focus:ring-primary-500 h-4 w-4 rounded border-slate-300"
type="checkbox"
onChange={() => {
todoUpdateMutation.mutate({
@ -155,12 +155,12 @@ export default function TodoList() {
}}
/>
</td>
<td className="space-x-4 whitespace-nowrap py-4 pl-4 pr-4 text-sm text-gray-500 sm:pr-6">
<td className="space-x-4 whitespace-nowrap py-4 pl-4 pr-4 text-sm text-slate-500 sm:pr-6">
{data?.user?.id === todo.userId && (
<>
{currentlyEditingTodo === todo.id ? (
<a
className="text-indigo-600 hover:text-indigo-900"
className="text-primary-600 hover:text-primary-900"
href="#"
onClick={() => {
setCurrentlyEditingTodo(null);
@ -169,7 +169,7 @@ export default function TodoList() {
</a>
) : (
<a
className="text-indigo-600 hover:text-indigo-900"
className="text-primary-600 hover:text-primary-900"
href="#"
onClick={async () => {
setCurrentlyEditingTodo(todo.id);
@ -178,7 +178,7 @@ export default function TodoList() {
</a>
)}
<a
className="text-indigo-600 hover:text-indigo-900"
className="text-primary-600 hover:text-primary-900"
href="#"
onClick={async () => {
const confirmDelete = window.confirm(

@ -27,7 +27,7 @@ export default function TodosCreate() {
</h1>
<form
ref={formRef}
className="w-full space-y-8 divide-y divide-gray-200"
className="w-full space-y-8 divide-y divide-slate-200"
onSubmit={async (event) => {
event.preventDefault();
if (!formRef.current) {
@ -52,14 +52,14 @@ export default function TodosCreate() {
}}>
<div className="mt-6">
<label
className="block text-sm font-medium text-gray-700"
className="block text-sm font-medium text-slate-700"
htmlFor="text">
Text
</label>
<div className="mt-1 flex rounded-md shadow-sm">
<input
autoFocus={true}
className="block w-full min-w-0 flex-1 rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
className="focus:border-primary-500 focus:ring-primary-500 block w-full min-w-0 flex-1 rounded-md border-slate-300 sm:text-sm"
id="text"
name="text"
type="text"
@ -71,12 +71,12 @@ export default function TodosCreate() {
<div className="pt-5">
<div className="flex justify-end">
<Link
className="rounded-md border border-gray-300 bg-white py-2 px-4 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
className="focus:ring-primary-500 rounded-md border border-slate-300 bg-white py-2 px-4 text-sm font-medium text-slate-700 shadow-sm hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-offset-2"
href="/todos">
Cancel
</Link>
<button
className="ml-3 inline-flex justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
className="bg-primary-600 hover:bg-primary-700 focus:ring-primary-500 ml-3 inline-flex justify-center rounded-md border border-transparent py-2 px-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2"
type="submit">
Save
</button>

@ -707,7 +707,7 @@ export const offersProfileRouter = createRouter()
// Update existing experience
await ctx.prisma.offersExperience.update({
data: {
companyId: exp.companyId,
companyId: exp.companyId, // TODO: check if can change with connect or whether there is a difference
durationInMonths: exp.durationInMonths,
level: exp.level,
specialization: exp.specialization,
@ -718,45 +718,91 @@ export const offersProfileRouter = createRouter()
});
if (exp.monthlySalary) {
await ctx.prisma.offersCurrency.update({
data: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
exp.monthlySalary.value,
exp.monthlySalary.currency,
baseCurrencyString,
),
currency: exp.monthlySalary.currency,
value: exp.monthlySalary.value,
},
where: {
id: exp.monthlySalary.id,
},
});
if (exp.monthlySalary.id) {
await ctx.prisma.offersCurrency.update({
data: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
exp.monthlySalary.value,
exp.monthlySalary.currency,
baseCurrencyString,
),
currency: exp.monthlySalary.currency,
value: exp.monthlySalary.value,
},
where: {
id: exp.monthlySalary.id,
},
});
} else {
await ctx.prisma.offersExperience.update({
data: {
monthlySalary: {
create: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
exp.monthlySalary.value,
exp.monthlySalary.currency,
baseCurrencyString,
),
currency: exp.monthlySalary.currency,
value: exp.monthlySalary.value,
},
},
},
where: {
id: exp.id,
},
});
}
}
if (exp.totalCompensation) {
await ctx.prisma.offersCurrency.update({
data: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
exp.totalCompensation.value,
exp.totalCompensation.currency,
baseCurrencyString,
),
currency: exp.totalCompensation.currency,
value: exp.totalCompensation.value,
},
where: {
id: exp.totalCompensation.id,
},
});
if (exp.totalCompensation.id) {
await ctx.prisma.offersCurrency.update({
data: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
exp.totalCompensation.value,
exp.totalCompensation.currency,
baseCurrencyString,
),
currency: exp.totalCompensation.currency,
value: exp.totalCompensation.value,
},
where: {
id: exp.totalCompensation.id,
},
});
} else {
await ctx.prisma.offersExperience.update({
data: {
totalCompensation: {
create: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
exp.totalCompensation.value,
exp.totalCompensation.currency,
baseCurrencyString,
),
currency: exp.totalCompensation.currency,
value: exp.totalCompensation.value,
},
},
},
where: {
id: exp.id,
},
});
}
}
} else if (!exp.id) {
// Create new experience
if (exp.jobType === JobType.FULLTIME) {
if (exp.totalCompensation?.currency != null &&
exp.totalCompensation?.value != null) {
if (
exp.totalCompensation?.currency != null &&
exp.totalCompensation?.value != null
) {
if (exp.companyId) {
await ctx.prisma.offersBackground.update({
data: {
@ -866,8 +912,10 @@ export const offersProfileRouter = createRouter()
});
}
} else if (exp.jobType === JobType.INTERN) {
if (exp.monthlySalary?.currency != null &&
exp.monthlySalary?.value != null) {
if (
exp.monthlySalary?.currency != null &&
exp.monthlySalary?.value != null
) {
if (exp.companyId) {
await ctx.prisma.offersBackground.update({
data: {

@ -5,8 +5,8 @@ import {
dashboardOfferDtoMapper,
getOffersResponseMapper,
} from '~/mappers/offers-mappers';
import { convertWithDate } from '~/utils/offers/currency/currencyExchange';
import { Currency } from '~/utils/offers/currency/CurrencyEnum';
import { convertWithDate } from '~/utils/offers/currency/currencyExchange';
import { createValidationRegex } from '~/utils/offers/zodRegex';
import { createRouter } from '../context';

@ -27,7 +27,7 @@ export const questionsAnswerCommentRouter = createProtectedRouter()
createdAt: 'desc',
},
where: {
answerId : input.answerId,
answerId: input.answerId,
},
});
return questionAnswerCommentsData.map((data) => {

@ -15,6 +15,7 @@ export const resumesRouter = createRouter()
searchValue: z.string(),
skip: z.number(),
sortOrder: z.string(),
take: z.number(),
}),
async resolve({ ctx, input }) {
const {
@ -25,6 +26,7 @@ export const resumesRouter = createRouter()
numComments,
skip,
searchValue,
take,
} = input;
const userId = ctx.session?.user?.id;
const totalRecords = await ctx.prisma.resumesResume.count({
@ -37,6 +39,7 @@ export const resumesRouter = createRouter()
experience: { in: experienceFilters },
location: { in: locationFilters },
role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' },
},
});
const resumesData = await ctx.prisma.resumesResume.findMany({
@ -74,7 +77,7 @@ export const resumesRouter = createRouter()
}
: { comments: { _count: 'desc' } },
skip,
take: 10,
take,
where: {
...(numComments === 0 && {
comments: {

@ -53,6 +53,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
searchValue: z.string(),
skip: z.number(),
sortOrder: z.string(),
take: z.number(),
}),
async resolve({ ctx, input }) {
const userId = ctx.session.user.id;
@ -64,6 +65,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
sortOrder,
numComments,
skip,
take,
} = input;
const totalRecords = await ctx.prisma.resumesStar.count({
where: {
@ -76,6 +78,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
experience: { in: experienceFilters },
location: { in: locationFilters },
role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' },
},
userId,
},
@ -121,7 +124,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
},
},
skip,
take: 10,
take,
where: {
resume: {
...(numComments === 0 && {
@ -167,6 +170,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
searchValue: z.string(),
skip: z.number(),
sortOrder: z.string(),
take: z.number(),
}),
async resolve({ ctx, input }) {
const userId = ctx.session.user.id;
@ -177,6 +181,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
sortOrder,
searchValue,
numComments,
take,
skip,
} = input;
const totalRecords = await ctx.prisma.resumesResume.count({
@ -189,6 +194,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
experience: { in: experienceFilters },
location: { in: locationFilters },
role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' },
userId,
},
});
@ -224,7 +230,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
}
: { comments: { _count: 'desc' } },
skip,
take: 10,
take,
where: {
...(numComments === 0 && {
comments: {

@ -1,170 +1,169 @@
// eslint-disable-next-line no-shadow
export enum Currency {
AED = "AED", // 'UNITED ARAB EMIRATES DIRHAM'
AFN = "AFN", // 'AFGHAN AFGHANI'
ALL = "ALL", // 'ALBANIAN LEK'
AMD = "AMD", // 'ARMENIAN DRAM'
ANG = "ANG", // 'NETHERLANDS ANTILLEAN GUILDER'
AOA = "AOA", // 'ANGOLAN KWANZA'
ARS = "ARS", // 'ARGENTINE PESO'
AUD = "AUD", // 'AUSTRALIAN DOLLAR'
AWG = "AWG", // 'ARUBAN FLORIN'
AZN = "AZN", // 'AZERBAIJANI MANAT'
BAM = "BAM", // 'BOSNIA-HERZEGOVINA CONVERTIBLE MARK'
BBD = "BBD", // 'BAJAN DOLLAR'
BDT = "BDT", // 'BANGLADESHI TAKA'
BGN = "BGN", // 'BULGARIAN LEV'
BHD = "BHD", // 'BAHRAINI DINAR'
BIF = "BIF", // 'BURUNDIAN FRANC'
BMD = "BMD", // 'BERMUDAN DOLLAR'
BND = "BND", // 'BRUNEI DOLLAR'
BOB = "BOB", // 'BOLIVIAN BOLIVIANO'
BRL = "BRL", // 'BRAZILIAN REAL'
BSD = "BSD", // 'BAHAMIAN DOLLAR'
BTN = "BTN", // 'BHUTAN CURRENCY'
BWP = "BWP", // 'BOTSWANAN PULA'
BYN = "BYN", // 'NEW BELARUSIAN RUBLE'
BYR = "BYR", // 'BELARUSIAN RUBLE'
BZD = "BZD", // 'BELIZE DOLLAR'
CAD = "CAD", // 'CANADIAN DOLLAR'
CDF = "CDF", // 'CONGOLESE FRANC'
CHF = "CHF", // 'SWISS FRANC'
CLF = "CLF", // 'CHILEAN UNIT OF ACCOUNT (UF)'
CLP = "CLP", // 'CHILEAN PESO'
CNY = "CNY", // 'CHINESE YUAN'
COP = "COP", // 'COLOMBIAN PESO'
CRC = "CRC", // 'COSTA RICAN COLÓN'
CUC = "CUC", // 'CUBAN CONVERTIBLE PESO'
CUP = "CUP", // 'CUBAN PESO'
CVE = "CVE", // 'CAPE VERDEAN ESCUDO'
CVX = "CVX", // 'CONVEX FINANCE'
CZK = "CZK", // 'CZECH KORUNA'
DJF = "DJF", // 'DJIBOUTIAN FRANC'
DKK = "DKK", // 'DANISH KRONE'
DOP = "DOP", // 'DOMINICAN PESO'
DZD = "DZD", // 'ALGERIAN DINAR'
EGP = "EGP", // 'EGYPTIAN POUND'
ERN = "ERN", // 'ERITREAN NAKFA'
ETB = "ETB", // 'ETHIOPIAN BIRR'
ETC = "ETC", // 'ETHEREUM CLASSIC'
EUR = "EUR", // 'EURO'
FEI = "FEI", // 'FEI USD'
FJD = "FJD", // 'FIJIAN DOLLAR'
FKP = "FKP", // 'FALKLAND ISLANDS POUND'
GBP = "GBP", // 'POUND STERLING'
GEL = "GEL", // 'GEORGIAN LARI'
GHS = "GHS", // 'GHANAIAN CEDI'
GIP = "GIP", // 'GIBRALTAR POUND'
GMD = "GMD", // 'GAMBIAN DALASI'
GNF = "GNF", // 'GUINEAN FRANC'
GTQ = "GTQ", // 'GUATEMALAN QUETZAL'
GYD = "GYD", // 'GUYANAESE DOLLAR'
HKD = "HKD", // 'HONG KONG DOLLAR'
HNL = "HNL", // 'HONDURAN LEMPIRA'
HRK = "HRK", // 'CROATIAN KUNA'
HTG = "HTG", // 'HAITIAN GOURDE'
HUF = "HUF", // 'HUNGARIAN FORINT'
ICP = "ICP", // 'INTERNET COMPUTER'
IDR = "IDR", // 'INDONESIAN RUPIAH'
ILS = "ILS", // 'ISRAELI NEW SHEKEL'
INR = "INR", // 'INDIAN RUPEE'
IQD = "IQD", // 'IRAQI DINAR'
IRR = "IRR", // 'IRANIAN RIAL'
ISK = "ISK", // 'ICELANDIC KRÓNA'
JEP = "JEP", // 'JERSEY POUND'
JMD = "JMD", // 'JAMAICAN DOLLAR'
JOD = "JOD", // 'JORDANIAN DINAR'
JPY = "JPY", // 'JAPANESE YEN'
KES = "KES", // 'KENYAN SHILLING'
KGS = "KGS", // 'KYRGYSTANI SOM'
KHR = "KHR", // 'CAMBODIAN RIEL'
KMF = "KMF", // 'COMORIAN FRANC'
KPW = "KPW", // 'NORTH KOREAN WON'
KRW = "KRW", // 'SOUTH KOREAN WON'
KWD = "KWD", // 'KUWAITI DINAR'
KYD = "KYD", // 'CAYMAN ISLANDS DOLLAR'
KZT = "KZT", // 'KAZAKHSTANI TENGE'
LAK = "LAK", // 'LAOTIAN KIP'
LBP = "LPB", // 'LEBANESE POUND'
LKR = "LKR", // 'SRI LANKAN RUPEE'
LRD = "LRD", // 'LIBERIAN DOLLAR'
LSL = "LSL", // 'LESOTHO LOTI'
LTL = "LTL", // 'LITHUANIAN LITAS'
LVL = "LVL", // 'LATVIAN LATS'
LYD = "LYD", // 'LIBYAN DINAR'
MAD = "MAD", // 'MOROCCAN DIRHAM'
MDL = "MDL", // 'MOLDOVAN LEU'
MGA = "MGA", // 'MALAGASY ARIARY'
MKD = "MKD", // 'MACEDONIAN DENAR'
MMK = "MMK", // 'MYANMAR KYAT'
MNT = "MNT", // 'MONGOLIAN TUGRIK'
MOP = "MOP", // 'MACANESE PATACA'
MRO = "MRO", // 'MAURITANIAN OUGUIYA'
MUR = "MUR", // 'MAURITIAN RUPEE'
MVR = "MVR", // 'MALDIVIAN RUFIYAA'
MWK = "MWK", // 'MALAWIAN KWACHA'
MXN = "MXN", // 'MEXICAN PESO'
MYR = "MYR", // 'MALAYSIAN RINGGIT'
MZN = "MZN", // 'MOZAMBICAN METICAL'
NAD = "NAD", // 'NAMIBIAN DOLLAR'
NGN = "NGN", // 'NIGERIAN NAIRA'
NIO = "NIO", // 'NICARAGUAN CÓRDOBA'
NOK = "NOK", // 'NORWEGIAN KRONE'
NPR = "NPR", // 'NEPALESE RUPEE'
NZD = "NZD", // 'NEW ZEALAND DOLLAR'
OMR = "OMR", // 'OMANI RIAL'
ONE = "ONE", // 'MENLO ONE'
PAB = "PAB", // 'PANAMANIAN BALBOA'
PGK = "PGK", // 'PAPUA NEW GUINEAN KINA'
PHP = "PHP", // 'PHILIPPINE PESO'
PKR = "PKR", // 'PAKISTANI RUPEE'
PLN = "PLN", // 'POLAND ZŁOTY'
PYG = "PYG", // 'PARAGUAYAN GUARANI'
QAR = "QAR", // 'QATARI RIAL'
RON = "RON", // 'ROMANIAN LEU'
RSD = "RSD", // 'SERBIAN DINAR'
RUB = "RUB", // 'RUSSIAN RUBLE'
RWF = "RWF", // 'RWANDAN FRANC'
SAR = "SAR", // 'SAUDI RIYAL'
SBD = "SBD", // 'SOLOMON ISLANDS DOLLAR'
SCR = "SCR", // 'SEYCHELLOIS RUPEE'
SDG = "SDG", // 'SUDANESE POUND'
SEK = "SEK", // 'SWEDISH KRONA'
SGD = "SGD", // 'SINGAPORE DOLLAR'
SHIB = "SHIB", // 'SHIBA INU'
SHP = "SHP", // 'SAINT HELENA POUND'
SLL = "SLL", // 'SIERRA LEONEAN LEONE'
SOS = "SOS", // 'SOMALI SHILLING'
SRD = "SRD", // 'SURINAMESE DOLLAR'
STD = "STD", // 'SÃO TOMÉ AND PRÍNCIPE DOBRA (PRE-2018)'
SVC = "SVC", // 'SALVADORAN COLÓN'
SYP = "SYP", // 'SYRIAN POUND'
SZL = "SZL", // 'SWAZI LILANGENI'
THB = "THB", // 'THAI BAHT'
TJS = "TJS", // 'TAJIKISTANI SOMONI'
TMT = "TMT", // 'TURKMENISTANI MANAT'
TND = "TND", // 'TUNISIAN DINAR'
TOP = "TOP", // "TONGAN PA'ANGA"
TRY = "TRY", // 'TURKISH LIRA'
TTD = "TTD", // 'TRINIDAD & TOBAGO DOLLAR'
TWD = "TWD", // 'NEW TAIWAN DOLLAR'
TZS = "TZS", // 'TANZANIAN SHILLING'
UAH = "UAH", // 'UKRAINIAN HRYVNIA'
UGX = "UGX", // 'UGANDAN SHILLING'
USD = "USD", // 'UNITED STATES DOLLAR'
UYU = "UYU", // 'URUGUAYAN PESO'
UZS = "UZS", // 'UZBEKISTANI SOM'
VND = "VND", // 'VIETNAMESE DONG'
VUV = "VUV", // 'VANUATU VATU'
WST = "WST", // 'SAMOAN TALA'
XAF = "XAF", // 'CENTRAL AFRICAN CFA FRANC'
XCD = "XCD", // 'EAST CARIBBEAN DOLLAR'
XOF = "XOF", // 'WEST AFRICAN CFA FRANC'
XPF = "XPF", // 'CFP FRANC'
YER = "YER", // 'YEMENI RIAL'
ZAR = "ZAR", // 'SOUTH AFRICAN RAND'
ZMW = "ZMW", // 'ZAMBIAN KWACHA'
ZWL = "ZWL", // 'ZIMBABWEAN DOLLAR'
export enum Currency {
AED = 'AED', // 'UNITED ARAB EMIRATES DIRHAM'
AFN = 'AFN', // 'AFGHAN AFGHANI'
ALL = 'ALL', // 'ALBANIAN LEK'
AMD = 'AMD', // 'ARMENIAN DRAM'
ANG = 'ANG', // 'NETHERLANDS ANTILLEAN GUILDER'
AOA = 'AOA', // 'ANGOLAN KWANZA'
ARS = 'ARS', // 'ARGENTINE PESO'
AUD = 'AUD', // 'AUSTRALIAN DOLLAR'
AWG = 'AWG', // 'ARUBAN FLORIN'
AZN = 'AZN', // 'AZERBAIJANI MANAT'
BAM = 'BAM', // 'BOSNIA-HERZEGOVINA CONVERTIBLE MARK'
BBD = 'BBD', // 'BAJAN DOLLAR'
BDT = 'BDT', // 'BANGLADESHI TAKA'
BGN = 'BGN', // 'BULGARIAN LEV'
BHD = 'BHD', // 'BAHRAINI DINAR'
BIF = 'BIF', // 'BURUNDIAN FRANC'
BMD = 'BMD', // 'BERMUDAN DOLLAR'
BND = 'BND', // 'BRUNEI DOLLAR'
BOB = 'BOB', // 'BOLIVIAN BOLIVIANO'
BRL = 'BRL', // 'BRAZILIAN REAL'
BSD = 'BSD', // 'BAHAMIAN DOLLAR'
BTN = 'BTN', // 'BHUTAN CURRENCY'
BWP = 'BWP', // 'BOTSWANAN PULA'
BYN = 'BYN', // 'NEW BELARUSIAN RUBLE'
BYR = 'BYR', // 'BELARUSIAN RUBLE'
BZD = 'BZD', // 'BELIZE DOLLAR'
CAD = 'CAD', // 'CANADIAN DOLLAR'
CDF = 'CDF', // 'CONGOLESE FRANC'
CHF = 'CHF', // 'SWISS FRANC'
CLF = 'CLF', // 'CHILEAN UNIT OF ACCOUNT (UF)'
CLP = 'CLP', // 'CHILEAN PESO'
CNY = 'CNY', // 'CHINESE YUAN'
COP = 'COP', // 'COLOMBIAN PESO'
CRC = 'CRC', // 'COSTA RICAN COLÓN'
CUC = 'CUC', // 'CUBAN CONVERTIBLE PESO'
CUP = 'CUP', // 'CUBAN PESO'
CVE = 'CVE', // 'CAPE VERDEAN ESCUDO'
CVX = 'CVX', // 'CONVEX FINANCE'
CZK = 'CZK', // 'CZECH KORUNA'
DJF = 'DJF', // 'DJIBOUTIAN FRANC'
DKK = 'DKK', // 'DANISH KRONE'
DOP = 'DOP', // 'DOMINICAN PESO'
DZD = 'DZD', // 'ALGERIAN DINAR'
EGP = 'EGP', // 'EGYPTIAN POUND'
ERN = 'ERN', // 'ERITREAN NAKFA'
ETB = 'ETB', // 'ETHIOPIAN BIRR'
ETC = 'ETC', // 'ETHEREUM CLASSIC'
EUR = 'EUR', // 'EURO'
FEI = 'FEI', // 'FEI USD'
FJD = 'FJD', // 'FIJIAN DOLLAR'
FKP = 'FKP', // 'FALKLAND ISLANDS POUND'
GBP = 'GBP', // 'POUND STERLING'
GEL = 'GEL', // 'GEORGIAN LARI'
GHS = 'GHS', // 'GHANAIAN CEDI'
GIP = 'GIP', // 'GIBRALTAR POUND'
GMD = 'GMD', // 'GAMBIAN DALASI'
GNF = 'GNF', // 'GUINEAN FRANC'
GTQ = 'GTQ', // 'GUATEMALAN QUETZAL'
GYD = 'GYD', // 'GUYANAESE DOLLAR'
HKD = 'HKD', // 'HONG KONG DOLLAR'
HNL = 'HNL', // 'HONDURAN LEMPIRA'
HRK = 'HRK', // 'CROATIAN KUNA'
HTG = 'HTG', // 'HAITIAN GOURDE'
HUF = 'HUF', // 'HUNGARIAN FORINT'
ICP = 'ICP', // 'INTERNET COMPUTER'
IDR = 'IDR', // 'INDONESIAN RUPIAH'
ILS = 'ILS', // 'ISRAELI NEW SHEKEL'
INR = 'INR', // 'INDIAN RUPEE'
IQD = 'IQD', // 'IRAQI DINAR'
IRR = 'IRR', // 'IRANIAN RIAL'
ISK = 'ISK', // 'ICELANDIC KRÓNA'
JEP = 'JEP', // 'JERSEY POUND'
JMD = 'JMD', // 'JAMAICAN DOLLAR'
JOD = 'JOD', // 'JORDANIAN DINAR'
JPY = 'JPY', // 'JAPANESE YEN'
KES = 'KES', // 'KENYAN SHILLING'
KGS = 'KGS', // 'KYRGYSTANI SOM'
KHR = 'KHR', // 'CAMBODIAN RIEL'
KMF = 'KMF', // 'COMORIAN FRANC'
KPW = 'KPW', // 'NORTH KOREAN WON'
KRW = 'KRW', // 'SOUTH KOREAN WON'
KWD = 'KWD', // 'KUWAITI DINAR'
KYD = 'KYD', // 'CAYMAN ISLANDS DOLLAR'
KZT = 'KZT', // 'KAZAKHSTANI TENGE'
LAK = 'LAK', // 'LAOTIAN KIP'
LBP = 'LPB', // 'LEBANESE POUND'
LKR = 'LKR', // 'SRI LANKAN RUPEE'
LRD = 'LRD', // 'LIBERIAN DOLLAR'
LSL = 'LSL', // 'LESOTHO LOTI'
LTL = 'LTL', // 'LITHUANIAN LITAS'
LVL = 'LVL', // 'LATVIAN LATS'
LYD = 'LYD', // 'LIBYAN DINAR'
MAD = 'MAD', // 'MOROCCAN DIRHAM'
MDL = 'MDL', // 'MOLDOVAN LEU'
MGA = 'MGA', // 'MALAGASY ARIARY'
MKD = 'MKD', // 'MACEDONIAN DENAR'
MMK = 'MMK', // 'MYANMAR KYAT'
MNT = 'MNT', // 'MONGOLIAN TUGRIK'
MOP = 'MOP', // 'MACANESE PATACA'
MRO = 'MRO', // 'MAURITANIAN OUGUIYA'
MUR = 'MUR', // 'MAURITIAN RUPEE'
MVR = 'MVR', // 'MALDIVIAN RUFIYAA'
MWK = 'MWK', // 'MALAWIAN KWACHA'
MXN = 'MXN', // 'MEXICAN PESO'
MYR = 'MYR', // 'MALAYSIAN RINGGIT'
MZN = 'MZN', // 'MOZAMBICAN METICAL'
NAD = 'NAD', // 'NAMIBIAN DOLLAR'
NGN = 'NGN', // 'NIGERIAN NAIRA'
NIO = 'NIO', // 'NICARAGUAN CÓRDOBA'
NOK = 'NOK', // 'NORWEGIAN KRONE'
NPR = 'NPR', // 'NEPALESE RUPEE'
NZD = 'NZD', // 'NEW ZEALAND DOLLAR'
OMR = 'OMR', // 'OMANI RIAL'
ONE = 'ONE', // 'MENLO ONE'
PAB = 'PAB', // 'PANAMANIAN BALBOA'
PGK = 'PGK', // 'PAPUA NEW GUINEAN KINA'
PHP = 'PHP', // 'PHILIPPINE PESO'
PKR = 'PKR', // 'PAKISTANI RUPEE'
PLN = 'PLN', // 'POLAND ZŁOTY'
PYG = 'PYG', // 'PARAGUAYAN GUARANI'
QAR = 'QAR', // 'QATARI RIAL'
RON = 'RON', // 'ROMANIAN LEU'
RSD = 'RSD', // 'SERBIAN DINAR'
RUB = 'RUB', // 'RUSSIAN RUBLE'
RWF = 'RWF', // 'RWANDAN FRANC'
SAR = 'SAR', // 'SAUDI RIYAL'
SBD = 'SBD', // 'SOLOMON ISLANDS DOLLAR'
SCR = 'SCR', // 'SEYCHELLOIS RUPEE'
SDG = 'SDG', // 'SUDANESE POUND'
SEK = 'SEK', // 'SWEDISH KRONA'
SGD = 'SGD', // 'SINGAPORE DOLLAR'
SHP = 'SHP', // 'SAINT HELENA POUND'
SLL = 'SLL', // 'SIERRA LEONEAN LEONE'
SOS = 'SOS', // 'SOMALI SHILLING'
SRD = 'SRD', // 'SURINAMESE DOLLAR'
STD = 'STD', // 'SÃO TOMÉ AND PRÍNCIPE DOBRA (PRE-2018)'
SVC = 'SVC', // 'SALVADORAN COLÓN'
SYP = 'SYP', // 'SYRIAN POUND'
SZL = 'SZL', // 'SWAZI LILANGENI'
THB = 'THB', // 'THAI BAHT'
TJS = 'TJS', // 'TAJIKISTANI SOMONI'
TMT = 'TMT', // 'TURKMENISTANI MANAT'
TND = 'TND', // 'TUNISIAN DINAR'
TOP = 'TOP', // "TONGAN PA'ANGA"
TRY = 'TRY', // 'TURKISH LIRA'
TTD = 'TTD', // 'TRINIDAD & TOBAGO DOLLAR'
TWD = 'TWD', // 'NEW TAIWAN DOLLAR'
TZS = 'TZS', // 'TANZANIAN SHILLING'
UAH = 'UAH', // 'UKRAINIAN HRYVNIA'
UGX = 'UGX', // 'UGANDAN SHILLING'
USD = 'USD', // 'UNITED STATES DOLLAR'
UYU = 'UYU', // 'URUGUAYAN PESO'
UZS = 'UZS', // 'UZBEKISTANI SOM'
VND = 'VND', // 'VIETNAMESE DONG'
VUV = 'VUV', // 'VANUATU VATU'
WST = 'WST', // 'SAMOAN TALA'
XAF = 'XAF', // 'CENTRAL AFRICAN CFA FRANC'
XCD = 'XCD', // 'EAST CARIBBEAN DOLLAR'
XOF = 'XOF', // 'WEST AFRICAN CFA FRANC'
XPF = 'XPF', // 'CFP FRANC'
YER = 'YER', // 'YEMENI RIAL'
ZAR = 'ZAR', // 'SOUTH AFRICAN RAND'
ZMW = 'ZMW', // 'ZAMBIAN KWACHA'
ZWL = 'ZWL', // 'ZIMBABWEAN DOLLAR'
}
export const CURRENCY_OPTIONS = Object.entries(Currency).map(

@ -3,7 +3,6 @@ export function getProfileLink(profileId: string, token?: string) {
}
export function copyProfileLink(profileId: string, token?: string) {
// TODO: Add notification
navigator.clipboard.writeText(getProfileLink(profileId, token));
}

@ -69,6 +69,7 @@ export default function Pagination({
pageNumberSet.add(page);
elements.push(
<PaginationPage
key={page}
isCurrent={current === page}
label={page}
onClick={(event) => {
@ -83,7 +84,7 @@ export default function Pagination({
addPage(i);
}
if (lastAddedPage < current - pagePadding) {
if (lastAddedPage < current - pagePadding - 1) {
elements.push(<PaginationEllipsis />);
}
@ -91,7 +92,7 @@ export default function Pagination({
addPage(i);
}
if (lastAddedPage < end - pagePadding) {
if (lastAddedPage < end - pagePadding - 1) {
elements.push(<PaginationEllipsis />);
}

@ -88,7 +88,7 @@ function Select<T>(
aria-label={isLabelHidden ? label : undefined}
className={clsx(
display === 'block' && 'block w-full',
'rounded-md py-2 pl-3 pr-8 text-base focus:outline-none sm:text-sm',
'rounded-md py-2 pl-3 pr-8 text-sm focus:outline-none',
stateClasses[state],
borderClasses[borderStyle],
disabled && 'bg-slate-100',

@ -108,7 +108,7 @@ function TextArea(
aria-describedby={hasError ? errorId : undefined}
aria-invalid={hasError ? true : undefined}
className={clsx(
'block w-full rounded-md sm:text-sm',
'block w-full rounded-md text-sm',
stateClasses[state].textArea,
disabled && 'bg-slate-100',
resizeClasses[resize],

@ -142,7 +142,7 @@ function TextInput(
</label>
<div
className={clsx(
'flex w-full overflow-hidden rounded-md border focus-within:ring-1 sm:text-sm',
'flex w-full overflow-hidden rounded-md border text-sm focus-within:ring-1',
disabled && 'pointer-events-none select-none bg-slate-100',
containerClass,
)}>
@ -178,7 +178,7 @@ function TextInput(
aria-describedby={hasError ? errorId : undefined}
aria-invalid={hasError ? true : undefined}
className={clsx(
'flex-1 border-none focus:outline-none focus:ring-0 sm:text-sm',
'w-0 flex-1 border-none text-sm focus:outline-none focus:ring-0',
inputClass,
disabled && 'bg-transparent',
)}

@ -88,7 +88,7 @@ export default function Typeahead({
)}
</Combobox.Label>
<div className="relative">
<div className="focus-visible:ring-offset-primary-300 relative w-full cursor-default overflow-hidden rounded-lg border border-slate-300 bg-white text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 sm:text-sm">
<div className="focus-visible:ring-offset-primary-300 relative w-full cursor-default overflow-hidden rounded-lg border border-slate-300 bg-white text-left text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2">
<Combobox.Input
className={clsx(
'w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-slate-900 focus:ring-0',
@ -117,7 +117,7 @@ export default function Typeahead({
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0">
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
<Combobox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
{options.length === 0 && query !== '' ? (
<div className="relative cursor-default select-none py-2 px-4 text-slate-700">
{noResultsMessage}

Loading…
Cancel
Save