diff --git a/apps/portal/src/mappers/offers-mappers.ts b/apps/portal/src/mappers/offers-mappers.ts
index ea0f624c..f8b0839f 100644
--- a/apps/portal/src/mappers/offers-mappers.ts
+++ b/apps/portal/src/mappers/offers-mappers.ts
@@ -56,7 +56,7 @@ const analysisOfferDtoMapper = (
location: offer.location,
monthYearReceived: offer.monthYearReceived,
negotiationStrategy: offer.negotiationStrategy,
- previousCompanies: [],
+ previousCompanies: [], // TODO: Fill this up
profileName,
specialization:
offer.jobType === JobType.FULLTIME
@@ -74,10 +74,18 @@ const analysisOfferDtoMapper = (
offer.offersFullTime.totalCompensation.value;
analysisOfferDto.income.currency =
offer.offersFullTime.totalCompensation.currency;
+ analysisOfferDto.income.baseValue =
+ offer.offersFullTime.totalCompensation.baseValue;
+ analysisOfferDto.income.baseCurrency =
+ offer.offersFullTime.totalCompensation.baseCurrency;
} else if (offer.offersIntern?.monthlySalary) {
analysisOfferDto.income.value = offer.offersIntern.monthlySalary.value;
analysisOfferDto.income.currency =
offer.offersIntern.monthlySalary.currency;
+ analysisOfferDto.income.baseValue =
+ offer.offersIntern.monthlySalary.baseValue;
+ analysisOfferDto.income.baseCurrency =
+ offer.offersIntern.monthlySalary.baseCurrency;
} else {
throw new TRPCError({
code: 'NOT_FOUND',
diff --git a/apps/portal/src/pages/offers/test/generateAnalysis.tsx b/apps/portal/src/pages/offers/test/generateAnalysis.tsx
index dc1fc18c..029ab5fe 100644
--- a/apps/portal/src/pages/offers/test/generateAnalysis.tsx
+++ b/apps/portal/src/pages/offers/test/generateAnalysis.tsx
@@ -8,7 +8,7 @@ function GenerateAnalysis() {
return (
{JSON.stringify(
- analysisMutation.mutate({ profileId: 'cl9h23fb1002ftxysli5iziu2' }),
+ analysisMutation.mutate({ profileId: 'cl9j50xzk008vutfqg6mta2ey' }),
)}
);
diff --git a/apps/portal/src/pages/offers/test/getAnalysis.tsx b/apps/portal/src/pages/offers/test/getAnalysis.tsx
index 4f29ddf7..477ee183 100644
--- a/apps/portal/src/pages/offers/test/getAnalysis.tsx
+++ b/apps/portal/src/pages/offers/test/getAnalysis.tsx
@@ -5,7 +5,7 @@ import { trpc } from '~/utils/trpc';
function GetAnalysis() {
const analysis = trpc.useQuery([
'offers.analysis.get',
- { profileId: 'cl9h23fb1002ftxysli5iziu2' },
+ { profileId: 'cl9j50xzk008vutfqg6mta2ey' },
]);
return {JSON.stringify(analysis.data)}
;
diff --git a/apps/portal/src/pages/offers/test/listOffers.tsx b/apps/portal/src/pages/offers/test/listOffers.tsx
index 747f5102..b59f50c6 100644
--- a/apps/portal/src/pages/offers/test/listOffers.tsx
+++ b/apps/portal/src/pages/offers/test/listOffers.tsx
@@ -6,6 +6,7 @@ function Test() {
const data = trpc.useQuery([
'offers.list',
{
+ currency: 'SGD',
limit: 100,
location: 'Singapore, Singapore',
offset: 0,
diff --git a/apps/portal/src/server/router/offers/offers-analysis-router.ts b/apps/portal/src/server/router/offers/offers-analysis-router.ts
index 37b0d83b..35bb9924 100644
--- a/apps/portal/src/server/router/offers/offers-analysis-router.ts
+++ b/apps/portal/src/server/router/offers/offers-analysis-router.ts
@@ -187,14 +187,14 @@ export const offersAnalysisRouter = createRouter()
{
offersFullTime: {
totalCompensation: {
- value: 'desc',
+ baseValue: 'desc',
},
},
},
{
offersIntern: {
monthlySalary: {
- value: 'desc',
+ baseValue: 'desc',
},
},
},
@@ -216,11 +216,11 @@ export const offersAnalysisRouter = createRouter()
// TODO: Shift yoe out of background to make it mandatory
if (
!overallHighestOffer.profile.background ||
- overallHighestOffer.profile.background.totalYoe === undefined
+ overallHighestOffer.profile.background.totalYoe == null
) {
throw new TRPCError({
- code: 'BAD_REQUEST',
- message: 'Cannot analyse without YOE',
+ code: 'NOT_FOUND',
+ message: 'YOE not found',
});
}
@@ -257,14 +257,14 @@ export const offersAnalysisRouter = createRouter()
{
offersFullTime: {
totalCompensation: {
- value: 'desc',
+ baseValue: 'desc',
},
},
},
{
offersIntern: {
monthlySalary: {
- value: 'desc',
+ baseValue: 'desc',
},
},
},
@@ -279,12 +279,10 @@ export const offersAnalysisRouter = createRouter()
{
offersFullTime: {
level: overallHighestOffer.offersFullTime?.level,
- specialization:
- overallHighestOffer.offersFullTime?.specialization,
+ title: overallHighestOffer.offersFullTime?.title,
},
offersIntern: {
- specialization:
- overallHighestOffer.offersIntern?.specialization,
+ title: overallHighestOffer.offersIntern?.title,
},
},
],
@@ -317,7 +315,7 @@ export const offersAnalysisRouter = createRouter()
similarOffers,
);
const overallPercentile =
- similarOffers.length === 0 ? 0 : overallIndex / similarOffers.length;
+ similarOffers.length === 0 ? 1.0 : overallIndex / similarOffers.length;
const companyIndex = searchOfferPercentile(
overallHighestOffer,
@@ -325,10 +323,11 @@ export const offersAnalysisRouter = createRouter()
);
const companyPercentile =
similarCompanyOffers.length === 0
- ? 0
+ ? 1.0
: companyIndex / similarCompanyOffers.length;
- // FIND TOP >=90 PERCENTILE OFFERS
+ // FIND TOP >=90 PERCENTILE OFFERS, DOESN'T GIVE 100th PERCENTILE
+ // e.g. If there only 4 offers, it gives the 2nd and 3rd offer
similarOffers = similarOffers.filter(
(offer) => offer.id !== overallHighestOffer.id,
);
@@ -337,10 +336,9 @@ export const offersAnalysisRouter = createRouter()
);
const noOfSimilarOffers = similarOffers.length;
- const similarOffers90PercentileIndex =
- Math.floor(noOfSimilarOffers * 0.9) - 1;
+ const similarOffers90PercentileIndex = Math.ceil(noOfSimilarOffers * 0.1);
const topPercentileOffers =
- noOfSimilarOffers > 1
+ noOfSimilarOffers > 2
? similarOffers.slice(
similarOffers90PercentileIndex,
similarOffers90PercentileIndex + 2,
@@ -348,10 +346,11 @@ export const offersAnalysisRouter = createRouter()
: similarOffers;
const noOfSimilarCompanyOffers = similarCompanyOffers.length;
- const similarCompanyOffers90PercentileIndex =
- Math.floor(noOfSimilarCompanyOffers * 0.9) - 1;
+ const similarCompanyOffers90PercentileIndex = Math.ceil(
+ noOfSimilarCompanyOffers * 0.1,
+ );
const topPercentileCompanyOffers =
- noOfSimilarCompanyOffers > 1
+ noOfSimilarCompanyOffers > 2
? similarCompanyOffers.slice(
similarCompanyOffers90PercentileIndex,
similarCompanyOffers90PercentileIndex + 2,
diff --git a/apps/portal/src/server/router/offers/offers.ts b/apps/portal/src/server/router/offers/offers.ts
index a68b19dd..284063e9 100644
--- a/apps/portal/src/server/router/offers/offers.ts
+++ b/apps/portal/src/server/router/offers/offers.ts
@@ -5,7 +5,7 @@ import {
dashboardOfferDtoMapper,
getOffersResponseMapper,
} from '~/mappers/offers-mappers';
-import { convert } from '~/utils/offers/currency/currency-exchange';
+import { convertWithDate } from '~/utils/offers/currency/currency-exchange';
import { Currency } from '~/utils/offers/currency/CurrencyEnum';
import { createValidationRegex } from '~/utils/offers/zodRegex';
@@ -106,7 +106,7 @@ export const offersRouter = createRouter().query('list', {
? {
offersIntern: {
monthlySalary: {
- value: order,
+ baseValue: order,
},
},
}
@@ -141,7 +141,7 @@ export const offersRouter = createRouter().query('list', {
{
offersIntern: {
monthlySalary: {
- value: {
+ baseValue: {
gte: input.salaryMin ?? undefined,
lte: input.salaryMax ?? undefined,
},
@@ -210,7 +210,7 @@ export const offersRouter = createRouter().query('list', {
? {
offersFullTime: {
totalCompensation: {
- value: order,
+ baseValue: order,
},
},
}
@@ -250,7 +250,7 @@ export const offersRouter = createRouter().query('list', {
{
offersFullTime: {
totalCompensation: {
- value: {
+ baseValue: {
gte: input.salaryMin ?? undefined,
lte: input.salaryMax ?? undefined,
},
@@ -288,36 +288,42 @@ export const offersRouter = createRouter().query('list', {
if (currency != null && currency in Currency) {
data = await Promise.all(
data.map(async (offer) => {
- if (offer.offersFullTime?.totalCompensation) {
- offer.offersFullTime.totalCompensation.value = await convert(
- offer.offersFullTime.totalCompensation.value,
- offer.offersFullTime.totalCompensation.currency,
- currency,
- );
+ if (offer.offersFullTime?.totalCompensation != null) {
+ offer.offersFullTime.totalCompensation.value =
+ await convertWithDate(
+ offer.offersFullTime.totalCompensation.value,
+ offer.offersFullTime.totalCompensation.currency,
+ currency,
+ offer.offersFullTime.totalCompensation.updatedAt,
+ );
offer.offersFullTime.totalCompensation.currency = currency;
- offer.offersFullTime.baseSalary.value = await convert(
- offer.offersFullTime.totalCompensation.value,
- offer.offersFullTime.totalCompensation.currency,
+ offer.offersFullTime.baseSalary.value = await convertWithDate(
+ offer.offersFullTime.baseSalary.value,
+ offer.offersFullTime.baseSalary.currency,
currency,
+ offer.offersFullTime.baseSalary.updatedAt,
);
offer.offersFullTime.baseSalary.currency = currency;
- offer.offersFullTime.stocks.value = await convert(
- offer.offersFullTime.totalCompensation.value,
- offer.offersFullTime.totalCompensation.currency,
+ offer.offersFullTime.stocks.value = await convertWithDate(
+ offer.offersFullTime.stocks.value,
+ offer.offersFullTime.stocks.currency,
currency,
+ offer.offersFullTime.stocks.updatedAt,
);
offer.offersFullTime.stocks.currency = currency;
- offer.offersFullTime.bonus.value = await convert(
- offer.offersFullTime.totalCompensation.value,
- offer.offersFullTime.totalCompensation.currency,
+ offer.offersFullTime.bonus.value = await convertWithDate(
+ offer.offersFullTime.bonus.value,
+ offer.offersFullTime.bonus.currency,
currency,
+ offer.offersFullTime.bonus.updatedAt,
);
offer.offersFullTime.bonus.currency = currency;
- } else if (offer.offersIntern?.monthlySalary) {
- offer.offersIntern.monthlySalary.value = await convert(
+ } else if (offer.offersIntern?.monthlySalary != null) {
+ offer.offersIntern.monthlySalary.value = await convertWithDate(
offer.offersIntern.monthlySalary.value,
offer.offersIntern.monthlySalary.currency,
currency,
+ offer.offersIntern.monthlySalary.updatedAt,
);
offer.offersIntern.monthlySalary.currency = currency;
} else {
diff --git a/apps/portal/src/utils/offers/currency/currency-exchange.ts b/apps/portal/src/utils/offers/currency/currency-exchange.ts
index 4c94209a..0f642100 100644
--- a/apps/portal/src/utils/offers/currency/currency-exchange.ts
+++ b/apps/portal/src/utils/offers/currency/currency-exchange.ts
@@ -1,4 +1,5 @@
// API from https://github.com/fawazahmed0/currency-api#readme
+
export const convert = async (
value: number,
fromCurrency: string,
@@ -16,3 +17,33 @@ export const convert = async (
.then((res) => res.json())
.then((data) => value * data[toCurrency]);
};
+// https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@{apiVersion}/{date}/{endpoint}
+
+export const convertWithDate = async (
+ value: number,
+ fromCurrency: string,
+ toCurrency: string,
+ date: Date,
+) => {
+ if (new Date().toDateString === date.toDateString) {
+ return await convert(value, fromCurrency, toCurrency);
+ }
+
+ fromCurrency = fromCurrency.trim().toLowerCase();
+ toCurrency = toCurrency.trim().toLowerCase();
+
+ // Format date to YYYY-MM-DD
+ const formattedDate = date.toJSON().substring(0, 10);
+
+ const url = [
+ 'https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1',
+ formattedDate,
+ 'currencies',
+ fromCurrency,
+ toCurrency,
+ ].join('/');
+
+ return await fetch(url + '.json')
+ .then((res) => res.json())
+ .then((data) => value * data[toCurrency]);
+};