[offers][chore] Refactor profile and analysis APIs to return defined types

pull/390/head
BryannYeap 3 years ago
parent ecad270d33
commit 00165a8f7d

@ -191,13 +191,13 @@ model OffersProfile {
user User? @relation(fields: [userId], references: [id]) user User? @relation(fields: [userId], references: [id])
userId String? userId String?
OffersAnalysis OffersAnalysis? analysis OffersAnalysis?
} }
model OffersBackground { model OffersBackground {
id String @id @default(cuid()) id String @id @default(cuid())
totalYoe Int? totalYoe Int
specificYoes OffersSpecificYoe[] specificYoes OffersSpecificYoe[]
experiences OffersExperience[] // For extensibility in the future experiences OffersExperience[] // For extensibility in the future
@ -320,7 +320,6 @@ model OffersOffer {
offersFullTimeId String? @unique offersFullTimeId String? @unique
OffersAnalysis OffersAnalysis? @relation("HighestOverallOffer") OffersAnalysis OffersAnalysis? @relation("HighestOverallOffer")
OffersAnalysisTopOverallOffers OffersAnalysis[] @relation("TopOverallOffers") OffersAnalysisTopOverallOffers OffersAnalysis[] @relation("TopOverallOffers")
OffersAnalysisTopCompanyOffers OffersAnalysis[] @relation("TopCompanyOffers") OffersAnalysisTopCompanyOffers OffersAnalysis[] @relation("TopCompanyOffers")
} }

@ -0,0 +1,524 @@
import type {
Company,
OffersAnalysis,
OffersBackground,
OffersCurrency,
OffersEducation,
OffersExperience,
OffersFullTime,
OffersIntern,
OffersOffer,
OffersProfile,
OffersReply,
OffersSpecificYoe,
User,
} from '@prisma/client';
import { JobType } from '@prisma/client';
import type {
AddToProfileResponse,
Analysis,
AnalysisHighestOffer,
AnalysisOffer,
Background,
CreateOfferProfileResponse,
Education,
Experience,
OffersCompany,
Profile,
ProfileAnalysis,
ProfileOffer,
SpecificYoe,
Valuation,
} from '~/types/offers';
const analysisOfferDtoMapper = (
offer: OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
company: Company;
profile: OffersProfile & { background: OffersBackground | null };
},
) => {
const { background, profileName } = offer.profile;
const analysisOfferDto: AnalysisOffer = {
company: offersCompanyDtoMapper(offer.company),
id: offer.id,
income: -1,
jobType: offer.jobType,
level: offer.OffersFullTime?.level ?? '',
location: offer.location,
monthYearReceived: offer.monthYearReceived,
negotiationStrategy: offer.negotiationStrategy,
previousCompanies: [],
profileName,
specialization:
offer.jobType === JobType.FULLTIME
? offer.OffersFullTime?.specialization ?? ''
: offer.OffersIntern?.specialization ?? '',
title:
offer.jobType === JobType.FULLTIME
? offer.OffersFullTime?.title ?? ''
: offer.OffersIntern?.title ?? '',
totalYoe: background?.totalYoe ?? -1,
};
if (offer.OffersFullTime?.totalCompensation) {
analysisOfferDto.income = offer.OffersFullTime.totalCompensation.value;
} else if (offer.OffersIntern?.monthlySalary) {
analysisOfferDto.income = offer.OffersIntern.monthlySalary.value;
}
return analysisOfferDto;
};
const analysisDtoMapper = (
noOfOffers: number,
percentile: number,
topPercentileOffers: Array<
OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
company: Company;
profile: OffersProfile & { background: OffersBackground | null };
}
>,
) => {
const analysisDto: Analysis = {
noOfOffers,
percentile,
topPercentileOffers: topPercentileOffers.map((offer) =>
analysisOfferDtoMapper(offer),
),
};
return analysisDto;
};
const analysisHighestOfferDtoMapper = (
offer: OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
company: Company;
profile: OffersProfile & { background: OffersBackground | null };
},
) => {
const analysisHighestOfferDto: AnalysisHighestOffer = {
company: offersCompanyDtoMapper(offer.company),
id: offer.id,
level: offer.OffersFullTime?.level ?? '',
location: offer.location,
specialization:
offer.jobType === JobType.FULLTIME
? offer.OffersFullTime?.specialization ?? ''
: offer.OffersIntern?.specialization ?? '',
totalYoe: offer.profile.background?.totalYoe ?? -1,
};
return analysisHighestOfferDto;
};
export const profileAnalysisDtoMapper = (
analysis:
| (OffersAnalysis & {
overallHighestOffer: OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
company: Company;
profile: OffersProfile & { background: OffersBackground | null };
};
topCompanyOffers: Array<
OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
company: Company;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
topOverallOffers: Array<
OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
company: Company;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
})
| null,
) => {
if (!analysis) {
return null;
}
const profileAnalysisDto: ProfileAnalysis = {
companyAnalysis: [
analysisDtoMapper(
analysis.noOfSimilarCompanyOffers,
analysis.companyPercentile,
analysis.topCompanyOffers,
),
],
id: analysis.id,
overallAnalysis: analysisDtoMapper(
analysis.noOfSimilarOffers,
analysis.overallPercentile,
analysis.topOverallOffers,
),
overallHighestOffer: analysisHighestOfferDtoMapper(
analysis.overallHighestOffer,
),
profileId: analysis.profileId,
};
return profileAnalysisDto;
};
export const valuationDtoMapper = (currency: {
currency: string;
id?: string;
value: number;
}) => {
const valuationDto: Valuation = {
currency: currency.currency,
value: currency.value,
};
return valuationDto;
};
export const offersCompanyDtoMapper = (company: Company | null) => {
if (!company) {
return null;
}
const companyDto: OffersCompany = {
createdAt: company.createdAt,
description: company?.description ?? '',
id: company.id,
logoUrl: company.logoUrl ?? '',
name: company.name,
slug: company.slug,
updatedAt: company.updatedAt,
};
return companyDto;
};
export const educationDtoMapper = (education: {
backgroundId?: string;
endDate: Date | null;
field: string | null;
id: string;
school: string | null;
startDate: Date | null;
type: string | null;
}) => {
const educationDto: Education = {
endDate: education.endDate,
field: education.field,
id: education.id,
school: education.school,
startDate: education.startDate,
type: education.type,
};
return educationDto;
};
export const experienceDtoMapper = (
experience: OffersExperience & {
company: Company | null;
monthlySalary: OffersCurrency | null;
totalCompensation: OffersCurrency | null;
},
) => {
const experienceDto: Experience = {
company: offersCompanyDtoMapper(experience.company),
durationInMonths: experience.durationInMonths,
id: experience.id,
jobType: experience.jobType,
level: experience.level,
monthlySalary: experience.monthlySalary
? valuationDtoMapper(experience.monthlySalary)
: experience.monthlySalary,
specialization: experience.specialization,
title: experience.title,
totalCompensation: experience.totalCompensation
? valuationDtoMapper(experience.totalCompensation)
: experience.totalCompensation,
};
return experienceDto;
};
export const specificYoeDtoMapper = (specificYoe: {
backgroundId?: string;
domain: string;
id: string;
yoe: number;
}) => {
const specificYoeDto: SpecificYoe = {
domain: specificYoe.domain,
id: specificYoe.id,
yoe: specificYoe.yoe,
};
return specificYoeDto;
};
export const backgroundDtoMapper = (
background:
| (OffersBackground & {
educations: Array<OffersEducation>;
experiences: Array<
OffersExperience & {
company: Company | null;
monthlySalary: OffersCurrency | null;
totalCompensation: OffersCurrency | null;
}
>;
specificYoes: Array<OffersSpecificYoe>;
})
| null,
) => {
if (!background) {
return null;
}
const educations = background.educations.map((education) =>
educationDtoMapper(education),
);
const experiences = background.experiences.map((experience) =>
experienceDtoMapper(experience),
);
const specificYoes = background.specificYoes.map((specificYoe) =>
specificYoeDtoMapper(specificYoe),
);
const backgroundDto: Background = {
educations,
experiences,
id: background.id,
specificYoes,
totalYoe: background.totalYoe,
};
return backgroundDto;
};
export const profileOfferDtoMapper = (
offer: OffersOffer & {
OffersFullTime:
| (OffersFullTime & {
baseSalary: OffersCurrency;
bonus: OffersCurrency;
stocks: OffersCurrency;
totalCompensation: OffersCurrency;
})
| null;
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
company: Company;
},
) => {
const profileOfferDto: ProfileOffer = {
comments: offer.comments,
company: offersCompanyDtoMapper(offer.company),
id: offer.id,
jobType: offer.jobType,
location: offer.location,
monthYearReceived: offer.monthYearReceived,
negotiationStrategy: offer.negotiationStrategy,
offersFullTime: offer.OffersFullTime,
offersIntern: offer.OffersIntern,
};
if (offer.OffersFullTime) {
profileOfferDto.offersFullTime = {
baseSalary: valuationDtoMapper(offer.OffersFullTime.baseSalary),
bonus: valuationDtoMapper(offer.OffersFullTime.bonus),
id: offer.OffersFullTime.id,
level: offer.OffersFullTime.level,
specialization: offer.OffersFullTime.specialization,
stocks: valuationDtoMapper(offer.OffersFullTime.stocks),
title: offer.OffersFullTime.title,
totalCompensation: valuationDtoMapper(
offer.OffersFullTime.totalCompensation,
),
};
} else if (offer.OffersIntern) {
profileOfferDto.offersIntern = {
id: offer.OffersIntern.id,
internshipCycle: offer.OffersIntern.internshipCycle,
monthlySalary: valuationDtoMapper(offer.OffersIntern.monthlySalary),
specialization: offer.OffersIntern.specialization,
startYear: offer.OffersIntern.startYear,
title: offer.OffersIntern.title,
};
}
return profileOfferDto;
};
export const profileDtoMapper = (
profile: OffersProfile & {
analysis:
| (OffersAnalysis & {
overallHighestOffer: OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
company: Company;
profile: OffersProfile & { background: OffersBackground | null };
};
topCompanyOffers: Array<
OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
company: Company;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
topOverallOffers: Array<
OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern:
| (OffersIntern & { monthlySalary: OffersCurrency })
| null;
company: Company;
profile: OffersProfile & {
background:
| (OffersBackground & {
experiences: Array<
OffersExperience & { company: Company | null }
>;
})
| null;
};
}
>;
})
| null;
background:
| (OffersBackground & {
educations: Array<OffersEducation>;
experiences: Array<
OffersExperience & {
company: Company | null;
monthlySalary: OffersCurrency | null;
totalCompensation: OffersCurrency | null;
}
>;
specificYoes: Array<OffersSpecificYoe>;
})
| null;
discussion: Array<
OffersReply & {
replies: Array<OffersReply>;
replyingTo: OffersReply | null;
user: User | null;
}
>;
offers: Array<
OffersOffer & {
OffersFullTime:
| (OffersFullTime & {
baseSalary: OffersCurrency;
bonus: OffersCurrency;
stocks: OffersCurrency;
totalCompensation: OffersCurrency;
})
| null;
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
company: Company;
}
>;
},
inputToken: string | undefined,
) => {
const profileDto: Profile = {
analysis: profileAnalysisDtoMapper(profile.analysis),
background: backgroundDtoMapper(profile.background),
editToken: null,
id: profile.id,
isEditable: false,
offers: profile.offers.map((offer) => profileOfferDtoMapper(offer)),
profileName: profile.profileName,
};
if (inputToken === profile.editToken) {
profileDto.editToken = profile.editToken;
profileDto.isEditable = true;
}
return profileDto;
};
export const createOfferProfileResponseMapper = (
profile: { id: string },
token: string,
) => {
const res: CreateOfferProfileResponse = {
id: profile.id,
token,
};
return res;
};
export const addToProfileResponseMapper = (updatedProfile: {
id: string;
profileName: string;
userId?: string | null;
}) => {
const addToProfileResponse: AddToProfileResponse = {
id: updatedProfile.id,
profileName: updatedProfile.profileName,
userId: updatedProfile.userId ?? '',
};
return addToProfileResponse;
};

@ -7,7 +7,7 @@ function Test() {
const [error, setError] = useState(''); const [error, setError] = useState('');
const createMutation = trpc.useMutation(['offers.profile.create'], { const createMutation = trpc.useMutation(['offers.profile.create'], {
onError(err: any) { onError(err) {
alert(err); alert(err);
}, },
onSuccess(data) { onSuccess(data) {
@ -18,7 +18,7 @@ function Test() {
const addToUserProfileMutation = trpc.useMutation( const addToUserProfileMutation = trpc.useMutation(
['offers.profile.addToUserProfile'], ['offers.profile.addToUserProfile'],
{ {
onError(err: any) { onError(err) {
alert(err); alert(err);
}, },
onSuccess(data) { onSuccess(data) {
@ -28,7 +28,7 @@ function Test() {
); );
const deleteCommentMutation = trpc.useMutation(['offers.comments.delete'], { const deleteCommentMutation = trpc.useMutation(['offers.comments.delete'], {
onError(err: any) { onError(err) {
alert(err); alert(err);
}, },
onSuccess(data) { onSuccess(data) {
@ -46,7 +46,7 @@ function Test() {
}; };
const updateCommentMutation = trpc.useMutation(['offers.comments.update'], { const updateCommentMutation = trpc.useMutation(['offers.comments.update'], {
onError(err: any) { onError(err) {
alert(err); alert(err);
}, },
onSuccess(data) { onSuccess(data) {
@ -64,7 +64,7 @@ function Test() {
}; };
const createCommentMutation = trpc.useMutation(['offers.comments.create'], { const createCommentMutation = trpc.useMutation(['offers.comments.create'], {
onError(err: any) { onError(err) {
alert(err); alert(err);
}, },
onSuccess(data) { onSuccess(data) {
@ -151,8 +151,10 @@ function Test() {
value: 104100, value: 104100,
}, },
}, },
comments: '',
companyId: 'cl9ec1mgg0000w33hg1a3612r', comments: 'I am a Raffles Institution almumni',
// Comments: '',
companyId: 'cl98yuqk80007txhgjtjp8fk4',
jobType: 'FULLTIME', jobType: 'FULLTIME',
location: 'Singapore, Singapore', location: 'Singapore, Singapore',
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'), monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
@ -180,8 +182,8 @@ function Test() {
value: 104100, value: 104100,
}, },
}, },
comments: "", comments: '',
companyId: 'cl9ec1mgg0000w33hg1a3612r', companyId: 'cl98yuqk80007txhgjtjp8fk4',
jobType: 'FULLTIME', jobType: 'FULLTIME',
location: 'Singapore, Singapore', location: 'Singapore, Singapore',
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'), monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
@ -228,7 +230,7 @@ function Test() {
}; };
const updateMutation = trpc.useMutation(['offers.profile.update'], { const updateMutation = trpc.useMutation(['offers.profile.update'], {
onError(err: any) { onError(err) {
alert(err); alert(err);
}, },
onSuccess(response) { onSuccess(response) {
@ -412,7 +414,7 @@ function Test() {
totalCompensationId: 'cl96stky90039w32glbpktd0o', totalCompensationId: 'cl96stky90039w32glbpktd0o',
}, },
OffersIntern: null, OffersIntern: null,
comments: null, comments: '',
company: { company: {
createdAt: new Date('2022-10-12T16:19:05.196Z'), createdAt: new Date('2022-10-12T16:19:05.196Z'),
description: description:
@ -465,7 +467,7 @@ function Test() {
totalCompensationId: 'cl96stky9003jw32gzumcoi7v', totalCompensationId: 'cl96stky9003jw32gzumcoi7v',
}, },
OffersIntern: null, OffersIntern: null,
comments: null, comments: '',
company: { company: {
createdAt: new Date('2022-10-12T16:19:05.196Z'), createdAt: new Date('2022-10-12T16:19:05.196Z'),
description: description:

@ -8,9 +8,10 @@ import type {
OffersOffer, OffersOffer,
OffersProfile, OffersProfile,
} from '@prisma/client'; } from '@prisma/client';
import { JobType } from '@prisma/client';
import { TRPCError } from '@trpc/server'; import { TRPCError } from '@trpc/server';
import { profileAnalysisDtoMapper } from '~/mappers/offers-mappers';
import { createRouter } from '../context'; import { createRouter } from '../context';
const searchOfferPercentile = ( const searchOfferPercentile = (
@ -27,9 +28,19 @@ const searchOfferPercentile = (
company: Company; company: Company;
profile: OffersProfile & { background: OffersBackground | null }; profile: OffersProfile & { background: OffersBackground | null };
}, },
similarOffers: Array<any> | string, similarOffers: Array<
OffersOffer & {
OffersFullTime:
| (OffersFullTime & {
totalCompensation: OffersCurrency;
})
| null;
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
company: Company;
profile: OffersProfile & { background: OffersBackground | null };
}
>,
) => { ) => {
for (let i = 0; i < similarOffers.length; i++) { for (let i = 0; i < similarOffers.length; i++) {
if (similarOffers[i].id === offer.id) { if (similarOffers[i].id === offer.id) {
return i; return i;
@ -39,116 +50,6 @@ const searchOfferPercentile = (
return -1; return -1;
}; };
const topPercentileDtoMapper = (topPercentileOffers: Array<any>) => {
return topPercentileOffers.map((offer) => {
const { background } = offer.profile;
return {
company: { id: offer.company.id, name: offer.company.name },
id: offer.id,
jobType: offer.jobType,
level: offer.OffersFullTime?.level,
monthYearReceived: offer.monthYearReceived,
monthlySalary: offer.OffersIntern?.monthlySalary?.value,
negotiationStrategy: offer.negotiationStrategy,
profile: {
background: {
experiences: background?.experiences.map(
(exp: { company: { id: any; name: any }; id: any }) => {
return {
company: { id: exp.company.id, name: exp.company.name },
id: exp.id,
};
},
),
id: background?.id,
totalYoe: background?.totalYoe,
},
id: offer.profileId,
name: offer.profile.profileName,
},
specialization:
offer.jobType === JobType.FULLTIME
? offer.OffersFullTime?.specialization
: offer.OffersIntern?.specialization,
title:
offer.jobType === JobType.FULLTIME
? offer.OffersFullTime?.title
: offer.OffersIntern?.title,
totalCompensation: offer.OffersFullTime?.totalCompensation?.value,
};
});
};
const specificAnalysisDtoMapper = (
noOfOffers: number,
percentile: number,
topPercentileOffers: Array<any>,
) => {
return {
noOfOffers,
percentile,
topPercentileCompanyOffers: topPercentileDtoMapper(topPercentileOffers),
};
};
const highestOfferDtoMapper = (
offer: OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
company: Company;
profile: OffersProfile & { background: OffersBackground | null };
},
) => {
return {
company: { id: offer.company.id, name: offer.company.name },
id: offer.id,
level: offer.OffersFullTime?.level,
location: offer.location,
specialization:
offer.jobType === JobType.FULLTIME
? offer.OffersFullTime?.specialization
: offer.OffersIntern?.specialization,
totalYoe: offer.profile.background?.totalYoe,
};
};
const profileAnalysisDtoMapper = (
analysisId: string,
profileId: string,
overallHighestOffer: OffersOffer & {
OffersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
company: Company;
profile: OffersProfile & { background: OffersBackground | null };
},
noOfSimilarOffers: number,
overallPercentile: number,
topPercentileOffers: Array<any>,
noOfSimilarCompanyOffers: number,
companyPercentile: number,
topPercentileCompanyOffers: Array<any>,
) => {
return {
companyAnalysis: specificAnalysisDtoMapper(
noOfSimilarCompanyOffers,
companyPercentile,
topPercentileCompanyOffers,
),
id: analysisId,
overallAnalysis: specificAnalysisDtoMapper(
noOfSimilarOffers,
overallPercentile,
topPercentileOffers,
),
overallHighestOffer: highestOfferDtoMapper(overallHighestOffer),
profileId,
};
};
export const offersAnalysisRouter = createRouter() export const offersAnalysisRouter = createRouter()
.query('generate', { .query('generate', {
input: z.object({ input: z.object({
@ -213,7 +114,7 @@ export const offersAnalysisRouter = createRouter()
const overallHighestOffer = offers[0]; const overallHighestOffer = offers[0];
// TODO: Shift yoe to background to make it mandatory // TODO: Shift yoe out of background to make it mandatory
if ( if (
!overallHighestOffer.profile.background || !overallHighestOffer.profile.background ||
!overallHighestOffer.profile.background.totalYoe !overallHighestOffer.profile.background.totalYoe
@ -465,17 +366,7 @@ export const offersAnalysisRouter = createRouter()
}, },
}); });
return profileAnalysisDtoMapper( return profileAnalysisDtoMapper(analysis);
analysis.id,
analysis.profileId,
overallHighestOffer,
noOfSimilarOffers,
overallPercentile,
topPercentileOffers,
noOfSimilarCompanyOffers,
companyPercentile,
topPercentileCompanyOffers,
);
}, },
}) })
.query('get', { .query('get', {
@ -574,16 +465,6 @@ export const offersAnalysisRouter = createRouter()
}); });
} }
return profileAnalysisDtoMapper( return profileAnalysisDtoMapper(analysis);
analysis.id,
analysis.profileId,
analysis.overallHighestOffer,
analysis.noOfSimilarOffers,
analysis.overallPercentile,
analysis.topOverallOffers,
analysis.noOfSimilarCompanyOffers,
analysis.companyPercentile,
analysis.topCompanyOffers,
);
}, },
}); });

@ -2,9 +2,13 @@ import crypto, { randomUUID } from 'crypto';
import { z } from 'zod'; import { z } from 'zod';
import * as trpc from '@trpc/server'; import * as trpc from '@trpc/server';
import { createRouter } from '../context'; import {
addToProfileResponseMapper,
createOfferProfileResponseMapper,
profileDtoMapper,
} from '~/mappers/offers-mappers';
import type { OffersProfile } from '~/types/offers'; import { createRouter } from '../context';
const valuation = z.object({ const valuation = z.object({
currency: z.string(), currency: z.string(),
@ -50,14 +54,14 @@ const offer = z.object({
totalCompensation: valuation.nullish(), // Full time totalCompensation: valuation.nullish(), // Full time
}) })
.nullish(), .nullish(),
comments: z.string().nullish(), comments: z.string(),
company: company.nullish(), company: company.nullish(),
companyId: z.string(), companyId: z.string(),
id: z.string().optional(), id: z.string().optional(),
jobType: z.string(), jobType: z.string(),
location: z.string(), location: z.string(),
monthYearReceived: z.date(), monthYearReceived: z.date(),
negotiationStrategy: z.string().nullish(), negotiationStrategy: z.string(),
offersFullTimeId: z.string().nullish(), offersFullTimeId: z.string().nullish(),
offersInternId: z.string().nullish(), offersInternId: z.string().nullish(),
profileId: z.string().nullish(), profileId: z.string().nullish(),
@ -98,30 +102,6 @@ const reply = z.object({
userId: z.string().nullish(), userId: z.string().nullish(),
}); });
type WithIsEditable<T> = T & {
isEditable: boolean;
};
function computeIsEditable(
profileInput: OffersProfile,
editToken?: string,
): WithIsEditable<OffersProfile> {
return {
...profileInput,
isEditable: profileInput.editToken === editToken,
};
}
function exclude<Key extends keyof WithIsEditable<OffersProfile>>(
profile: WithIsEditable<OffersProfile>,
...keys: Array<Key>
): Omit<WithIsEditable<OffersProfile>, Key> {
for (const key of keys) {
delete profile[key];
}
return profile;
}
export const offersProfileRouter = createRouter() export const offersProfileRouter = createRouter()
.query('listOne', { .query('listOne', {
input: z.object({ input: z.object({
@ -131,6 +111,86 @@ export const offersProfileRouter = createRouter()
async resolve({ ctx, input }) { async resolve({ ctx, input }) {
const result = await ctx.prisma.offersProfile.findFirst({ const result = await ctx.prisma.offersProfile.findFirst({
include: { include: {
analysis: {
include: {
overallHighestOffer: {
include: {
OffersFullTime: {
include: {
totalCompensation: true,
},
},
OffersIntern: {
include: {
monthlySalary: true,
},
},
company: true,
profile: {
include: {
background: true,
},
},
},
},
topCompanyOffers: {
include: {
OffersFullTime: {
include: {
totalCompensation: true,
},
},
OffersIntern: {
include: {
monthlySalary: true,
},
},
company: true,
profile: {
include: {
background: {
include: {
experiences: {
include: {
company: true,
},
},
},
},
},
},
},
},
topOverallOffers: {
include: {
OffersFullTime: {
include: {
totalCompensation: true,
},
},
OffersIntern: {
include: {
monthlySalary: true,
},
},
company: true,
profile: {
include: {
background: {
include: {
experiences: {
include: {
company: true,
},
},
},
},
},
},
},
},
},
},
background: { background: {
include: { include: {
educations: true, educations: true,
@ -176,7 +236,7 @@ export const offersProfileRouter = createRouter()
}); });
if (result) { if (result) {
return exclude(computeIsEditable(result, input.token), 'editToken'); return profileDtoMapper(result, input.token);
} }
throw new trpc.TRPCError({ throw new trpc.TRPCError({
@ -422,41 +482,9 @@ export const offersProfileRouter = createRouter()
}, },
profileName: randomUUID().substring(0, 10), profileName: randomUUID().substring(0, 10),
}, },
include: {
background: {
include: {
educations: true,
experiences: {
include: {
company: true,
monthlySalary: true,
totalCompensation: true,
},
},
specificYoes: true,
},
},
offers: {
include: {
OffersFullTime: {
include: {
baseSalary: true,
bonus: true,
stocks: true,
totalCompensation: true,
},
},
OffersIntern: {
include: {
monthlySalary: true,
},
},
},
},
},
}); });
// TODO: add analysis to profile object then return
return profile; return createOfferProfileResponseMapper(profile, token);
}, },
}) })
.mutation('delete', { .mutation('delete', {
@ -990,7 +1018,7 @@ export const offersProfileRouter = createRouter()
} }
} }
} }
// TODO: add analysis to profile object then return
const result = await ctx.prisma.offersProfile.findFirst({ const result = await ctx.prisma.offersProfile.findFirst({
include: { include: {
background: { background: {
@ -1038,7 +1066,7 @@ export const offersProfileRouter = createRouter()
}); });
if (result) { if (result) {
return exclude(computeIsEditable(result, input.token), 'editToken'); return createOfferProfileResponseMapper(result, input.token);
} }
throw new trpc.TRPCError({ throw new trpc.TRPCError({
@ -1082,11 +1110,7 @@ export const offersProfileRouter = createRouter()
}, },
}); });
return { return addToProfileResponseMapper(updated);
id: updated.id,
profileName: updated.profileName,
userId: updated.userId,
};
} }
throw new trpc.TRPCError({ throw new trpc.TRPCError({

@ -1,8 +1,8 @@
import type { JobType } from '@prisma/client'; import type { JobType } from '@prisma/client';
export type OffersProfile = { export type Profile = {
analysis: OffersAnalysis; analysis: ProfileAnalysis?;
background: Background; background: Background?;
editToken: string?; editToken: string?;
id: string; id: string;
isEditable: boolean; isEditable: boolean;
@ -30,7 +30,7 @@ export type Experience = {
totalCompensation: Valuation?; totalCompensation: Valuation?;
}; };
export type Company = { export type OffersCompany = {
createdAt: Date; createdAt: Date;
description: string; description: string;
id: string; id: string;
@ -55,7 +55,6 @@ export type Education = {
}; };
export type SpecificYoe = { export type SpecificYoe = {
backgroundId: string;
domain: string; domain: string;
id: string; id: string;
yoe: number; yoe: number;
@ -78,11 +77,11 @@ export type ProfileOffer = {
location: string; location: string;
monthYearReceived: Date; monthYearReceived: Date;
negotiationStrategy: string; negotiationStrategy: string;
offersFullTime: OffersFullTime?; offersFullTime: FullTime?;
offersIntern: OffersIntern?; offersIntern: Intern?;
}; };
export type OffersFullTime = { export type FullTime = {
baseSalary: Valuation; baseSalary: Valuation;
bonus: Valuation; bonus: Valuation;
id: string; id: string;
@ -93,7 +92,7 @@ export type OffersFullTime = {
totalCompensation: Valuation; totalCompensation: Valuation;
}; };
export type OffersIntern = { export type Intern = {
id: string; id: string;
internshipCycle: string; internshipCycle: string;
monthlySalary: Valuation; monthlySalary: Valuation;
@ -132,7 +131,6 @@ export type Paging = {
}; };
export type CreateOfferProfileResponse = { export type CreateOfferProfileResponse = {
analysis: OffersAnalysis;
id: string; id: string;
token: string; token: string;
}; };
@ -141,7 +139,7 @@ export type OffersDiscussion = {
data: Array<OReply>; data: Array<OReply>;
}; };
export type OffersAnalysis = { export type ProfileAnalysis = {
companyAnalysis: Array<Analysis>; companyAnalysis: Array<Analysis>;
id: string; id: string;
overallAnalysis: Analysis; overallAnalysis: Analysis;
@ -179,3 +177,9 @@ export type AnalysisOffer = {
title: string; title: string;
totalYoe: number; totalYoe: number;
}; };
export type AddToProfileResponse = {
id: string;
profileName: string;
userId: string;
};
Loading…
Cancel
Save