@@ -58,7 +80,7 @@ export default function OffersHomePage() {
if (option) {
setCompanyFilter(option.value);
gaEvent({
- action: 'offers.table_filter_company',
+ action: `offers.table_filter_company_${option.value}`,
category: 'engagement',
label: 'Filter by company',
});
@@ -70,6 +92,7 @@ export default function OffersHomePage() {
diff --git a/apps/portal/src/pages/offers/profile/edit/[offerProfileId].tsx b/apps/portal/src/pages/offers/profile/edit/[offerProfileId].tsx
index 05e47b7c..829acb28 100644
--- a/apps/portal/src/pages/offers/profile/edit/[offerProfileId].tsx
+++ b/apps/portal/src/pages/offers/profile/edit/[offerProfileId].tsx
@@ -36,13 +36,14 @@ export default function OffersEditPage() {
experiences.length === 0
? [{ jobType: JobType.FULLTIME }]
: experiences.map((exp) => ({
+ cityId: exp.location?.cityId,
+ cityName: exp.location?.cityName,
companyId: exp.company?.id,
companyName: exp.company?.name,
durationInMonths: exp.durationInMonths,
id: exp.id,
jobType: exp.jobType,
level: exp.level,
- location: exp.location,
monthlySalary: exp.monthlySalary,
title: exp.title,
totalCompensation: exp.totalCompensation,
@@ -53,12 +54,13 @@ export default function OffersEditPage() {
},
id: data.id,
offers: data.offers.map((offer) => ({
+ cityId: offer.location.cityId,
+ cityName: offer.location.cityName,
comments: offer.comments,
companyId: offer.company.id,
companyName: offer.company.name,
id: offer.id,
jobType: offer.jobType,
- location: offer.location,
monthYearReceived: convertToMonthYear(offer.monthYearReceived),
negotiationStrategy: offer.negotiationStrategy,
offersFullTime: offer.offersFullTime,
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 55689f54..0c084763 100644
--- a/apps/portal/src/server/router/offers/offers-analysis-router.ts
+++ b/apps/portal/src/server/router/offers/offers-analysis-router.ts
@@ -1,4 +1,19 @@
import { z } from 'zod';
+<<<<<<< HEAD
+=======
+import type {
+ City,
+ Company,
+ Country,
+ OffersBackground,
+ OffersCurrency,
+ OffersFullTime,
+ OffersIntern,
+ OffersOffer,
+ OffersProfile,
+ State,
+} from '@prisma/client';
+>>>>>>> BryannYeap/location
import { TRPCError } from '@trpc/server';
import { generateAnalysis } from '~/utils/offers/analysisGeneration';
@@ -19,6 +34,15 @@ export const offersAnalysisRouter = createRouter()
topSimilarOffers: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
offersFullTime: {
include: {
totalCompensation: true,
@@ -36,6 +60,15 @@ export const offersAnalysisRouter = createRouter()
experiences: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
},
},
},
@@ -51,6 +84,15 @@ export const offersAnalysisRouter = createRouter()
topSimilarOffers: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
offersFullTime: {
include: {
totalCompensation: true,
@@ -68,6 +110,15 @@ export const offersAnalysisRouter = createRouter()
experiences: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
},
},
},
@@ -81,6 +132,15 @@ export const offersAnalysisRouter = createRouter()
overallHighestOffer: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
offersFullTime: {
include: {
totalCompensation: true,
diff --git a/apps/portal/src/server/router/offers/offers-profile-router.ts b/apps/portal/src/server/router/offers/offers-profile-router.ts
index 42c15eb3..8617da46 100644
--- a/apps/portal/src/server/router/offers/offers-profile-router.ts
+++ b/apps/portal/src/server/router/offers/offers-profile-router.ts
@@ -35,12 +35,12 @@ const company = z.object({
});
const offer = z.object({
+ cityId: z.string(),
comments: z.string(),
company: company.nullish(),
companyId: z.string(),
id: z.string().optional(),
jobType: z.string().regex(createValidationRegex(Object.keys(JobType), null)),
- location: z.string(),
monthYearReceived: z.date(),
negotiationStrategy: z.string(),
offersFullTime: z
@@ -75,6 +75,7 @@ const offer = z.object({
const experience = z.object({
backgroundId: z.string().nullish(),
+ cityId: z.string().nullish(),
company: company.nullish(),
companyId: z.string().nullish(),
durationInMonths: z.number().nullish(),
@@ -84,7 +85,6 @@ const experience = z.object({
.regex(createValidationRegex(Object.keys(JobType), null))
.nullish(),
level: z.string().nullish(),
- location: z.string().nullish(),
monthlySalary: valuation.nullish(),
monthlySalaryId: z.string().nullish(),
title: z.string().nullish(),
@@ -171,6 +171,15 @@ export const offersProfileRouter = createRouter()
topSimilarOffers: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
offersFullTime: {
include: {
totalCompensation: true,
@@ -188,6 +197,15 @@ export const offersProfileRouter = createRouter()
experiences: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
},
},
},
@@ -203,6 +221,15 @@ export const offersProfileRouter = createRouter()
topSimilarOffers: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
offersFullTime: {
include: {
totalCompensation: true,
@@ -220,6 +247,15 @@ export const offersProfileRouter = createRouter()
experiences: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
},
},
},
@@ -233,6 +269,15 @@ export const offersProfileRouter = createRouter()
overallHighestOffer: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
offersFullTime: {
include: {
totalCompensation: true,
@@ -258,6 +303,15 @@ export const offersProfileRouter = createRouter()
experiences: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
monthlySalary: true,
totalCompensation: true,
},
@@ -275,6 +329,15 @@ export const offersProfileRouter = createRouter()
offers: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
offersFullTime: {
include: {
baseSalary: true,
@@ -350,6 +413,39 @@ export const offersProfileRouter = createRouter()
input.background.experiences.map(async (x) => {
if (x.jobType === JobType.FULLTIME) {
if (x.companyId) {
+ if (x.cityId) {
+ return {
+ company: {
+ connect: {
+ id: x.companyId,
+ },
+ },
+ durationInMonths: x.durationInMonths,
+ jobType: x.jobType,
+ level: x.level,
+ location: {
+ connect: {
+ id: x.cityId
+ }
+ },
+ title: x.title,
+ totalCompensation:
+ x.totalCompensation != null
+ ? {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ x.totalCompensation.value,
+ x.totalCompensation.currency,
+ baseCurrencyString,
+ ),
+ currency: x.totalCompensation.currency,
+ value: x.totalCompensation.value,
+ },
+ }
+ : undefined,
+ };
+ }
return {
company: {
connect: {
@@ -377,11 +473,40 @@ export const offersProfileRouter = createRouter()
: undefined,
};
}
+ if (x.cityId) {
+ return {
+ durationInMonths: x.durationInMonths,
+ jobType: x.jobType,
+ level: x.level,
+ location: {
+ connect: {
+ where: {
+ id: x.cityId
+ }
+ }
+ },
+ title: x.title,
+ totalCompensation:
+ x.totalCompensation != null
+ ? {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ x.totalCompensation.value,
+ x.totalCompensation.currency,
+ baseCurrencyString,
+ ),
+ currency: x.totalCompensation.currency,
+ value: x.totalCompensation.value,
+ },
+ }
+ : undefined,
+ };
+ }
return {
durationInMonths: x.durationInMonths,
jobType: x.jobType,
level: x.level,
- location: x.location,
title: x.title,
totalCompensation:
x.totalCompensation != null
@@ -402,6 +527,40 @@ export const offersProfileRouter = createRouter()
}
if (x.jobType === JobType.INTERN) {
if (x.companyId) {
+ if (x.cityId) {
+ return {
+ company: {
+ connect: {
+ id: x.companyId,
+ },
+ },
+ durationInMonths: x.durationInMonths,
+ jobType: x.jobType,
+ location: {
+ connect: {
+ where: {
+ id: x.cityId
+ }
+ }
+ },
+ monthlySalary:
+ x.monthlySalary != null
+ ? {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ x.monthlySalary.value,
+ x.monthlySalary.currency,
+ baseCurrencyString,
+ ),
+ currency: x.monthlySalary.currency,
+ value: x.monthlySalary.value,
+ },
+ }
+ : undefined,
+ title: x.title,
+ };
+ }
return {
company: {
connect: {
@@ -428,6 +587,37 @@ export const offersProfileRouter = createRouter()
title: x.title,
};
}
+
+ if (x.cityId) {
+ return {
+ durationInMonths: x.durationInMonths,
+ jobType: x.jobType,
+ location: {
+ connect: {
+ where: {
+ id: x.cityId
+ }
+ }
+ },
+ monthlySalary:
+ x.monthlySalary != null
+ ? {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ x.monthlySalary.value,
+ x.monthlySalary.currency,
+ baseCurrencyString,
+ ),
+ currency: x.monthlySalary.currency,
+ value: x.monthlySalary.value,
+ },
+ }
+ : undefined,
+ title: x.title,
+ };
+ }
+
return {
durationInMonths: x.durationInMonths,
jobType: x.jobType,
@@ -488,7 +678,13 @@ export const offersProfileRouter = createRouter()
},
},
jobType: x.jobType,
- location: x.location,
+ location: {
+ connect: {
+ where: {
+ id: x.cityId
+ }
+ }
+ },
monthYearReceived: x.monthYearReceived,
negotiationStrategy: x.negotiationStrategy,
offersIntern: {
@@ -528,7 +724,13 @@ export const offersProfileRouter = createRouter()
},
},
jobType: x.jobType,
- location: x.location,
+ location: {
+ connect: {
+ where: {
+ id: x.cityId
+ }
+ }
+ },
monthYearReceived: x.monthYearReceived,
negotiationStrategy: x.negotiationStrategy,
offersFullTime: {
@@ -871,35 +1073,110 @@ export const offersProfileRouter = createRouter()
exp.totalCompensation?.currency != null &&
exp.totalCompensation?.value != null
) {
+ // FULLTIME
if (exp.companyId) {
+ if (exp.cityId) {
+ await ctx.prisma.offersBackground.update({
+ data: {
+ experiences: {
+ create: {
+ company: {
+ connect: {
+ id: exp.companyId,
+ },
+ },
+ durationInMonths: exp.durationInMonths,
+ jobType: exp.jobType,
+ level: exp.level,
+ location: {
+ connect: {
+ id: exp.cityId
+ }
+ },
+ title: exp.title,
+ totalCompensation: exp.totalCompensation
+ ? {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.totalCompensation.value,
+ exp.totalCompensation.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.totalCompensation.currency,
+ value: exp.totalCompensation.value,
+ },
+ }
+ : undefined,
+ },
+ },
+ },
+ where: {
+ id: input.background.id,
+ },
+ });
+ } else {
+ await ctx.prisma.offersBackground.update({
+ data: {
+ experiences: {
+ create: {
+ company: {
+ connect: {
+ id: exp.companyId,
+ },
+ },
+ durationInMonths: exp.durationInMonths,
+ jobType: exp.jobType,
+ level: exp.level,
+ title: exp.title,
+ totalCompensation: exp.totalCompensation
+ ? {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.totalCompensation.value,
+ exp.totalCompensation.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.totalCompensation.currency,
+ value: exp.totalCompensation.value,
+ },
+ }
+ : undefined,
+ },
+ },
+ },
+ where: {
+ id: input.background.id,
+ },
+ });
+ }
+ } else if (exp.cityId) {
await ctx.prisma.offersBackground.update({
data: {
experiences: {
create: {
- company: {
- connect: {
- id: exp.companyId,
- },
- },
durationInMonths: exp.durationInMonths,
jobType: exp.jobType,
level: exp.level,
- location: exp.location,
+ location: {
+ connect: {
+ id: exp.cityId
+ }
+ },
title: exp.title,
- totalCompensation: exp.totalCompensation
- ? {
- create: {
- baseCurrency: baseCurrencyString,
- baseValue: await convert(
- exp.totalCompensation.value,
- exp.totalCompensation.currency,
- baseCurrencyString,
- ),
- currency: exp.totalCompensation.currency,
- value: exp.totalCompensation.value,
- },
- }
- : undefined,
+ totalCompensation: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.totalCompensation.value,
+ exp.totalCompensation.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.totalCompensation.currency,
+ value: exp.totalCompensation.value,
+ },
+ },
},
},
},
@@ -915,7 +1192,6 @@ export const offersProfileRouter = createRouter()
durationInMonths: exp.durationInMonths,
jobType: exp.jobType,
level: exp.level,
- location: exp.location,
title: exp.title,
totalCompensation: {
create: {
@@ -938,19 +1214,67 @@ export const offersProfileRouter = createRouter()
});
}
} else if (exp.companyId) {
+ if (exp.cityId) {
+ await ctx.prisma.offersBackground.update({
+ data: {
+ experiences: {
+ create: {
+ company: {
+ connect: {
+ id: exp.companyId,
+ },
+ },
+ durationInMonths: exp.durationInMonths,
+ jobType: exp.jobType,
+ level: exp.level,
+ location: {
+ connect: {
+ id: exp.cityId
+ }
+ },
+ title: exp.title,
+ },
+ },
+ },
+ where: {
+ id: input.background.id,
+ },
+ });
+ } else {
+ await ctx.prisma.offersBackground.update({
+ data: {
+ experiences: {
+ create: {
+ company: {
+ connect: {
+ id: exp.companyId,
+ },
+ },
+ durationInMonths: exp.durationInMonths,
+ jobType: exp.jobType,
+ level: exp.level,
+ title: exp.title,
+ },
+ },
+ },
+ where: {
+ id: input.background.id,
+ },
+ });
+ }
+ } else if (exp.cityId) {
await ctx.prisma.offersBackground.update({
data: {
experiences: {
create: {
- company: {
- connect: {
- id: exp.companyId,
- },
- },
durationInMonths: exp.durationInMonths,
jobType: exp.jobType,
level: exp.level,
- location: exp.location,
+ location: {
+ connect: {
+ id: exp.cityId
+ }
+ },
title: exp.title,
},
},
@@ -967,7 +1291,6 @@ export const offersProfileRouter = createRouter()
durationInMonths: exp.durationInMonths,
jobType: exp.jobType,
level: exp.level,
- location: exp.location,
title: exp.title,
},
},
@@ -982,19 +1305,90 @@ export const offersProfileRouter = createRouter()
exp.monthlySalary?.currency != null &&
exp.monthlySalary?.value != null
) {
+ // INTERN
if (exp.companyId) {
+ if (exp.cityId) {
+ await ctx.prisma.offersBackground.update({
+ data: {
+ experiences: {
+ create: {
+ company: {
+ connect: {
+ id: exp.companyId,
+ },
+ },
+ durationInMonths: exp.durationInMonths,
+ jobType: exp.jobType,
+ location: {
+ connect: {
+ id: exp.cityId
+ }
+ },
+ monthlySalary: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.monthlySalary.value,
+ exp.monthlySalary.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.monthlySalary.currency,
+ value: exp.monthlySalary.value,
+ },
+ },
+ title: exp.title,
+ },
+ },
+ },
+ where: {
+ id: input.background.id,
+ },
+ });
+ } else {
+ await ctx.prisma.offersBackground.update({
+ data: {
+ experiences: {
+ create: {
+ company: {
+ connect: {
+ id: exp.companyId,
+ },
+ },
+ durationInMonths: exp.durationInMonths,
+ jobType: exp.jobType,
+ monthlySalary: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.monthlySalary.value,
+ exp.monthlySalary.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.monthlySalary.currency,
+ value: exp.monthlySalary.value,
+ },
+ },
+ title: exp.title,
+ },
+ },
+ },
+ where: {
+ id: input.background.id,
+ },
+ });
+ }
+ } else if (exp.cityId) {
await ctx.prisma.offersBackground.update({
data: {
experiences: {
create: {
- company: {
- connect: {
- id: exp.companyId,
- },
- },
durationInMonths: exp.durationInMonths,
jobType: exp.jobType,
- location: exp.location,
+ location: {
+ connect: {
+ id: exp.cityId
+ }
+ },
monthlySalary: {
create: {
baseCurrency: baseCurrencyString,
@@ -1022,7 +1416,6 @@ export const offersProfileRouter = createRouter()
create: {
durationInMonths: exp.durationInMonths,
jobType: exp.jobType,
- location: exp.location,
monthlySalary: {
create: {
baseCurrency: baseCurrencyString,
@@ -1045,18 +1438,64 @@ export const offersProfileRouter = createRouter()
});
}
} else if (exp.companyId) {
+ if (exp.cityId) {
+ await ctx.prisma.offersBackground.update({
+ data: {
+ experiences: {
+ create: {
+ company: {
+ connect: {
+ id: exp.companyId,
+ },
+ },
+ durationInMonths: exp.durationInMonths,
+ jobType: exp.jobType,
+ location: {
+ connect: {
+ id: exp.cityId,
+ }
+ },
+ title: exp.title,
+ },
+ },
+ },
+ where: {
+ id: input.background.id,
+ },
+ });
+ } else {
+ await ctx.prisma.offersBackground.update({
+ data: {
+ experiences: {
+ create: {
+ company: {
+ connect: {
+ id: exp.companyId,
+ },
+ },
+ durationInMonths: exp.durationInMonths,
+ jobType: exp.jobType,
+ title: exp.title,
+ },
+ },
+ },
+ where: {
+ id: input.background.id,
+ },
+ });
+ }
+ } else if (exp.cityId) {
await ctx.prisma.offersBackground.update({
data: {
experiences: {
create: {
- company: {
- connect: {
- id: exp.companyId,
- },
- },
durationInMonths: exp.durationInMonths,
jobType: exp.jobType,
- location: exp.location,
+ location: {
+ connect: {
+ id: exp.cityId
+ }
+ },
title: exp.title,
},
},
@@ -1072,7 +1511,6 @@ export const offersProfileRouter = createRouter()
create: {
durationInMonths: exp.durationInMonths,
jobType: exp.jobType,
- location: exp.location,
title: exp.title,
},
},
@@ -1160,12 +1598,20 @@ export const offersProfileRouter = createRouter()
await ctx.prisma.offersOffer.update({
data: {
comments: offerToUpdate.comments,
- companyId: offerToUpdate.companyId,
+ company: {
+ connect: {
+ id: offerToUpdate.companyId
+ }
+ },
jobType:
offerToUpdate.jobType === JobType.FULLTIME
? JobType.FULLTIME
: JobType.INTERN,
- location: offerToUpdate.location,
+ location: {
+ connect: {
+ id: offerToUpdate.cityId
+ }
+ },
monthYearReceived: offerToUpdate.monthYearReceived,
negotiationStrategy: offerToUpdate.negotiationStrategy,
},
@@ -1302,7 +1748,11 @@ export const offersProfileRouter = createRouter()
},
},
jobType: offerToUpdate.jobType,
- location: offerToUpdate.location,
+ location: {
+ connect: {
+ id: offerToUpdate.cityId
+ }
+ },
monthYearReceived: offerToUpdate.monthYearReceived,
negotiationStrategy: offerToUpdate.negotiationStrategy,
offersIntern: {
@@ -1356,7 +1806,11 @@ export const offersProfileRouter = createRouter()
},
},
jobType: offerToUpdate.jobType,
- location: offerToUpdate.location,
+ location: {
+ connect: {
+ id: offerToUpdate.cityId
+ }
+ },
monthYearReceived: offerToUpdate.monthYearReceived,
negotiationStrategy: offerToUpdate.negotiationStrategy,
offersFullTime: {
diff --git a/apps/portal/src/server/router/offers/offers-user-profile-router.ts b/apps/portal/src/server/router/offers/offers-user-profile-router.ts
index 48994044..ae816261 100644
--- a/apps/portal/src/server/router/offers/offers-user-profile-router.ts
+++ b/apps/portal/src/server/router/offers/offers-user-profile-router.ts
@@ -57,6 +57,15 @@ export const offersUserProfileRouter = createProtectedRouter()
offers: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
offersFullTime: {
include: {
totalCompensation: true,
diff --git a/apps/portal/src/server/router/offers/offers.ts b/apps/portal/src/server/router/offers/offers.ts
index 86cd29ef..916dcb57 100644
--- a/apps/portal/src/server/router/offers/offers.ts
+++ b/apps/portal/src/server/router/offers/offers.ts
@@ -43,12 +43,12 @@ const getYoeRange = (yoeCategory: number) => {
export const offersRouter = createRouter().query('list', {
input: z.object({
+ cityId: z.string(),
companyId: z.string().nullish(),
currency: z.string().nullish(),
dateEnd: z.date().nullish(),
dateStart: z.date().nullish(),
limit: z.number().positive(),
- location: z.string(),
offset: z.number().nonnegative(),
salaryMax: z.number().nonnegative().nullish(),
salaryMin: z.number().nonnegative().nullish(),
@@ -132,8 +132,7 @@ export const offersRouter = createRouter().query('list', {
where: {
AND: [
{
- location:
- input.location.length === 0 ? undefined : input.location,
+ cityId: input.cityId.length === 0 ? undefined : input.cityId,
},
{
offersIntern: {
@@ -246,8 +245,7 @@ export const offersRouter = createRouter().query('list', {
where: {
AND: [
{
- location:
- input.location.length === 0 ? undefined : input.location,
+ cityId: input.cityId.length === 0 ? undefined : input.cityId,
},
{
offersIntern: {
diff --git a/apps/portal/src/types/offers.d.ts b/apps/portal/src/types/offers.d.ts
index 2ff82a16..3dcba938 100644
--- a/apps/portal/src/types/offers.d.ts
+++ b/apps/portal/src/types/offers.d.ts
@@ -25,7 +25,7 @@ export type Experience = {
id: string;
jobType: JobType?;
level: string?;
- location: string?;
+ location: Location?;
monthlySalary: Valuation?;
title: string?;
totalCompensation: Valuation?;
@@ -79,7 +79,7 @@ export type ProfileOffer = {
company: OffersCompany;
id: string;
jobType: JobType;
- location: string;
+ location: Location;
monthYearReceived: Date;
negotiationStrategy: string;
offersFullTime: FullTime?;
@@ -163,7 +163,7 @@ export type AnalysisHighestOffer = {
company: OffersCompany;
id: string;
level: string;
- location: string;
+ location: Location;
totalYoe: number;
};
@@ -173,7 +173,7 @@ export type AnalysisOffer = {
income: Valuation;
jobType: JobType;
level: string;
- location: string;
+ location: Location;
monthYearReceived: Date;
negotiationStrategy: string;
previousCompanies: Array;
@@ -202,7 +202,17 @@ export type UserProfileOffer = {
income: Valuation;
jobType: JobType;
level: string;
- location: string;
+ location: Location;
monthYearReceived: Date;
title: string;
};
+
+export type Location = {
+ cityId: string;
+ cityName: string;
+ countryCode: string;
+ countryId: string;
+ countryName: string;
+ stateId: string;
+ stateName: string;
+};
diff --git a/apps/portal/src/utils/offers/analysisGeneration.ts b/apps/portal/src/utils/offers/analysisGeneration.ts
index 75ba4f46..4d80cc8d 100644
--- a/apps/portal/src/utils/offers/analysisGeneration.ts
+++ b/apps/portal/src/utils/offers/analysisGeneration.ts
@@ -16,8 +16,14 @@ import { profileAnalysisDtoMapper } from '../../mappers/offers-mappers';
type Offer = OffersOffer & {
company: Company;
+ location: City & { state: State & { country: Country } };
offersFullTime:
- | (OffersFullTime & { totalCompensation: OffersCurrency })
+ | (OffersFullTime & {
+ baseSalary: OffersCurrency | null;
+ bonus: OffersCurrency | null;
+ stocks: OffersCurrency | null;
+ totalCompensation: OffersCurrency;
+ })
| null;
offersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
profile: OffersProfile & { background: OffersBackground | null };
@@ -60,298 +66,393 @@ export const generateAnalysis = async (params: {
}) => {
const { ctx, input } = params;
await ctx.prisma.offersAnalysis.deleteMany({
- where: {
- profileId: input.profileId,
- },
- });
-
- const offers = await ctx.prisma.offersOffer.findMany({
- include: {
- company: true,
- offersFullTime: {
- include: {
- baseSalary: true,
- bonus: true,
- stocks: true,
- totalCompensation: true,
- },
- },
- offersIntern: {
- include: {
- monthlySalary: true,
+ where: {
+ profileId: input.profileId,
},
- },
- profile: {
+ });
+
+ const offers = await ctx.prisma.offersOffer.findMany({
include: {
- background: true,
- },
- },
- },
- orderBy: [
- {
- offersFullTime: {
- totalCompensation: {
- baseValue: 'desc',
+ company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
+ offersFullTime: {
+ include: {
+ baseSalary: true,
+ bonus: true,
+ stocks: true,
+ totalCompensation: true,
+ },
+ },
+ offersIntern: {
+ include: {
+ monthlySalary: true,
+ },
+ },
+ profile: {
+ include: {
+ background: true,
+ },
},
},
- },
- {
- offersIntern: {
- monthlySalary: {
- baseValue: 'desc',
+ orderBy: [
+ {
+ offersFullTime: {
+ totalCompensation: {
+ baseValue: 'desc',
+ },
+ },
+ },
+ {
+ offersIntern: {
+ monthlySalary: {
+ baseValue: 'desc',
+ },
+ },
},
+ ],
+ where: {
+ profileId: input.profileId,
},
- },
- ],
- where: {
- profileId: input.profileId,
- },
- });
+ });
- if (!offers || offers.length === 0) {
- throw new TRPCError({
- code: 'NOT_FOUND',
- message: 'No offers found on this profile',
- });
- }
+ if (!offers || offers.length === 0) {
+ throw new TRPCError({
+ code: 'NOT_FOUND',
+ message: 'No offers found on this profile',
+ });
+ }
- const overallHighestOffer = offers[0];
+ const overallHighestOffer = offers[0];
- if (
- !overallHighestOffer.profile.background ||
- overallHighestOffer.profile.background.totalYoe == null
- ) {
- throw new TRPCError({
- code: 'NOT_FOUND',
- message: 'YOE not found',
- });
- }
+ if (
+ !overallHighestOffer.profile.background ||
+ overallHighestOffer.profile.background.totalYoe == null
+ ) {
+ throw new TRPCError({
+ code: 'NOT_FOUND',
+ message: 'YOE not found',
+ });
+ }
- const yoe = overallHighestOffer.profile.background.totalYoe as number;
- const monthYearReceived = new Date(overallHighestOffer.monthYearReceived);
- monthYearReceived.setFullYear(monthYearReceived.getFullYear() - 1);
+ const yoe = overallHighestOffer.profile.background.totalYoe as number;
+ const monthYearReceived = new Date(overallHighestOffer.monthYearReceived);
+ monthYearReceived.setFullYear(monthYearReceived.getFullYear() - 1);
- const similarOffers = await ctx.prisma.offersOffer.findMany({
- include: {
- company: true,
- offersFullTime: {
- include: {
- totalCompensation: true,
- },
- },
- offersIntern: {
- include: {
- monthlySalary: true,
- },
- },
- profile: {
+ let similarOffers = await ctx.prisma.offersOffer.findMany({
include: {
- background: {
+ company: true,
+ location: {
include: {
- experiences: {
+ state: {
include: {
- company: true,
+ country: true,
},
},
},
},
- },
- },
- },
- orderBy: [
- {
- offersFullTime: {
- totalCompensation: {
- baseValue: 'desc',
+ offersFullTime: {
+ include: {
+ totalCompensation: true,
+ },
},
- },
- },
- {
- offersIntern: {
- monthlySalary: {
- baseValue: 'desc',
+ offersIntern: {
+ include: {
+ monthlySalary: true,
+ },
},
- },
- },
- ],
- where: {
- AND: [
- {
- location: overallHighestOffer.location,
- },
- {
- monthYearReceived: {
- gte: monthYearReceived,
+ profile: {
+ include: {
+ background: {
+ include: {
+ experiences: {
+ include: {
+ company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
},
},
- {
- OR: [
- {
- offersFullTime: {
- title: overallHighestOffer.offersFullTime?.title,
+ orderBy: [
+ {
+ offersFullTime: {
+ totalCompensation: {
+ baseValue: 'desc',
},
- offersIntern: {
- title: overallHighestOffer.offersIntern?.title,
+ },
+ },
+ {
+ offersIntern: {
+ monthlySalary: {
+ baseValue: 'desc',
},
},
- ],
- },
- {
- profile: {
- background: {
- AND: [
+ },
+ ],
+ where: {
+ AND: [
+ {
+ location: overallHighestOffer.location,
+ },
+ {
+ monthYearReceived: {
+ gte: monthYearReceived,
+ },
+ },
+ {
+ OR: [
{
- totalYoe: {
- gte: Math.max(yoe - 1, 0),
- lte: yoe + 1,
+ offersFullTime: {
+ title: overallHighestOffer.offersFullTime?.title,
+ },
+ offersIntern: {
+ title: overallHighestOffer.offersIntern?.title,
},
},
],
},
- },
+ {
+ profile: {
+ background: {
+ AND: [
+ {
+ totalYoe: {
+ gte: Math.max(yoe - 1, 0),
+ lte: yoe + 1,
+ },
+ },
+ ],
+ },
+ },
+ },
+ ],
},
- ],
- },
- });
+ });
- // COMPANY ANALYSIS
- const companyMap = new Map();
- offers.forEach((offer) => {
- if (companyMap.get(offer.companyId) == null) {
- companyMap.set(offer.companyId, offer);
- }
- });
+ // COMPANY ANALYSIS
+ const companyMap = new Map();
+ offers.forEach((offer) => {
+ if (companyMap.get(offer.companyId) == null) {
+ companyMap.set(offer.companyId, offer);
+ }
+ });
- const companyAnalysis = Array.from(companyMap.values()).map(
- (companyOffer) => {
- // TODO: Refactor calculating analysis into a function
- const similarCompanyOffers = similarOffers.filter(
- (offer) => offer.companyId === companyOffer.companyId,
- );
+ const companyAnalysis = Array.from(companyMap.values()).map(
+ (companyOffer) => {
+ // TODO: Refactor calculating analysis into a function
+ let similarCompanyOffers = similarOffers.filter(
+ (offer) => offer.companyId === companyOffer.companyId,
+ );
- const companyIndex = searchOfferPercentile(
- companyOffer,
- similarCompanyOffers,
- );
- const companyPercentile =
- similarCompanyOffers.length <= 1
- ? 100
- : 100 - (100 * companyIndex) / (similarCompanyOffers.length - 1);
+ const companyIndex = searchOfferPercentile(
+ companyOffer,
+ similarCompanyOffers,
+ );
+ const companyPercentile =
+ similarCompanyOffers.length <= 1
+ ? 100
+ : 100 - (100 * companyIndex) / (similarCompanyOffers.length - 1);
- // Get top offers (excluding user's offer)
- const similarCompanyOffersWithoutUsersOffers =
- similarCompanyOffers.filter(
- (offer) => offer.profileId !== input.profileId,
- );
+ // Get top offers (excluding user's offer)
+ similarCompanyOffers = similarCompanyOffers.filter(
+ (offer) => offer.id !== companyOffer.id,
+ );
- const noOfSimilarCompanyOffers =
- similarCompanyOffersWithoutUsersOffers.length;
- const similarCompanyOffers90PercentileIndex = Math.ceil(
- noOfSimilarCompanyOffers * 0.1,
- );
- const topPercentileCompanyOffers =
- noOfSimilarCompanyOffers > 2
- ? similarCompanyOffersWithoutUsersOffers.slice(
- similarCompanyOffers90PercentileIndex,
- similarCompanyOffers90PercentileIndex + 2,
- )
- : similarCompanyOffersWithoutUsersOffers;
+ const noOfSimilarCompanyOffers = similarCompanyOffers.length;
+ const similarCompanyOffers90PercentileIndex = Math.ceil(
+ noOfSimilarCompanyOffers * 0.1,
+ );
+ const topPercentileCompanyOffers =
+ noOfSimilarCompanyOffers > 2
+ ? similarCompanyOffers.slice(
+ similarCompanyOffers90PercentileIndex,
+ similarCompanyOffers90PercentileIndex + 2,
+ )
+ : similarCompanyOffers;
- return {
- companyName: companyOffer.company.name,
- noOfSimilarOffers: noOfSimilarCompanyOffers,
- percentile: companyPercentile,
- topSimilarOffers: topPercentileCompanyOffers,
- };
- },
- );
+ return {
+ companyName: companyOffer.company.name,
+ noOfSimilarOffers: noOfSimilarCompanyOffers,
+ percentile: companyPercentile,
+ topSimilarOffers: topPercentileCompanyOffers,
+ };
+ },
+ );
- // OVERALL ANALYSIS
- const overallIndex = searchOfferPercentile(
- overallHighestOffer,
- similarOffers,
- );
- const overallPercentile =
- similarOffers.length <= 1
- ? 100
- : 100 - (100 * overallIndex) / (similarOffers.length - 1);
+ // OVERALL ANALYSIS
+ const overallIndex = searchOfferPercentile(
+ overallHighestOffer,
+ similarOffers,
+ );
+ const overallPercentile =
+ similarOffers.length <= 1
+ ? 100
+ : 100 - (100 * overallIndex) / (similarOffers.length - 1);
- const similarOffersWithoutUsersOffers = similarOffers.filter(
- (similarOffer) => similarOffer.profileId !== input.profileId,
- );
+ similarOffers = similarOffers.filter(
+ (offer) => offer.id !== overallHighestOffer.id,
+ );
- const noOfSimilarOffers = similarOffersWithoutUsersOffers.length;
- const similarOffers90PercentileIndex = Math.ceil(noOfSimilarOffers * 0.1);
- const topPercentileOffers =
- noOfSimilarOffers > 2
- ? similarOffersWithoutUsersOffers.slice(
- similarOffers90PercentileIndex,
- similarOffers90PercentileIndex + 2,
- )
- : similarOffersWithoutUsersOffers;
+ const noOfSimilarOffers = similarOffers.length;
+ const similarOffers90PercentileIndex = Math.ceil(noOfSimilarOffers * 0.1);
+ const topPercentileOffers =
+ noOfSimilarOffers > 2
+ ? similarOffers.slice(
+ similarOffers90PercentileIndex,
+ similarOffers90PercentileIndex + 2,
+ )
+ : similarOffers;
- const analysis = await ctx.prisma.offersAnalysis.create({
- data: {
- companyAnalysis: {
- create: companyAnalysis.map((analysisUnit) => {
- return {
- companyName: analysisUnit.companyName,
- noOfSimilarOffers: analysisUnit.noOfSimilarOffers,
- percentile: analysisUnit.percentile,
- topSimilarOffers: {
- connect: analysisUnit.topSimilarOffers.map((offer) => {
- return { id: offer.id };
- }),
- },
- };
- }),
- },
- overallAnalysis: {
- create: {
- companyName: overallHighestOffer.company.name,
- noOfSimilarOffers,
- percentile: overallPercentile,
- topSimilarOffers: {
- connect: topPercentileOffers.map((offer) => {
- return { id: offer.id };
+ const analysis = await ctx.prisma.offersAnalysis.create({
+ data: {
+ companyAnalysis: {
+ create: companyAnalysis.map((analysisUnit) => {
+ return {
+ companyName: analysisUnit.companyName,
+ noOfSimilarOffers: analysisUnit.noOfSimilarOffers,
+ percentile: analysisUnit.percentile,
+ topSimilarOffers: {
+ connect: analysisUnit.topSimilarOffers.map((offer) => {
+ return { id: offer.id };
+ }),
+ },
+ };
}),
},
+ overallAnalysis: {
+ create: {
+ companyName: overallHighestOffer.company.name,
+ noOfSimilarOffers,
+ percentile: overallPercentile,
+ topSimilarOffers: {
+ connect: topPercentileOffers.map((offer) => {
+ return { id: offer.id };
+ }),
+ },
+ },
+ },
+ overallHighestOffer: {
+ connect: {
+ id: overallHighestOffer.id,
+ },
+ },
+ profile: {
+ connect: {
+ id: input.profileId,
+ },
+ },
},
- },
- overallHighestOffer: {
- connect: {
- id: overallHighestOffer.id,
- },
- },
- profile: {
- connect: {
- id: input.profileId,
- },
- },
- },
- include: {
- companyAnalysis: {
include: {
- topSimilarOffers: {
+ companyAnalysis: {
include: {
- company: true,
- offersFullTime: {
- include: {
- totalCompensation: true,
- },
- },
- offersIntern: {
+ topSimilarOffers: {
include: {
- monthlySalary: true,
+ company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
+ offersFullTime: {
+ include: {
+ totalCompensation: true,
+ },
+ },
+ offersIntern: {
+ include: {
+ monthlySalary: true,
+ },
+ },
+ profile: {
+ include: {
+ background: {
+ include: {
+ experiences: {
+ include: {
+ company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
},
},
- profile: {
+ },
+ },
+ overallAnalysis: {
+ include: {
+ topSimilarOffers: {
include: {
- background: {
+ company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
+ offersFullTime: {
+ include: {
+ totalCompensation: true,
+ },
+ },
+ offersIntern: {
+ include: {
+ monthlySalary: true,
+ },
+ },
+ profile: {
include: {
- experiences: {
+ background: {
include: {
- company: true,
+ experiences: {
+ include: {
+ company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
+ },
+ },
},
},
},
@@ -360,13 +461,18 @@ export const generateAnalysis = async (params: {
},
},
},
- },
- },
- overallAnalysis: {
- include: {
- topSimilarOffers: {
+ overallHighestOffer: {
include: {
company: true,
+ location: {
+ include: {
+ state: {
+ include: {
+ country: true,
+ },
+ },
+ },
+ },
offersFullTime: {
include: {
totalCompensation: true,
@@ -379,43 +485,13 @@ export const generateAnalysis = async (params: {
},
profile: {
include: {
- background: {
- include: {
- experiences: {
- include: {
- company: true,
- },
- },
- },
- },
+ background: true,
},
},
},
},
},
- },
- overallHighestOffer: {
- include: {
- company: true,
- offersFullTime: {
- include: {
- totalCompensation: true,
- },
- },
- offersIntern: {
- include: {
- monthlySalary: true,
- },
- },
- profile: {
- include: {
- background: true,
- },
- },
- },
- },
- },
- });
+ });
- return profileAnalysisDtoMapper(analysis);
+ return profileAnalysisDtoMapper(analysis);
};