* main: [resumes][refactor] Update resume review page UI (#418) [resumes][fix] fix unauthenticated issue on submit form [resumes][fix] Fix browse page scrolling UI (#421) [resumes][feat] update submit page [portal] add required field for companies typeahead [portal] add job titles typeahead [misc] prettify files [portal] standardize colors [ui][typeahead] fix results showing below other stacked elements by adding z-index [ui][text input] fix input add on disappearing when width is too small [ui] change to text-sm for some elements [offers][fix] Remove crypto coin from currencies [offers][fix] fix edit profile endpoint [offers][fix] remove dark theme for table (#420) [resumes][fix] search and pagination bugs (#419) [offers][feat] Add toast (#417) [offers][feat] Add analysis to offers profile page (#416) [resumes][feat] hide scrollbar [resumes][feat] remove resume title, clean up submit form (#415) [resumes][feat] update submit form to be more compact (#414) # Conflicts: # apps/portal/src/server/router/offers/offers-analysis-router.tspull/501/head^2
commit
51ae657d28
@ -0,0 +1,29 @@
|
||||
import { OVERALL_TAB } from '../constants';
|
||||
|
||||
import type { Analysis } from '~/types/offers';
|
||||
|
||||
type OfferPercentileAnalysisTextProps = Readonly<{
|
||||
companyName: string;
|
||||
offerAnalysis: Analysis;
|
||||
tab: string;
|
||||
}>;
|
||||
|
||||
export default function OfferPercentileAnalysisText({
|
||||
tab,
|
||||
companyName,
|
||||
offerAnalysis: { noOfOffers, percentile },
|
||||
}: OfferPercentileAnalysisTextProps) {
|
||||
return tab === OVERALL_TAB ? (
|
||||
<p>
|
||||
Your highest offer is from <b>{companyName}</b>, which is{' '}
|
||||
<b>{percentile.toFixed(1)}</b> percentile out of <b>{noOfOffers}</b>{' '}
|
||||
offers received for the same job title and YOE(±1) in the last year.
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
Your offer from <b>{companyName}</b> is <b>{percentile.toFixed(1)}</b>{' '}
|
||||
percentile out of <b>{noOfOffers}</b> offers received in {companyName} for
|
||||
the same job title and YOE(±1) in the last year.
|
||||
</p>
|
||||
);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import type { Analysis } from '~/types/offers';
|
||||
|
||||
type OfferPercentileAnalysisTextProps = Readonly<{
|
||||
companyName: string;
|
||||
offerAnalysis: Analysis;
|
||||
tab: string;
|
||||
}>;
|
||||
|
||||
export default function OfferPercentileAnalysisText({
|
||||
tab,
|
||||
companyName,
|
||||
offerAnalysis: { noOfOffers, percentile },
|
||||
}: OfferPercentileAnalysisTextProps) {
|
||||
return tab === 'Overall' ? (
|
||||
<p>
|
||||
Your highest offer is from <b>{companyName}</b>, which is{' '}
|
||||
<b>{percentile}</b> percentile out of <b>{noOfOffers}</b> offers received
|
||||
for the same job title and YOE(+/-1) in the last year.
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
Your offer from <b>{companyName}</b> is <b>{percentile}</b> percentile out
|
||||
of <b>{noOfOffers}</b> offers received in {companyName} for the same job
|
||||
title and YOE(+/-1) in the last year.
|
||||
</p>
|
||||
);
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import { Badge } from '@tih/ui';
|
||||
|
||||
export default function ResumeReviewsTitle() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="mb-1 text-2xl font-bold">Resume Reviews</h1>
|
||||
<Badge
|
||||
label="Check out reviewed resumes or look for resumes to review"
|
||||
variant="info"
|
||||
/>
|
||||
</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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
Before Width: | Height: | Size: 652 KiB After Width: | Height: | Size: 586 KiB |
Before Width: | Height: | Size: 1006 KiB After Width: | Height: | Size: 909 KiB |
Before Width: | Height: | Size: 437 KiB After Width: | Height: | Size: 396 KiB |
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
Loading…
Reference in new issue