[resumes][fix] add resolve resume functionality (#442)

* [resumes][fix] button getting cut off

* [resumes][feat] add resolve functionality

* [resumes][feat] replace student roles with internship

* [resumes][feat] fetch isResolved field
This commit is contained in:
Keane Chan
2022-10-27 16:08:01 +08:00
committed by GitHub
parent e3620faafe
commit 410d8712c3
8 changed files with 83 additions and 32 deletions

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "ResumesResume" ADD COLUMN "isResolved" BOOLEAN NOT NULL DEFAULT false;

View File

@ -120,6 +120,7 @@ model ResumesResume {
location String @db.Text location String @db.Text
url String url String
additionalInfo String? @db.Text additionalInfo String? @db.Text
isResolved Boolean @default(false)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)

View File

@ -9,6 +9,7 @@ import {
AcademicCapIcon, AcademicCapIcon,
BriefcaseIcon, BriefcaseIcon,
CalendarIcon, CalendarIcon,
CheckCircleIcon,
InformationCircleIcon, InformationCircleIcon,
MapPinIcon, MapPinIcon,
PencilSquareIcon, PencilSquareIcon,
@ -57,24 +58,33 @@ export default function ResumeReviewPage() {
); );
const starMutation = trpc.useMutation('resumes.resume.star', { const starMutation = trpc.useMutation('resumes.resume.star', {
onSuccess() { onSuccess() {
utils.invalidateQueries(['resumes.resume.findOne']); invalidateResumeQueries();
utils.invalidateQueries(['resumes.resume.findAll']);
utils.invalidateQueries(['resumes.resume.user.findUserStarred']);
utils.invalidateQueries(['resumes.resume.user.findUserCreated']);
}, },
}); });
const unstarMutation = trpc.useMutation('resumes.resume.unstar', { const unstarMutation = trpc.useMutation('resumes.resume.unstar', {
onSuccess() { onSuccess() {
invalidateResumeQueries();
},
});
const resolveMutation = trpc.useMutation('resumes.resume.user.resolve', {
onSuccess() {
invalidateResumeQueries();
},
});
const invalidateResumeQueries = () => {
utils.invalidateQueries(['resumes.resume.findOne']); utils.invalidateQueries(['resumes.resume.findOne']);
utils.invalidateQueries(['resumes.resume.findAll']); utils.invalidateQueries(['resumes.resume.findAll']);
utils.invalidateQueries(['resumes.resume.user.findUserStarred']); utils.invalidateQueries(['resumes.resume.user.findUserStarred']);
utils.invalidateQueries(['resumes.resume.user.findUserCreated']); utils.invalidateQueries(['resumes.resume.user.findUserCreated']);
}, };
});
const userIsOwner = const userIsOwner =
session?.user?.id !== undefined && session?.user?.id !== undefined &&
session.user.id === detailsQuery.data?.userId; session.user.id === detailsQuery.data?.userId;
const isResumeResolved = detailsQuery.data?.isResolved;
const [isEditMode, setIsEditMode] = useState(false); const [isEditMode, setIsEditMode] = useState(false);
const [showCommentsForm, setShowCommentsForm] = useState(false); const [showCommentsForm, setShowCommentsForm] = useState(false);
@ -139,6 +149,13 @@ export default function ResumeReviewPage() {
setIsEditMode(true); setIsEditMode(true);
}; };
const onResolveButtonClick = () => {
resolveMutation.mutate({
id: resumeId as string,
val: !isResumeResolved,
});
};
const renderReviewButton = () => { const renderReviewButton = () => {
if (session === null) { if (session === null) {
return ( return (
@ -175,10 +192,7 @@ export default function ResumeReviewPage() {
url: detailsQuery.data.url, url: detailsQuery.data.url,
}} }}
onClose={() => { onClose={() => {
utils.invalidateQueries(['resumes.resume.findOne']); invalidateResumeQueries();
utils.invalidateQueries(['resumes.resume.findAll']);
utils.invalidateQueries(['resumes.resume.user.findUserStarred']);
utils.invalidateQueries(['resumes.resume.user.findUserCreated']);
setIsEditMode(false); setIsEditMode(false);
}} }}
/> />
@ -206,6 +220,7 @@ export default function ResumeReviewPage() {
</h1> </h1>
<div className="flex gap-3 xl:pr-4"> <div className="flex gap-3 xl:pr-4">
{userIsOwner && ( {userIsOwner && (
<>
<Button <Button
addonPosition="start" addonPosition="start"
className="h-10 shadow-md" className="h-10 shadow-md"
@ -214,9 +229,27 @@ export default function ResumeReviewPage() {
variant="tertiary" variant="tertiary"
onClick={onEditButtonClick} onClick={onEditButtonClick}
/> />
<button
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-md hover:bg-slate-50 focus:ring-slate-600 disabled:hover:bg-white"
disabled={resolveMutation.isLoading}
type="button"
onClick={onResolveButtonClick}>
<div className="-ml-1 mr-2 h-5 w-5">
{resolveMutation.isLoading ? (
<Spinner className="mt-0.5" size="xs" />
) : (
<CheckCircleIcon
aria-hidden="true"
className={isResumeResolved ? '' : 'text-slate-400'}
/>
)}
</div>
{isResumeResolved ? 'Resolved' : 'Resolve'}
</button>
</>
)} )}
<button <button
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-md hover:bg-slate-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-md hover:bg-slate-50 focus:ring-slate-600 disabled:hover:bg-white"
disabled={starMutation.isLoading || unstarMutation.isLoading} disabled={starMutation.isLoading || unstarMutation.isLoading}
type="button" type="button"
onClick={onStarButtonClick}> onClick={onStarButtonClick}>

View File

@ -542,7 +542,7 @@ export default function ResumeHomePage() {
/> />
</div> </div>
<Button <Button
className="lg:hidden" className="whitespace-pre-wrap px-2 lg:hidden"
label="Submit Resume" label="Submit Resume"
variant="primary" variant="primary"
onClick={onSubmitResume} onClick={onSubmitResume}

View File

@ -96,6 +96,7 @@ export const resumesRouter = createRouter()
createdAt: r.createdAt, createdAt: r.createdAt,
experience: r.experience, experience: r.experience,
id: r.id, id: r.id,
isResolved: r.isResolved,
isStarredByUser: r.stars.length > 0, isStarredByUser: r.stars.length > 0,
location: r.location, location: r.location,
numComments: r._count.comments, numComments: r._count.comments,

View File

@ -44,6 +44,23 @@ export const resumesResumeUserRouter = createProtectedRouter()
}); });
}, },
}) })
.mutation('resolve', {
input: z.object({
id: z.string(),
val: z.boolean(),
}),
async resolve({ ctx, input }) {
const resume = await ctx.prisma.resumesResume.update({
data: {
isResolved: input.val,
},
where: {
id: input.id,
},
});
return resume.isResolved;
},
})
.query('findUserStarred', { .query('findUserStarred', {
input: z.object({ input: z.object({
experienceFilters: z.string().array(), experienceFilters: z.string().array(),
@ -147,6 +164,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
createdAt: rs.resume.createdAt, createdAt: rs.resume.createdAt,
experience: rs.resume.experience, experience: rs.resume.experience,
id: rs.resume.id, id: rs.resume.id,
isResolved: rs.resume.isResolved,
isStarredByUser: true, isStarredByUser: true,
location: rs.resume.location, location: rs.resume.location,
numComments: rs.resume._count.comments, numComments: rs.resume._count.comments,
@ -250,6 +268,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
createdAt: r.createdAt, createdAt: r.createdAt,
experience: r.experience, experience: r.experience,
id: r.id, id: r.id,
isResolved: r.isResolved,
isStarredByUser: r.stars.length > 0, isStarredByUser: r.stars.length > 0,
location: r.location, location: r.location,
numComments: r._count.comments, numComments: r._count.comments,

View File

@ -3,6 +3,7 @@ export type Resume = {
createdAt: Date; createdAt: Date;
experience: string; experience: string;
id: string; id: string;
isResolved: boolean;
isStarredByUser: boolean; isStarredByUser: boolean;
location: string; location: string;
numComments: number; numComments: number;

View File

@ -14,12 +14,9 @@ export type RoleFilter =
export type ExperienceFilter = export type ExperienceFilter =
| 'Entry Level (0 - 2 years)' | 'Entry Level (0 - 2 years)'
| 'Freshman' | 'Internship'
| 'Junior'
| 'Mid Level (3 - 5 years)' | 'Mid Level (3 - 5 years)'
| 'Senior Level (5+ years)' | 'Senior Level (5+ years)';
| 'Senior'
| 'Sophomore';
export type LocationFilter = 'India' | 'Singapore' | 'United States'; export type LocationFilter = 'India' | 'Singapore' | 'United States';
@ -79,10 +76,7 @@ export const ROLES: Array<FilterOption<RoleFilter>> = [
]; ];
export const EXPERIENCES: Array<FilterOption<ExperienceFilter>> = [ export const EXPERIENCES: Array<FilterOption<ExperienceFilter>> = [
{ label: 'Freshman', value: 'Freshman' }, { label: 'Internship', value: 'Internship' },
{ label: 'Sophomore', value: 'Sophomore' },
{ label: 'Junior', value: 'Junior' },
{ label: 'Senior', value: 'Senior' },
{ {
label: 'Entry Level (0 - 2 years)', label: 'Entry Level (0 - 2 years)',
value: 'Entry Level (0 - 2 years)', value: 'Entry Level (0 - 2 years)',
@ -133,7 +127,7 @@ export const SHORTCUTS: Array<Shortcut> = [
}, },
{ {
filters: INITIAL_FILTER_STATE, filters: INITIAL_FILTER_STATE,
name: 'GOATs', name: 'Top 10',
sortOrder: 'popular', sortOrder: 'popular',
}, },
{ {