diff --git a/apps/portal/prisma/schema.prisma b/apps/portal/prisma/schema.prisma index 07f5d51f..4085e88b 100644 --- a/apps/portal/prisma/schema.prisma +++ b/apps/portal/prisma/schema.prisma @@ -191,13 +191,13 @@ model OffersProfile { user User? @relation(fields: [userId], references: [id]) userId String? - OffersAnalysis OffersAnalysis? + analysis OffersAnalysis? } model OffersBackground { id String @id @default(cuid()) - totalYoe Int? + totalYoe Int specificYoes OffersSpecificYoe[] experiences OffersExperience[] // For extensibility in the future @@ -320,7 +320,6 @@ model OffersOffer { offersFullTimeId String? @unique OffersAnalysis OffersAnalysis? @relation("HighestOverallOffer") - OffersAnalysisTopOverallOffers OffersAnalysis[] @relation("TopOverallOffers") OffersAnalysisTopCompanyOffers OffersAnalysis[] @relation("TopCompanyOffers") } diff --git a/apps/portal/src/mappers/offers-mappers.ts b/apps/portal/src/mappers/offers-mappers.ts new file mode 100644 index 00000000..0e78d0f0 --- /dev/null +++ b/apps/portal/src/mappers/offers-mappers.ts @@ -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; + experiences: Array< + OffersExperience & { + company: Company | null; + monthlySalary: OffersCurrency | null; + totalCompensation: OffersCurrency | null; + } + >; + specificYoes: Array; + }) + | 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; + experiences: Array< + OffersExperience & { + company: Company | null; + monthlySalary: OffersCurrency | null; + totalCompensation: OffersCurrency | null; + } + >; + specificYoes: Array; + }) + | null; + discussion: Array< + OffersReply & { + replies: Array; + 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; +}; diff --git a/apps/portal/src/pages/offers/test/createProfile.tsx b/apps/portal/src/pages/offers/test/createProfile.tsx index 618ba886..6aad538b 100644 --- a/apps/portal/src/pages/offers/test/createProfile.tsx +++ b/apps/portal/src/pages/offers/test/createProfile.tsx @@ -7,7 +7,7 @@ function Test() { const [error, setError] = useState(''); const createMutation = trpc.useMutation(['offers.profile.create'], { - onError(err: any) { + onError(err) { alert(err); }, onSuccess(data) { @@ -18,7 +18,7 @@ function Test() { const addToUserProfileMutation = trpc.useMutation( ['offers.profile.addToUserProfile'], { - onError(err: any) { + onError(err) { alert(err); }, onSuccess(data) { @@ -28,7 +28,7 @@ function Test() { ); const deleteCommentMutation = trpc.useMutation(['offers.comments.delete'], { - onError(err: any) { + onError(err) { alert(err); }, onSuccess(data) { @@ -46,7 +46,7 @@ function Test() { }; const updateCommentMutation = trpc.useMutation(['offers.comments.update'], { - onError(err: any) { + onError(err) { alert(err); }, onSuccess(data) { @@ -64,7 +64,7 @@ function Test() { }; const createCommentMutation = trpc.useMutation(['offers.comments.create'], { - onError(err: any) { + onError(err) { alert(err); }, onSuccess(data) { @@ -151,8 +151,10 @@ function Test() { value: 104100, }, }, - comments: '', - companyId: 'cl9ec1mgg0000w33hg1a3612r', + + comments: 'I am a Raffles Institution almumni', + // Comments: '', + companyId: 'cl98yuqk80007txhgjtjp8fk4', jobType: 'FULLTIME', location: 'Singapore, Singapore', monthYearReceived: new Date('2022-09-30T07:58:54.000Z'), @@ -180,8 +182,8 @@ function Test() { value: 104100, }, }, - comments: "", - companyId: 'cl9ec1mgg0000w33hg1a3612r', + comments: '', + companyId: 'cl98yuqk80007txhgjtjp8fk4', jobType: 'FULLTIME', location: 'Singapore, Singapore', monthYearReceived: new Date('2022-09-30T07:58:54.000Z'), @@ -228,7 +230,7 @@ function Test() { }; const updateMutation = trpc.useMutation(['offers.profile.update'], { - onError(err: any) { + onError(err) { alert(err); }, onSuccess(response) { @@ -412,7 +414,7 @@ function Test() { totalCompensationId: 'cl96stky90039w32glbpktd0o', }, OffersIntern: null, - comments: null, + comments: '', company: { createdAt: new Date('2022-10-12T16:19:05.196Z'), description: @@ -465,7 +467,7 @@ function Test() { totalCompensationId: 'cl96stky9003jw32gzumcoi7v', }, OffersIntern: null, - comments: null, + comments: '', company: { createdAt: new Date('2022-10-12T16:19:05.196Z'), description: 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 3a836706..de92b546 100644 --- a/apps/portal/src/server/router/offers/offers-analysis-router.ts +++ b/apps/portal/src/server/router/offers/offers-analysis-router.ts @@ -8,9 +8,10 @@ import type { OffersOffer, OffersProfile, } from '@prisma/client'; -import { JobType } from '@prisma/client'; import { TRPCError } from '@trpc/server'; +import { profileAnalysisDtoMapper } from '~/mappers/offers-mappers'; + import { createRouter } from '../context'; const searchOfferPercentile = ( @@ -27,9 +28,19 @@ const searchOfferPercentile = ( company: Company; profile: OffersProfile & { background: OffersBackground | null }; }, - similarOffers: Array | 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++) { if (similarOffers[i].id === offer.id) { return i; @@ -39,116 +50,6 @@ const searchOfferPercentile = ( return -1; }; -const topPercentileDtoMapper = (topPercentileOffers: Array) => { - 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, -) => { - 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, - noOfSimilarCompanyOffers: number, - companyPercentile: number, - topPercentileCompanyOffers: Array, -) => { - return { - companyAnalysis: specificAnalysisDtoMapper( - noOfSimilarCompanyOffers, - companyPercentile, - topPercentileCompanyOffers, - ), - id: analysisId, - overallAnalysis: specificAnalysisDtoMapper( - noOfSimilarOffers, - overallPercentile, - topPercentileOffers, - ), - overallHighestOffer: highestOfferDtoMapper(overallHighestOffer), - profileId, - }; -}; - export const offersAnalysisRouter = createRouter() .query('generate', { input: z.object({ @@ -213,7 +114,7 @@ export const offersAnalysisRouter = createRouter() const overallHighestOffer = offers[0]; - // TODO: Shift yoe to background to make it mandatory + // TODO: Shift yoe out of background to make it mandatory if ( !overallHighestOffer.profile.background || !overallHighestOffer.profile.background.totalYoe @@ -465,17 +366,7 @@ export const offersAnalysisRouter = createRouter() }, }); - return profileAnalysisDtoMapper( - analysis.id, - analysis.profileId, - overallHighestOffer, - noOfSimilarOffers, - overallPercentile, - topPercentileOffers, - noOfSimilarCompanyOffers, - companyPercentile, - topPercentileCompanyOffers, - ); + return profileAnalysisDtoMapper(analysis); }, }) .query('get', { @@ -574,16 +465,6 @@ export const offersAnalysisRouter = createRouter() }); } - return profileAnalysisDtoMapper( - analysis.id, - analysis.profileId, - analysis.overallHighestOffer, - analysis.noOfSimilarOffers, - analysis.overallPercentile, - analysis.topOverallOffers, - analysis.noOfSimilarCompanyOffers, - analysis.companyPercentile, - analysis.topCompanyOffers, - ); + return profileAnalysisDtoMapper(analysis); }, - }); \ No newline at end of file + }); 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 b25870aa..b48e6836 100644 --- a/apps/portal/src/server/router/offers/offers-profile-router.ts +++ b/apps/portal/src/server/router/offers/offers-profile-router.ts @@ -2,9 +2,13 @@ import crypto, { randomUUID } from 'crypto'; import { z } from 'zod'; 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({ currency: z.string(), @@ -50,14 +54,14 @@ const offer = z.object({ totalCompensation: valuation.nullish(), // Full time }) .nullish(), - comments: z.string().nullish(), + comments: z.string(), company: company.nullish(), companyId: z.string(), id: z.string().optional(), jobType: z.string(), location: z.string(), monthYearReceived: z.date(), - negotiationStrategy: z.string().nullish(), + negotiationStrategy: z.string(), offersFullTimeId: z.string().nullish(), offersInternId: z.string().nullish(), profileId: z.string().nullish(), @@ -98,30 +102,6 @@ const reply = z.object({ userId: z.string().nullish(), }); -type WithIsEditable = T & { - isEditable: boolean; -}; - -function computeIsEditable( - profileInput: OffersProfile, - editToken?: string, -): WithIsEditable { - return { - ...profileInput, - isEditable: profileInput.editToken === editToken, - }; -} - -function exclude>( - profile: WithIsEditable, - ...keys: Array -): Omit, Key> { - for (const key of keys) { - delete profile[key]; - } - return profile; -} - export const offersProfileRouter = createRouter() .query('listOne', { input: z.object({ @@ -131,6 +111,86 @@ export const offersProfileRouter = createRouter() async resolve({ ctx, input }) { const result = await ctx.prisma.offersProfile.findFirst({ 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: { include: { educations: true, @@ -176,7 +236,7 @@ export const offersProfileRouter = createRouter() }); if (result) { - return exclude(computeIsEditable(result, input.token), 'editToken'); + return profileDtoMapper(result, input.token); } throw new trpc.TRPCError({ @@ -422,41 +482,9 @@ export const offersProfileRouter = createRouter() }, 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', { @@ -990,7 +1018,7 @@ export const offersProfileRouter = createRouter() } } } - // TODO: add analysis to profile object then return + const result = await ctx.prisma.offersProfile.findFirst({ include: { background: { @@ -1038,7 +1066,7 @@ export const offersProfileRouter = createRouter() }); if (result) { - return exclude(computeIsEditable(result, input.token), 'editToken'); + return createOfferProfileResponseMapper(result, input.token); } throw new trpc.TRPCError({ @@ -1082,11 +1110,7 @@ export const offersProfileRouter = createRouter() }, }); - return { - id: updated.id, - profileName: updated.profileName, - userId: updated.userId, - }; + return addToProfileResponseMapper(updated); } throw new trpc.TRPCError({ diff --git a/apps/portal/src/types/offers.d.ts b/apps/portal/src/types/offers.d.ts index ad5bce5f..291c44ba 100644 --- a/apps/portal/src/types/offers.d.ts +++ b/apps/portal/src/types/offers.d.ts @@ -1,8 +1,8 @@ import type { JobType } from '@prisma/client'; -export type OffersProfile = { - analysis: OffersAnalysis; - background: Background; +export type Profile = { + analysis: ProfileAnalysis?; + background: Background?; editToken: string?; id: string; isEditable: boolean; @@ -30,7 +30,7 @@ export type Experience = { totalCompensation: Valuation?; }; -export type Company = { +export type OffersCompany = { createdAt: Date; description: string; id: string; @@ -55,7 +55,6 @@ export type Education = { }; export type SpecificYoe = { - backgroundId: string; domain: string; id: string; yoe: number; @@ -78,11 +77,11 @@ export type ProfileOffer = { location: string; monthYearReceived: Date; negotiationStrategy: string; - offersFullTime: OffersFullTime?; - offersIntern: OffersIntern?; + offersFullTime: FullTime?; + offersIntern: Intern?; }; -export type OffersFullTime = { +export type FullTime = { baseSalary: Valuation; bonus: Valuation; id: string; @@ -93,7 +92,7 @@ export type OffersFullTime = { totalCompensation: Valuation; }; -export type OffersIntern = { +export type Intern = { id: string; internshipCycle: string; monthlySalary: Valuation; @@ -132,7 +131,6 @@ export type Paging = { }; export type CreateOfferProfileResponse = { - analysis: OffersAnalysis; id: string; token: string; }; @@ -141,7 +139,7 @@ export type OffersDiscussion = { data: Array; }; -export type OffersAnalysis = { +export type ProfileAnalysis = { companyAnalysis: Array; id: string; overallAnalysis: Analysis; @@ -179,3 +177,9 @@ export type AnalysisOffer = { title: string; totalYoe: number; }; + +export type AddToProfileResponse = { + id: string; + profileName: string; + userId: string; +}; \ No newline at end of file