mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-28 20:52:00 +08:00
[offers][feat] add token check to profile and add company filter to table (#373)
* [offers][feat] add token check to profile, company filter to table and currency formatter * [offers][feat] add currency formatter
This commit is contained in:
@ -14,16 +14,16 @@ type Props = Readonly<{
|
||||
|
||||
export default function OfferCard({
|
||||
offer: {
|
||||
companyName = 'Meta',
|
||||
jobTitle = 'Senior Engineer',
|
||||
jobLevel,
|
||||
location = 'Singapore',
|
||||
receivedMonth = 'Jun 2021',
|
||||
totalCompensation = '350.1k',
|
||||
base = '0k',
|
||||
stocks = '0k',
|
||||
bonus = '0k',
|
||||
base,
|
||||
bonus,
|
||||
companyName,
|
||||
duration,
|
||||
jobTitle,
|
||||
jobLevel,
|
||||
location,
|
||||
receivedMonth,
|
||||
totalCompensation,
|
||||
stocks,
|
||||
monthlySalary,
|
||||
negotiationStrategy,
|
||||
otherComment,
|
||||
|
@ -19,7 +19,10 @@ export type OffersTableProps = Readonly<{
|
||||
companyFilter: string;
|
||||
jobTitleFilter: string;
|
||||
}>;
|
||||
export default function OffersTable({ jobTitleFilter }: OffersTableProps) {
|
||||
export default function OffersTable({
|
||||
companyFilter,
|
||||
jobTitleFilter,
|
||||
}: OffersTableProps) {
|
||||
const [currency, setCurrency] = useState('SGD'); // TODO: Detect location
|
||||
const [selectedTab, setSelectedTab] = useState(YOE_CATEGORY.ENTRY);
|
||||
const [pagination, setPagination] = useState<PaginationType>({
|
||||
@ -42,10 +45,9 @@ export default function OffersTable({ jobTitleFilter }: OffersTableProps) {
|
||||
[
|
||||
'offers.list',
|
||||
{
|
||||
// Company: companyFilter, // TODO
|
||||
companyId: companyFilter,
|
||||
limit: NUMBER_OF_OFFERS_IN_PAGE,
|
||||
|
||||
location: 'Singapore, Singapore',
|
||||
location: 'Singapore, Singapore', // TODO: Geolocation
|
||||
offset: pagination.currentPage - 1,
|
||||
sortBy: '-monthYearReceived',
|
||||
title: jobTitleFilter,
|
||||
|
@ -137,11 +137,11 @@ type EducationDisplay = {
|
||||
export type OfferEntity = {
|
||||
base?: string;
|
||||
bonus?: string;
|
||||
companyName: string;
|
||||
companyName?: string;
|
||||
duration?: string;
|
||||
id?: string;
|
||||
jobLevel?: string;
|
||||
jobTitle: string;
|
||||
jobTitle?: string;
|
||||
location?: string;
|
||||
monthlySalary?: string;
|
||||
negotiationStrategy?: string;
|
||||
|
@ -7,7 +7,7 @@ import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
|
||||
|
||||
export default function OffersHomePage() {
|
||||
const [jobTitleFilter, setjobTitleFilter] = useState('Software Engineer');
|
||||
const [companyFilter, setCompanyFilter] = useState('All companies');
|
||||
const [companyFilter, setCompanyFilter] = useState('');
|
||||
|
||||
return (
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
|
@ -8,6 +8,7 @@ import ProfileHeader from '~/components/offers/profile/ProfileHeader';
|
||||
import type { OfferEntity } from '~/components/offers/types';
|
||||
import type { BackgroundCard } from '~/components/offers/types';
|
||||
|
||||
import { convertCurrencyToString } from '~/utils/offers/currency';
|
||||
import { formatDate } from '~/utils/offers/time';
|
||||
import { trpc } from '~/utils/trpc';
|
||||
export default function OfferProfile() {
|
||||
@ -33,58 +34,57 @@ export default function OfferProfile() {
|
||||
if (!data) {
|
||||
router.push('/offers');
|
||||
}
|
||||
// If the profile is not editable with a wrong token, redirect to the profile page
|
||||
if (!data?.isEditable && token !== '') {
|
||||
router.push(`/offers/profile/${offerProfileId}`);
|
||||
}
|
||||
|
||||
setIsEditable(data?.isEditable ?? false);
|
||||
|
||||
const filteredOffers: Array<OfferEntity> = data
|
||||
? data?.offers.map((res) => {
|
||||
if (res.OffersFullTime) {
|
||||
if (data?.offers) {
|
||||
const filteredOffers: Array<OfferEntity> = data
|
||||
? data?.offers.map((res) => {
|
||||
if (res.OffersFullTime) {
|
||||
const filteredOffer: OfferEntity = {
|
||||
base: convertCurrencyToString(
|
||||
res.OffersFullTime.baseSalary.value,
|
||||
),
|
||||
bonus: convertCurrencyToString(
|
||||
res.OffersFullTime.bonus.value,
|
||||
),
|
||||
companyName: res.company.name,
|
||||
id: res.OffersFullTime.id,
|
||||
jobLevel: res.OffersFullTime.level,
|
||||
jobTitle: res.OffersFullTime.title,
|
||||
location: res.location,
|
||||
negotiationStrategy: res.negotiationStrategy || '',
|
||||
otherComment: res.comments || '',
|
||||
receivedMonth: formatDate(res.monthYearReceived),
|
||||
stocks: convertCurrencyToString(res.OffersFullTime.stocks),
|
||||
totalCompensation: convertCurrencyToString(
|
||||
res.OffersFullTime.totalCompensation,
|
||||
),
|
||||
};
|
||||
|
||||
return filteredOffer;
|
||||
}
|
||||
const filteredOffer: OfferEntity = {
|
||||
base: res.OffersFullTime.baseSalary.value
|
||||
? `${res.OffersFullTime.baseSalary.value} ${res.OffersFullTime.baseSalary.currency}`
|
||||
: '',
|
||||
bonus: res.OffersFullTime.bonus.value
|
||||
? `${res.OffersFullTime.bonus.value} ${res.OffersFullTime.bonus.currency}`
|
||||
: '',
|
||||
companyName: res.company.name,
|
||||
id: res.OffersFullTime.id,
|
||||
jobLevel: res.OffersFullTime.level,
|
||||
jobTitle: res.OffersFullTime.title,
|
||||
id: res.OffersIntern!.id,
|
||||
jobTitle: res.OffersIntern!.title,
|
||||
location: res.location,
|
||||
monthlySalary: convertCurrencyToString(
|
||||
res.OffersIntern!.monthlySalary,
|
||||
),
|
||||
negotiationStrategy: res.negotiationStrategy || '',
|
||||
otherComment: res.comments || '',
|
||||
receivedMonth: formatDate(res.monthYearReceived),
|
||||
stocks: res.OffersFullTime.stocks.value
|
||||
? `${res.OffersFullTime.stocks.value} ${res.OffersFullTime.stocks.currency}`
|
||||
: '',
|
||||
totalCompensation: res.OffersFullTime.totalCompensation.value
|
||||
? `${res.OffersFullTime.totalCompensation.value} ${res.OffersFullTime.totalCompensation.currency}`
|
||||
: '',
|
||||
};
|
||||
|
||||
return filteredOffer;
|
||||
}
|
||||
const filteredOffer: OfferEntity = {
|
||||
companyName: res.company.name,
|
||||
id: res.OffersIntern!.id,
|
||||
jobTitle: res.OffersIntern!.title,
|
||||
location: res.location,
|
||||
monthlySalary: res.OffersIntern!.monthlySalary.value
|
||||
? `${res.OffersIntern!.monthlySalary.value} ${
|
||||
res.OffersIntern!.monthlySalary.currency
|
||||
}`
|
||||
: '',
|
||||
negotiationStrategy: res.negotiationStrategy || '',
|
||||
otherComment: res.comments || '',
|
||||
receivedMonth: formatDate(res.monthYearReceived),
|
||||
};
|
||||
return filteredOffer;
|
||||
})
|
||||
: [];
|
||||
setOffers(filteredOffers);
|
||||
})
|
||||
: [];
|
||||
setOffers(filteredOffers);
|
||||
}
|
||||
|
||||
if (data?.background) {
|
||||
const transformedBackground = {
|
||||
@ -102,23 +102,29 @@ export default function OfferProfile() {
|
||||
},
|
||||
],
|
||||
experiences: [
|
||||
{
|
||||
companyName:
|
||||
data.background.experiences[0].company?.name ?? '-',
|
||||
duration:
|
||||
String(data.background.experiences[0].durationInMonths) ??
|
||||
'-',
|
||||
jobLevel: data.background.experiences[0].level ?? '',
|
||||
jobTitle: data.background.experiences[0].title ?? '-',
|
||||
monthlySalary: data.background.experiences[0].monthlySalary
|
||||
?.value
|
||||
? `${data.background.experiences[0].monthlySalary?.value} ${data.background.experiences[0].monthlySalary?.currency}`
|
||||
: `-`,
|
||||
totalCompensation: data.background.experiences[0]
|
||||
.totalCompensation?.value
|
||||
? `${data.background.experiences[0].totalCompensation?.value} ${data.background.experiences[0].totalCompensation?.currency}`
|
||||
: ``,
|
||||
},
|
||||
data.background.experiences &&
|
||||
data.background.experiences.length > 0
|
||||
? {
|
||||
companyName:
|
||||
data.background.experiences[0].company?.name ?? '-',
|
||||
duration:
|
||||
String(data.background.experiences[0].durationInMonths) ??
|
||||
'-',
|
||||
jobLevel: data.background.experiences[0].level ?? '',
|
||||
jobTitle: data.background.experiences[0].title ?? '-',
|
||||
monthlySalary: data.background.experiences[0].monthlySalary
|
||||
? convertCurrencyToString(
|
||||
data.background.experiences[0].monthlySalary,
|
||||
)
|
||||
: '-',
|
||||
totalCompensation: data.background.experiences[0]
|
||||
.totalCompensation
|
||||
? convertCurrencyToString(
|
||||
data.background.experiences[0].totalCompensation,
|
||||
)
|
||||
: '-',
|
||||
}
|
||||
: {},
|
||||
],
|
||||
profileName: data.profileName,
|
||||
specificYoes: data.background.specificYoes ?? [],
|
||||
@ -131,16 +137,22 @@ export default function OfferProfile() {
|
||||
);
|
||||
|
||||
const trpcContext = trpc.useContext();
|
||||
const deleteMutation = trpc.useMutation(['offers.profile.delete']);
|
||||
const deleteMutation = trpc.useMutation(['offers.profile.delete'], {
|
||||
onError: () => {
|
||||
alert('Error deleting profile'); // TODO: replace with toast
|
||||
},
|
||||
onSuccess: () => {
|
||||
trpcContext.invalidateQueries(['offers.profile.listOne']);
|
||||
router.push('/offers');
|
||||
},
|
||||
});
|
||||
|
||||
function handleDelete() {
|
||||
if (isEditable) {
|
||||
deleteMutation.mutate({
|
||||
profileId: offerProfileId as string,
|
||||
token: 'CHANGE THIS PART TO URL PARAM @ ZIQING', // TODO: token: token as string,
|
||||
token: token as string,
|
||||
});
|
||||
trpcContext.invalidateQueries(['offers.profile.listOne']);
|
||||
router.push('/offers');
|
||||
}
|
||||
}
|
||||
|
||||
|
14
apps/portal/src/utils/offers/currency/index.tsx
Normal file
14
apps/portal/src/utils/offers/currency/index.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import type { Money } from '~/components/offers/types';
|
||||
|
||||
export function convertCurrencyToString({ currency, value }: Money) {
|
||||
if (!value) {
|
||||
return '-';
|
||||
}
|
||||
const formatter = new Intl.NumberFormat('en-US', {
|
||||
currency,
|
||||
maximumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1)
|
||||
minimumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501)
|
||||
style: 'currency',
|
||||
});
|
||||
return `${formatter.format(10000)}`; /* $2,500.00 */
|
||||
}
|
Reference in New Issue
Block a user