mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-28 20:52:00 +08:00
[offers][refactor] add types for interfaces (#390)
* [offers][chore] Create types for API responses * [offers][fix] fix get comments bug * [offers][fix] make offers api open to unauthenticated users * [offers][chore] add return types to comment api * [offers][chore] add types to get comments api * [offers][chore] Refactor profile and analysis APIs to return defined types * [offers][chore] Add typed response for get offers API * [offers][chore] Changed delete offer API response * [offers][fix] Fix type definitions for OffersCompany in types/offers * [offers][fix] fix list some offer frontend Co-authored-by: BryannYeap <e0543723@u.nus.edu> Co-authored-by: Stuart Long Chay Boon <chayboon@gmail.com>
This commit is contained in:
@ -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
|
||||
@ -308,8 +308,8 @@ model OffersOffer {
|
||||
|
||||
monthYearReceived DateTime
|
||||
location String
|
||||
negotiationStrategy String?
|
||||
comments String?
|
||||
negotiationStrategy String
|
||||
comments String
|
||||
|
||||
jobType JobType
|
||||
|
||||
@ -320,7 +320,6 @@ model OffersOffer {
|
||||
offersFullTimeId String? @unique
|
||||
|
||||
OffersAnalysis OffersAnalysis? @relation("HighestOverallOffer")
|
||||
|
||||
OffersAnalysisTopOverallOffers OffersAnalysis[] @relation("TopOverallOffers")
|
||||
OffersAnalysisTopCompanyOffers OffersAnalysis[] @relation("TopCompanyOffers")
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import type { OfferTableRowData } from '~/components/offers/table/types';
|
||||
import type { DashboardOffer } from '../../../types/offers';
|
||||
import { convertCurrencyToString } from '../../../utils/offers/currency';
|
||||
import { formatDate } from '../../../utils/offers/time';
|
||||
|
||||
export type OfferTableRowProps = Readonly<{ row: OfferTableRowData }>;
|
||||
export type OfferTableRowProps = Readonly<{ row: DashboardOffer }>;
|
||||
|
||||
export default function OfferTableRow({
|
||||
row: { company, date, id, profileId, salary, title, yoe },
|
||||
row: { company, id, income, monthYearReceived, profileId, title, totalYoe },
|
||||
}: OfferTableRowProps) {
|
||||
return (
|
||||
<tr
|
||||
@ -14,12 +16,12 @@ export default function OfferTableRow({
|
||||
<th
|
||||
className="whitespace-nowrap py-4 px-6 font-medium text-gray-900 dark:text-white"
|
||||
scope="row">
|
||||
{company}
|
||||
{company.name}
|
||||
</th>
|
||||
<td className="py-4 px-6">{title}</td>
|
||||
<td className="py-4 px-6">{yoe}</td>
|
||||
<td className="py-4 px-6">{salary}</td>
|
||||
<td className="py-4 px-6">{date}</td>
|
||||
<td className="py-4 px-6">{totalYoe}</td>
|
||||
<td className="py-4 px-6">{convertCurrencyToString(income)}</td>
|
||||
<td className="py-4 px-6">{formatDate(monthYearReceived)}</td>
|
||||
<td className="space-x-4 py-4 px-6">
|
||||
<Link
|
||||
className="font-medium text-indigo-600 hover:underline dark:text-indigo-500"
|
||||
|
@ -2,18 +2,15 @@ import { useEffect, useState } from 'react';
|
||||
import { HorizontalDivider, Select, Spinner, Tabs } from '@tih/ui';
|
||||
|
||||
import OffersTablePagination from '~/components/offers/table/OffersTablePagination';
|
||||
import type {
|
||||
OfferTableRowData,
|
||||
PaginationType,
|
||||
} from '~/components/offers/table/types';
|
||||
import { YOE_CATEGORY } from '~/components/offers/table/types';
|
||||
|
||||
import CurrencySelector from '~/utils/offers/currency/CurrencySelector';
|
||||
import { formatDate } from '~/utils/offers/time';
|
||||
import { trpc } from '~/utils/trpc';
|
||||
|
||||
import OffersRow from './OffersRow';
|
||||
|
||||
import type { DashboardOffer, Paging } from '~/types/offers';
|
||||
|
||||
const NUMBER_OF_OFFERS_IN_PAGE = 10;
|
||||
export type OffersTableProps = Readonly<{
|
||||
companyFilter: string;
|
||||
@ -25,18 +22,18 @@ export default function OffersTable({
|
||||
}: OffersTableProps) {
|
||||
const [currency, setCurrency] = useState('SGD'); // TODO: Detect location
|
||||
const [selectedTab, setSelectedTab] = useState(YOE_CATEGORY.ENTRY);
|
||||
const [pagination, setPagination] = useState<PaginationType>({
|
||||
currentPage: 1,
|
||||
numOfItems: 1,
|
||||
const [pagination, setPagination] = useState<Paging>({
|
||||
currentPage: 0,
|
||||
numOfItems: 0,
|
||||
numOfPages: 0,
|
||||
totalItems: 0,
|
||||
});
|
||||
const [offers, setOffers] = useState<Array<OfferTableRowData>>([]);
|
||||
const [offers, setOffers] = useState<Array<DashboardOffer>>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setPagination({
|
||||
currentPage: 1,
|
||||
numOfItems: 1,
|
||||
currentPage: 0,
|
||||
numOfItems: 0,
|
||||
numOfPages: 0,
|
||||
totalItems: 0,
|
||||
});
|
||||
@ -48,7 +45,7 @@ export default function OffersTable({
|
||||
companyId: companyFilter,
|
||||
limit: NUMBER_OF_OFFERS_IN_PAGE,
|
||||
location: 'Singapore, Singapore', // TODO: Geolocation
|
||||
offset: pagination.currentPage - 1,
|
||||
offset: 0,
|
||||
sortBy: '-monthYearReceived',
|
||||
title: jobTitleFilter,
|
||||
yoeCategory: selectedTab,
|
||||
@ -56,28 +53,19 @@ export default function OffersTable({
|
||||
],
|
||||
{
|
||||
onSuccess: (response) => {
|
||||
const filteredData = response.data.map((res) => {
|
||||
return {
|
||||
company: res.company.name,
|
||||
date: formatDate(res.monthYearReceived),
|
||||
id: res.OffersFullTime
|
||||
? res.OffersFullTime!.id
|
||||
: res.OffersIntern!.id,
|
||||
profileId: res.profileId,
|
||||
salary: res.OffersFullTime
|
||||
? res.OffersFullTime?.totalCompensation.value
|
||||
: res.OffersIntern?.monthlySalary.value,
|
||||
title: res.OffersFullTime ? res.OffersFullTime?.level : '',
|
||||
yoe: 100,
|
||||
};
|
||||
});
|
||||
setOffers(filteredData);
|
||||
setPagination({
|
||||
currentPage: (response.paging.currPage as number) + 1,
|
||||
numOfItems: response.paging.numOfItemsInPage,
|
||||
numOfPages: response.paging.numOfPages,
|
||||
totalItems: response.paging.totalNumberOfOffers,
|
||||
});
|
||||
// Const filteredData = response.data.map((res) => {
|
||||
// return {
|
||||
// company: res.company.name,
|
||||
// date: res.monthYearReceived,
|
||||
// id: res.id,
|
||||
// profileId: res.profileId,
|
||||
// income: res.income,
|
||||
// title: res.title,
|
||||
// yoe: res.totalYoe,
|
||||
// };
|
||||
// });
|
||||
setOffers(response.data);
|
||||
setPagination(response.paging);
|
||||
},
|
||||
},
|
||||
);
|
||||
@ -90,15 +78,15 @@ export default function OffersTable({
|
||||
label="Table Navigation"
|
||||
tabs={[
|
||||
{
|
||||
label: 'Fresh Grad (0-3 YOE)',
|
||||
label: 'Fresh Grad (0-2 YOE)',
|
||||
value: YOE_CATEGORY.ENTRY,
|
||||
},
|
||||
{
|
||||
label: 'Mid (4-7 YOE)',
|
||||
label: 'Mid (3-5 YOE)',
|
||||
value: YOE_CATEGORY.MID,
|
||||
},
|
||||
{
|
||||
label: 'Senior (8+ YOE)',
|
||||
label: 'Senior (6+ YOE)',
|
||||
value: YOE_CATEGORY.SENIOR,
|
||||
},
|
||||
{
|
||||
@ -187,14 +175,11 @@ export default function OffersTable({
|
||||
)}
|
||||
<OffersTablePagination
|
||||
endNumber={
|
||||
(pagination.currentPage - 1) * NUMBER_OF_OFFERS_IN_PAGE +
|
||||
offers.length
|
||||
pagination.currentPage * NUMBER_OF_OFFERS_IN_PAGE + offers.length
|
||||
}
|
||||
handlePageChange={handlePageChange}
|
||||
pagination={pagination}
|
||||
startNumber={
|
||||
(pagination.currentPage - 1) * NUMBER_OF_OFFERS_IN_PAGE + 1
|
||||
}
|
||||
startNumber={pagination.currentPage * NUMBER_OF_OFFERS_IN_PAGE + 1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { Pagination } from '@tih/ui';
|
||||
|
||||
import type { PaginationType } from '~/components/offers/table/types';
|
||||
import type { Paging } from '~/types/offers';
|
||||
|
||||
type OffersTablePaginationProps = Readonly<{
|
||||
endNumber: number;
|
||||
handlePageChange: (page: number) => void;
|
||||
pagination: PaginationType;
|
||||
pagination: Paging;
|
||||
startNumber: number;
|
||||
}>;
|
||||
|
||||
@ -30,13 +30,13 @@ export default function OffersTablePagination({
|
||||
</span>
|
||||
</span>
|
||||
<Pagination
|
||||
current={pagination.currentPage}
|
||||
current={pagination.currentPage + 1}
|
||||
end={pagination.numOfPages}
|
||||
label="Pagination"
|
||||
pagePadding={1}
|
||||
start={1}
|
||||
onSelect={(currPage) => {
|
||||
handlePageChange(currPage);
|
||||
handlePageChange(currPage - 1);
|
||||
}}
|
||||
/>
|
||||
</nav>
|
||||
|
@ -1,13 +1,3 @@
|
||||
export type OfferTableRowData = {
|
||||
company: string;
|
||||
date: string;
|
||||
id: string;
|
||||
profileId: string;
|
||||
salary: number | undefined;
|
||||
title: string;
|
||||
yoe: number;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
export enum YOE_CATEGORY {
|
||||
INTERN = 0,
|
||||
|
574
apps/portal/src/mappers/offers-mappers.ts
Normal file
574
apps/portal/src/mappers/offers-mappers.ts
Normal file
@ -0,0 +1,574 @@
|
||||
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,
|
||||
DashboardOffer,
|
||||
Education,
|
||||
Experience,
|
||||
GetOffersResponse,
|
||||
OffersCompany,
|
||||
Paging,
|
||||
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) => {
|
||||
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: experience.company
|
||||
? offersCompanyDtoMapper(experience.company)
|
||||
: null,
|
||||
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;
|
||||
};
|
||||
|
||||
export const dashboardOfferDtoMapper = (
|
||||
offer: OffersOffer & {
|
||||
OffersFullTime:
|
||||
| (OffersFullTime & {
|
||||
baseSalary: OffersCurrency;
|
||||
bonus: OffersCurrency;
|
||||
stocks: OffersCurrency;
|
||||
totalCompensation: OffersCurrency;
|
||||
})
|
||||
| null;
|
||||
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
|
||||
company: Company;
|
||||
profile: OffersProfile & { background: OffersBackground | null };
|
||||
},
|
||||
) => {
|
||||
const dashboardOfferDto: DashboardOffer = {
|
||||
company: offersCompanyDtoMapper(offer.company),
|
||||
id: offer.id,
|
||||
income: valuationDtoMapper({ currency: '', value: -1 }),
|
||||
monthYearReceived: offer.monthYearReceived,
|
||||
profileId: offer.profileId,
|
||||
title: offer.OffersFullTime?.title ?? '',
|
||||
totalYoe: offer.profile.background?.totalYoe ?? -1,
|
||||
};
|
||||
|
||||
if (offer.OffersFullTime) {
|
||||
dashboardOfferDto.income = valuationDtoMapper(
|
||||
offer.OffersFullTime.totalCompensation,
|
||||
);
|
||||
} else if (offer.OffersIntern) {
|
||||
dashboardOfferDto.income = valuationDtoMapper(
|
||||
offer.OffersIntern.monthlySalary,
|
||||
);
|
||||
}
|
||||
|
||||
return dashboardOfferDto;
|
||||
};
|
||||
|
||||
export const getOffersResponseMapper = (
|
||||
data: Array<DashboardOffer>,
|
||||
paging: Paging,
|
||||
) => {
|
||||
const getOffersResponse: GetOffersResponse = {
|
||||
data,
|
||||
paging,
|
||||
};
|
||||
return getOffersResponse;
|
||||
};
|
@ -43,25 +43,23 @@ export default function OfferProfile() {
|
||||
if (data?.offers) {
|
||||
const filteredOffers: Array<OfferEntity> = data
|
||||
? data?.offers.map((res) => {
|
||||
if (res.OfferFullTime) {
|
||||
if (res.offersFullTime) {
|
||||
const filteredOffer: OfferEntity = {
|
||||
base: convertCurrencyToString(
|
||||
res.OfferFullTime.baseSalary,
|
||||
),
|
||||
bonus: convertCurrencyToString(
|
||||
res.OfferFullTime.bonus,
|
||||
res.offersFullTime.baseSalary,
|
||||
),
|
||||
bonus: convertCurrencyToString(res.offersFullTime.bonus),
|
||||
companyName: res.company.name,
|
||||
id: res.OfferFullTime.id,
|
||||
jobLevel: res.OfferFullTime.level,
|
||||
jobTitle: res.OfferFullTime.title,
|
||||
id: res.offersFullTime.id,
|
||||
jobLevel: res.offersFullTime.level,
|
||||
jobTitle: res.offersFullTime.title,
|
||||
location: res.location,
|
||||
negotiationStrategy: res.negotiationStrategy || '',
|
||||
otherComment: res.comments || '',
|
||||
receivedMonth: formatDate(res.monthYearReceived),
|
||||
stocks: convertCurrencyToString(res.OfferFullTime.stocks),
|
||||
stocks: convertCurrencyToString(res.offersFullTime.stocks),
|
||||
totalCompensation: convertCurrencyToString(
|
||||
res.OfferFullTime.totalCompensation,
|
||||
res.offersFullTime.totalCompensation,
|
||||
),
|
||||
};
|
||||
|
||||
@ -69,11 +67,11 @@ export default function OfferProfile() {
|
||||
}
|
||||
const filteredOffer: OfferEntity = {
|
||||
companyName: res.company.name,
|
||||
id: res.OfferIntern!.id,
|
||||
jobTitle: res.OfferIntern!.title,
|
||||
id: res.offersIntern!.id,
|
||||
jobTitle: res.offersIntern!.title,
|
||||
location: res.location,
|
||||
monthlySalary: convertCurrencyToString(
|
||||
res.OfferIntern!.monthlySalary,
|
||||
res.offersIntern!.monthlySalary,
|
||||
),
|
||||
negotiationStrategy: res.negotiationStrategy || '',
|
||||
otherComment: res.comments || '',
|
||||
|
@ -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) {
|
||||
@ -74,17 +74,18 @@ function Test() {
|
||||
|
||||
const handleCreate = () => {
|
||||
createCommentMutation.mutate({
|
||||
message: 'hello',
|
||||
profileId: 'cl96stky5002ew32gx2kale2x',
|
||||
// UserId: 'cl97dl51k001e7iygd5v5gt58'
|
||||
message: 'wassup bro',
|
||||
profileId: 'cl9efyn9p004ww3u42mjgl1vn',
|
||||
replyingToId: 'cl9el4xj10001w3w21o3p2iny',
|
||||
userId: 'cl9ehvpng0000w3ec2mpx0bdd'
|
||||
});
|
||||
};
|
||||
|
||||
const handleLink = () => {
|
||||
addToUserProfileMutation.mutate({
|
||||
profileId: 'cl96stky5002ew32gx2kale2x',
|
||||
profileId: 'cl9efyn9p004ww3u42mjgl1vn',
|
||||
token: 'afca11e436d21bde24543718fa957c6c625335439dc504f24ee35eae7b5ef1ba',
|
||||
userId: 'cl97dl51k001e7iygd5v5gt58',
|
||||
userId: 'cl9ehvpng0000w3ec2mpx0bdd',
|
||||
});
|
||||
};
|
||||
|
||||
@ -102,7 +103,7 @@ function Test() {
|
||||
],
|
||||
experiences: [
|
||||
{
|
||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||
durationInMonths: 24,
|
||||
jobType: 'FULLTIME',
|
||||
level: 'Junior',
|
||||
@ -150,6 +151,8 @@ function Test() {
|
||||
value: 104100,
|
||||
},
|
||||
},
|
||||
|
||||
comments: 'I am a Raffles Institution almumni',
|
||||
// Comments: '',
|
||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||
jobType: 'FULLTIME',
|
||||
@ -179,25 +182,25 @@ function Test() {
|
||||
value: 104100,
|
||||
},
|
||||
},
|
||||
comments: undefined,
|
||||
comments: '',
|
||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||
jobType: 'FULLTIME',
|
||||
location: 'Singapore, Singapore',
|
||||
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
|
||||
// NegotiationStrategy: 'Leveraged having multiple offers',
|
||||
negotiationStrategy: 'Leveraged having multiple offers',
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
const profileId = 'cl99fhrsf00007ijpbrdk8gue'; // Remember to change this filed after testing deleting
|
||||
const profileId = 'cl9efyn9p004ww3u42mjgl1vn'; // Remember to change this filed after testing deleting
|
||||
const data = trpc.useQuery(
|
||||
[
|
||||
`offers.profile.listOne`,
|
||||
{
|
||||
profileId,
|
||||
token:
|
||||
'e7effd2a40adba2deb1ddea4fb9f1e6c3c98ab0a85a88ed1567fc2a107fdb445',
|
||||
'd14666ff76e267c9e99445844b41410e83874936d0c07e664db73ff0ea76919e',
|
||||
},
|
||||
],
|
||||
{
|
||||
@ -216,6 +219,7 @@ function Test() {
|
||||
},
|
||||
);
|
||||
|
||||
// Console.log(replies.data?.data)
|
||||
const deleteMutation = trpc.useMutation(['offers.profile.delete']);
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
@ -226,7 +230,7 @@ function Test() {
|
||||
};
|
||||
|
||||
const updateMutation = trpc.useMutation(['offers.profile.update'], {
|
||||
onError(err: any) {
|
||||
onError(err) {
|
||||
alert(err);
|
||||
},
|
||||
onSuccess(response) {
|
||||
@ -261,7 +265,7 @@ function Test() {
|
||||
slug: 'meta',
|
||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||
},
|
||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||
durationInMonths: 24,
|
||||
id: 'cl96stky6002iw32gpt6t87s2',
|
||||
jobType: 'FULLTIME',
|
||||
@ -368,7 +372,7 @@ function Test() {
|
||||
slug: 'meta',
|
||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||
},
|
||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||
id: 'cl976t4de00047iygl0zbce11',
|
||||
jobType: 'FULLTIME',
|
||||
location: 'Singapore, Singapore',
|
||||
@ -410,7 +414,7 @@ function Test() {
|
||||
totalCompensationId: 'cl96stky90039w32glbpktd0o',
|
||||
},
|
||||
OffersIntern: null,
|
||||
comments: null,
|
||||
comments: '',
|
||||
company: {
|
||||
createdAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||
description:
|
||||
@ -421,7 +425,7 @@ function Test() {
|
||||
slug: 'meta',
|
||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||
},
|
||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||
id: 'cl96stky80031w32gau9mu1gs',
|
||||
jobType: 'FULLTIME',
|
||||
location: 'Singapore, Singapore',
|
||||
@ -463,7 +467,7 @@ function Test() {
|
||||
totalCompensationId: 'cl96stky9003jw32gzumcoi7v',
|
||||
},
|
||||
OffersIntern: null,
|
||||
comments: null,
|
||||
comments: '',
|
||||
company: {
|
||||
createdAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||
description:
|
||||
@ -474,7 +478,7 @@ function Test() {
|
||||
slug: 'meta',
|
||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||
},
|
||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||
id: 'cl96stky9003bw32gc3l955vr',
|
||||
jobType: 'FULLTIME',
|
||||
location: 'Singapore, Singapore',
|
||||
@ -527,7 +531,7 @@ function Test() {
|
||||
slug: 'meta',
|
||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||
},
|
||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||
id: 'cl976wf28000t7iyga4noyz7s',
|
||||
jobType: 'FULLTIME',
|
||||
location: 'Singapore, Singapore',
|
||||
@ -580,7 +584,7 @@ function Test() {
|
||||
slug: 'meta',
|
||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||
},
|
||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||
id: 'cl96tbb3o0051w32gjrpaiiit',
|
||||
jobType: 'FULLTIME',
|
||||
location: 'Singapore, Singapore',
|
||||
@ -600,7 +604,7 @@ function Test() {
|
||||
return (
|
||||
<>
|
||||
<div>{createdData}</div>
|
||||
<div>{JSON.stringify(replies.data)}</div>
|
||||
<div>{JSON.stringify(replies.data?.data)}</div>
|
||||
<button type="button" onClick={handleClick}>
|
||||
Click Me!
|
||||
</button>
|
||||
|
@ -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<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++) {
|
||||
if (similarOffers[i].id === offer.id) {
|
||||
return i;
|
||||
@ -39,116 +50,6 @@ const searchOfferPercentile = (
|
||||
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()
|
||||
.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);
|
||||
},
|
||||
});
|
||||
|
@ -1,264 +1,329 @@
|
||||
import { z } from 'zod';
|
||||
import * as trpc from '@trpc/server';
|
||||
|
||||
import { createProtectedRouter } from '../context';
|
||||
import { createRouter } from '../context';
|
||||
|
||||
import type { Reply } from '~/types/offers-profile';
|
||||
import type { OffersDiscussion, Reply } from '~/types/offers';
|
||||
|
||||
export const offersCommentsRouter = createRouter()
|
||||
.query('getComments', {
|
||||
input: z.object({
|
||||
profileId: z.string(),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const profile = await ctx.prisma.offersProfile.findFirst({
|
||||
where: {
|
||||
id: input.profileId,
|
||||
},
|
||||
});
|
||||
|
||||
export const offersCommentsRouter = createProtectedRouter()
|
||||
.query('getComments', {
|
||||
input: z.object({
|
||||
profileId: z.string()
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
|
||||
const profile = await ctx.prisma.offersProfile.findFirst({
|
||||
where: {
|
||||
id: input.profileId
|
||||
}
|
||||
})
|
||||
|
||||
const result = await ctx.prisma.offersProfile.findFirst({
|
||||
const result = await ctx.prisma.offersProfile.findFirst({
|
||||
include: {
|
||||
discussion: {
|
||||
include: {
|
||||
replies: {
|
||||
include: {
|
||||
discussion: {
|
||||
include: {
|
||||
replies: {
|
||||
include: {
|
||||
user: true
|
||||
}
|
||||
},
|
||||
replyingTo: true,
|
||||
user: true
|
||||
}
|
||||
}
|
||||
user: true,
|
||||
},
|
||||
where: {
|
||||
id: input.profileId
|
||||
}
|
||||
},
|
||||
replyingTo: true,
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.profileId,
|
||||
},
|
||||
});
|
||||
|
||||
const discussions: OffersDiscussion = {
|
||||
data: result?.discussion
|
||||
.filter((x) => {
|
||||
return x.replyingToId === null
|
||||
})
|
||||
.map((x) => {
|
||||
if (x.user == null) {
|
||||
x.user = {
|
||||
email: '',
|
||||
emailVerified: null,
|
||||
id: '',
|
||||
image: '',
|
||||
name: profile?.profileName ?? '<missing name>',
|
||||
};
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return result.discussion
|
||||
.filter((x: Reply) => x.replyingToId === null)
|
||||
.map((x: Reply) => {
|
||||
if (x.user == null) {
|
||||
x.user = {
|
||||
email: "",
|
||||
emailVerified: null,
|
||||
id: "",
|
||||
image: "",
|
||||
name: profile?.profileName ?? "<missing name>"
|
||||
}
|
||||
}
|
||||
|
||||
x.replies?.map((y) => {
|
||||
if (y.user == null) {
|
||||
y.user = {
|
||||
email: "",
|
||||
emailVerified: null,
|
||||
id: "",
|
||||
image: "",
|
||||
name: profile?.profileName ?? "<missing name>"
|
||||
}
|
||||
}
|
||||
})
|
||||
return x;
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
})
|
||||
.mutation("create", {
|
||||
input: z.object({
|
||||
message: z.string(),
|
||||
profileId: z.string(),
|
||||
replyingToId: z.string().optional(),
|
||||
userId: z.string().optional()
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const createdReply = await ctx.prisma.offersReply.create({
|
||||
data: {
|
||||
message: input.message,
|
||||
profile: {
|
||||
connect: {
|
||||
id: input.profileId
|
||||
}
|
||||
}
|
||||
x.replies?.map((y) => {
|
||||
if (y.user == null) {
|
||||
y.user = {
|
||||
email: '',
|
||||
emailVerified: null,
|
||||
id: '',
|
||||
image: '',
|
||||
name: profile?.profileName ?? '<missing name>',
|
||||
};
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (input.replyingToId) {
|
||||
await ctx.prisma.offersReply.update({
|
||||
data: {
|
||||
replyingTo: {
|
||||
connect: {
|
||||
id: input.replyingToId
|
||||
}
|
||||
}
|
||||
},
|
||||
where: {
|
||||
id: createdReply.id
|
||||
}
|
||||
})
|
||||
}
|
||||
const replyType: Reply = {
|
||||
createdAt: x.createdAt,
|
||||
id: x.id,
|
||||
message: x.message,
|
||||
replies: x.replies.map((reply) => {
|
||||
return {
|
||||
createdAt: reply.createdAt,
|
||||
id: reply.id,
|
||||
message: reply.message,
|
||||
replies: [],
|
||||
replyingToId: reply.replyingToId,
|
||||
user: reply.user
|
||||
}
|
||||
}),
|
||||
replyingToId: x.replyingToId,
|
||||
user: x.user
|
||||
}
|
||||
|
||||
if (input.userId) {
|
||||
await ctx.prisma.offersReply.update({
|
||||
data: {
|
||||
user: {
|
||||
connect: {
|
||||
id: input.userId
|
||||
}
|
||||
}
|
||||
},
|
||||
where: {
|
||||
id: createdReply.id
|
||||
}
|
||||
})
|
||||
}
|
||||
// Get replies
|
||||
const result = await ctx.prisma.offersProfile.findFirst({
|
||||
include: {
|
||||
discussion: {
|
||||
include: {
|
||||
replies: true,
|
||||
replyingTo: true,
|
||||
user: true
|
||||
}
|
||||
}
|
||||
},
|
||||
where: {
|
||||
id: input.profileId
|
||||
}
|
||||
})
|
||||
return replyType
|
||||
}) ?? []
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return result.discussion.filter((x) => x.replyingToId === null)
|
||||
}
|
||||
return discussions
|
||||
},
|
||||
})
|
||||
.mutation('create', {
|
||||
input: z.object({
|
||||
message: z.string(),
|
||||
profileId: z.string(),
|
||||
replyingToId: z.string().optional(),
|
||||
token: z.string().optional(),
|
||||
userId: z.string().optional()
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const profile = await ctx.prisma.offersProfile.findFirst({
|
||||
where: {
|
||||
id: input.profileId,
|
||||
},
|
||||
});
|
||||
|
||||
return result
|
||||
}
|
||||
})
|
||||
.mutation("update", {
|
||||
input: z.object({
|
||||
id: z.string(),
|
||||
message: z.string(),
|
||||
profileId: z.string(),
|
||||
// Have to pass in either userID or token for validation
|
||||
token: z.string().optional(),
|
||||
userId: z.string().optional(),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const messageToUpdate = await ctx.prisma.offersReply.findFirst({
|
||||
where: {
|
||||
id: input.id
|
||||
}
|
||||
})
|
||||
const profile = await ctx.prisma.offersProfile.findFirst({
|
||||
where: {
|
||||
const profileEditToken = profile?.editToken;
|
||||
|
||||
if (input.token === profileEditToken || input.userId) {
|
||||
const createdReply = await ctx.prisma.offersReply.create({
|
||||
data: {
|
||||
message: input.message,
|
||||
profile: {
|
||||
connect: {
|
||||
id: input.profileId,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (input.replyingToId) {
|
||||
await ctx.prisma.offersReply.update({
|
||||
data: {
|
||||
replyingTo: {
|
||||
connect: {
|
||||
id: input.replyingToId,
|
||||
},
|
||||
});
|
||||
|
||||
const profileEditToken = profile?.editToken;
|
||||
|
||||
// To validate user editing, OP or correct user
|
||||
// TODO: improve validation process
|
||||
if (profileEditToken === input.token || messageToUpdate?.userId === input.userId) {
|
||||
await ctx.prisma.offersReply.update({
|
||||
data: {
|
||||
message: input.message
|
||||
},
|
||||
where: {
|
||||
id: input.id
|
||||
}
|
||||
})
|
||||
|
||||
const result = await ctx.prisma.offersProfile.findFirst({
|
||||
include: {
|
||||
discussion: {
|
||||
include: {
|
||||
replies: true,
|
||||
replyingTo: true,
|
||||
user: true
|
||||
}
|
||||
}
|
||||
},
|
||||
where: {
|
||||
id: input.profileId
|
||||
}
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return result.discussion.filter((x) => x.replyingToId === null)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
throw new trpc.TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'Wrong userId or token.'
|
||||
})
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: createdReply.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
.mutation("delete", {
|
||||
input: z.object({
|
||||
id: z.string(),
|
||||
profileId: z.string(),
|
||||
// Have to pass in either userID or token for validation
|
||||
token: z.string().optional(),
|
||||
userId: z.string().optional(),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const messageToDelete = await ctx.prisma.offersReply.findFirst({
|
||||
where: {
|
||||
id: input.id
|
||||
}
|
||||
})
|
||||
const profile = await ctx.prisma.offersProfile.findFirst({
|
||||
where: {
|
||||
id: input.profileId,
|
||||
|
||||
if (input.userId) {
|
||||
await ctx.prisma.offersReply.update({
|
||||
data: {
|
||||
user: {
|
||||
connect: {
|
||||
id: input.userId,
|
||||
},
|
||||
});
|
||||
|
||||
const profileEditToken = profile?.editToken;
|
||||
|
||||
// To validate user editing, OP or correct user
|
||||
// TODO: improve validation process
|
||||
if (profileEditToken === input.token || messageToDelete?.userId === input.userId) {
|
||||
await ctx.prisma.offersReply.delete({
|
||||
where: {
|
||||
id: input.id
|
||||
}
|
||||
})
|
||||
const result = await ctx.prisma.offersProfile.findFirst({
|
||||
include: {
|
||||
discussion: {
|
||||
include: {
|
||||
replies: true,
|
||||
replyingTo: true,
|
||||
user: true
|
||||
}
|
||||
}
|
||||
},
|
||||
where: {
|
||||
id: input.profileId
|
||||
}
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return result.discussion.filter((x) => x.replyingToId === null)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
throw new trpc.TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'Wrong userId or token.'
|
||||
})
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: createdReply.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
const created = await ctx.prisma.offersReply.findFirst({
|
||||
include: {
|
||||
user: true
|
||||
},
|
||||
where: {
|
||||
id: createdReply.id,
|
||||
},
|
||||
});
|
||||
|
||||
const result: Reply = {
|
||||
createdAt: created!.createdAt,
|
||||
id: created!.id,
|
||||
message: created!.message,
|
||||
replies: [], // New message should have no replies
|
||||
replyingToId: created!.replyingToId,
|
||||
user: created!.user ?? {
|
||||
email: '',
|
||||
emailVerified: null,
|
||||
id: '',
|
||||
image: '',
|
||||
name: profile?.profileName ?? '<missing name>',
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
throw new trpc.TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'Missing userId or wrong token.',
|
||||
});
|
||||
},
|
||||
})
|
||||
.mutation('update', {
|
||||
input: z.object({
|
||||
id: z.string(),
|
||||
message: z.string(),
|
||||
profileId: z.string(),
|
||||
// Have to pass in either userID or token for validation
|
||||
token: z.string().optional(),
|
||||
userId: z.string().optional(),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const messageToUpdate = await ctx.prisma.offersReply.findFirst({
|
||||
where: {
|
||||
id: input.id,
|
||||
},
|
||||
});
|
||||
const profile = await ctx.prisma.offersProfile.findFirst({
|
||||
where: {
|
||||
id: input.profileId,
|
||||
},
|
||||
});
|
||||
|
||||
const profileEditToken = profile?.editToken;
|
||||
|
||||
// To validate user editing, OP or correct user
|
||||
// TODO: improve validation process
|
||||
if (
|
||||
profileEditToken === input.token ||
|
||||
messageToUpdate?.userId === input.userId
|
||||
) {
|
||||
const updated = await ctx.prisma.offersReply.update({
|
||||
data: {
|
||||
message: input.message,
|
||||
},
|
||||
include: {
|
||||
replies: {
|
||||
include: {
|
||||
user: true
|
||||
}
|
||||
},
|
||||
user: true
|
||||
},
|
||||
where: {
|
||||
id: input.id,
|
||||
},
|
||||
});
|
||||
|
||||
const result: Reply = {
|
||||
createdAt: updated!.createdAt,
|
||||
id: updated!.id,
|
||||
message: updated!.message,
|
||||
replies: updated!.replies.map((x) => {
|
||||
return {
|
||||
createdAt: x.createdAt,
|
||||
id: x.id,
|
||||
message: x.message,
|
||||
replies: [],
|
||||
replyingToId: x.replyingToId,
|
||||
user: x.user ?? {
|
||||
email: '',
|
||||
emailVerified: null,
|
||||
id: '',
|
||||
image: '',
|
||||
name: profile?.profileName ?? '<missing name>',
|
||||
}
|
||||
}
|
||||
}),
|
||||
replyingToId: updated!.replyingToId,
|
||||
user: updated!.user ?? {
|
||||
email: '',
|
||||
emailVerified: null,
|
||||
id: '',
|
||||
image: '',
|
||||
name: profile?.profileName ?? '<missing name>',
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
throw new trpc.TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'Wrong userId or token.',
|
||||
});
|
||||
},
|
||||
})
|
||||
.mutation('delete', {
|
||||
input: z.object({
|
||||
id: z.string(),
|
||||
profileId: z.string(),
|
||||
// Have to pass in either userID or token for validation
|
||||
token: z.string().optional(),
|
||||
userId: z.string().optional(),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const messageToDelete = await ctx.prisma.offersReply.findFirst({
|
||||
where: {
|
||||
id: input.id,
|
||||
},
|
||||
});
|
||||
const profile = await ctx.prisma.offersProfile.findFirst({
|
||||
where: {
|
||||
id: input.profileId,
|
||||
},
|
||||
});
|
||||
|
||||
const profileEditToken = profile?.editToken;
|
||||
|
||||
// To validate user editing, OP or correct user
|
||||
// TODO: improve validation process
|
||||
if (
|
||||
profileEditToken === input.token ||
|
||||
messageToDelete?.userId === input.userId
|
||||
) {
|
||||
await ctx.prisma.offersReply.delete({
|
||||
where: {
|
||||
id: input.id,
|
||||
},
|
||||
});
|
||||
await ctx.prisma.offersProfile.findFirst({
|
||||
include: {
|
||||
discussion: {
|
||||
include: {
|
||||
replies: true,
|
||||
replyingTo: true,
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.profileId,
|
||||
},
|
||||
});
|
||||
|
||||
// If (result) {
|
||||
// return result.discussion.filter((x) => x.replyingToId === null);
|
||||
// }
|
||||
|
||||
// return result;
|
||||
}
|
||||
|
||||
throw new trpc.TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'Wrong userId or token.',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -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-profile';
|
||||
import { createRouter } from '../context';
|
||||
|
||||
const valuation = z.object({
|
||||
currency: z.string(),
|
||||
@ -19,41 +23,45 @@ const company = z.object({
|
||||
logoUrl: z.string().nullish(),
|
||||
name: z.string(),
|
||||
slug: z.string(),
|
||||
updatedAt: z.date()
|
||||
})
|
||||
updatedAt: z.date(),
|
||||
});
|
||||
|
||||
const offer = z.object({
|
||||
OffersFullTime: z.object({
|
||||
baseSalary: valuation.nullish(),
|
||||
baseSalaryId: z.string().nullish(),
|
||||
bonus: valuation.nullish(),
|
||||
bonusId: z.string().nullish(),
|
||||
id: z.string().optional(),
|
||||
level: z.string().nullish(),
|
||||
specialization: z.string(),
|
||||
stocks: valuation.nullish(),
|
||||
stocksId: z.string().nullish(),
|
||||
title: z.string(),
|
||||
totalCompensation: valuation.nullish(),
|
||||
totalCompensationId: z.string().nullish(),
|
||||
}).nullish(),
|
||||
OffersIntern: z.object({
|
||||
id: z.string().optional(),
|
||||
internshipCycle: z.string().nullish(),
|
||||
monthlySalary: valuation.nullish(),
|
||||
specialization: z.string(),
|
||||
startYear: z.number().nullish(),
|
||||
title: z.string(),
|
||||
totalCompensation: valuation.nullish(), // Full time
|
||||
}).nullish(),
|
||||
comments: z.string().nullish(),
|
||||
OffersFullTime: z
|
||||
.object({
|
||||
baseSalary: valuation.nullish(),
|
||||
baseSalaryId: z.string().nullish(),
|
||||
bonus: valuation.nullish(),
|
||||
bonusId: z.string().nullish(),
|
||||
id: z.string().optional(),
|
||||
level: z.string().nullish(),
|
||||
specialization: z.string(),
|
||||
stocks: valuation.nullish(),
|
||||
stocksId: z.string().nullish(),
|
||||
title: z.string(),
|
||||
totalCompensation: valuation.nullish(),
|
||||
totalCompensationId: z.string().nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
OffersIntern: z
|
||||
.object({
|
||||
id: z.string().optional(),
|
||||
internshipCycle: z.string().nullish(),
|
||||
monthlySalary: valuation.nullish(),
|
||||
specialization: z.string(),
|
||||
startYear: z.number().nullish(),
|
||||
title: z.string(),
|
||||
totalCompensation: valuation.nullish(), // Full time
|
||||
})
|
||||
.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(),
|
||||
@ -72,7 +80,7 @@ const experience = z.object({
|
||||
specialization: z.string().nullish(),
|
||||
title: z.string().nullish(),
|
||||
totalCompensation: valuation.nullish(),
|
||||
totalCompensationId: z.string().nullish()
|
||||
totalCompensationId: z.string().nullish(),
|
||||
});
|
||||
|
||||
const education = z.object({
|
||||
@ -91,32 +99,8 @@ const reply = z.object({
|
||||
messages: z.string().nullish(),
|
||||
profileId: z.string().nullish(),
|
||||
replyingToId: 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;
|
||||
}
|
||||
userId: z.string().nullish(),
|
||||
});
|
||||
|
||||
export const offersProfileRouter = createRouter()
|
||||
.query('listOne', {
|
||||
@ -127,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,
|
||||
@ -144,7 +208,7 @@ export const offersProfileRouter = createRouter()
|
||||
include: {
|
||||
replies: true,
|
||||
replyingTo: true,
|
||||
user: true
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
offers: {
|
||||
@ -172,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({
|
||||
@ -389,7 +453,8 @@ export const offersProfileRouter = createRouter()
|
||||
title: x.OffersFullTime.title,
|
||||
totalCompensation: {
|
||||
create: {
|
||||
currency: x.OffersFullTime.totalCompensation?.currency,
|
||||
currency:
|
||||
x.OffersFullTime.totalCompensation?.currency,
|
||||
value: x.OffersFullTime.totalCompensation?.value,
|
||||
},
|
||||
},
|
||||
@ -417,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', {
|
||||
@ -468,11 +501,13 @@ export const offersProfileRouter = createRouter()
|
||||
const profileEditToken = profileToDelete?.editToken;
|
||||
|
||||
if (profileEditToken === input.token) {
|
||||
return await ctx.prisma.offersProfile.delete({
|
||||
const deletedProfile = await ctx.prisma.offersProfile.delete({
|
||||
where: {
|
||||
id: input.profileId,
|
||||
},
|
||||
});
|
||||
|
||||
return deletedProfile.id;
|
||||
}
|
||||
// TODO: Throw 401
|
||||
throw new trpc.TRPCError({
|
||||
@ -493,7 +528,7 @@ export const offersProfileRouter = createRouter()
|
||||
backgroundId: z.string().optional(),
|
||||
domain: z.string(),
|
||||
id: z.string().optional(),
|
||||
yoe: z.number()
|
||||
yoe: z.number(),
|
||||
}),
|
||||
),
|
||||
totalYoe: z.number(),
|
||||
@ -505,7 +540,7 @@ export const offersProfileRouter = createRouter()
|
||||
offers: z.array(offer),
|
||||
profileName: z.string(),
|
||||
token: z.string(),
|
||||
userId: z.string().nullish()
|
||||
userId: z.string().nullish(),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const profileToUpdate = await ctx.prisma.offersProfile.findFirst({
|
||||
@ -522,17 +557,17 @@ export const offersProfileRouter = createRouter()
|
||||
},
|
||||
where: {
|
||||
id: input.id,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
await ctx.prisma.offersBackground.update({
|
||||
data: {
|
||||
totalYoe: input.background.totalYoe
|
||||
totalYoe: input.background.totalYoe,
|
||||
},
|
||||
where: {
|
||||
id: input.background.id
|
||||
}
|
||||
})
|
||||
id: input.background.id,
|
||||
},
|
||||
});
|
||||
|
||||
for (const edu of input.background.educations) {
|
||||
if (edu.id) {
|
||||
@ -545,27 +580,26 @@ export const offersProfileRouter = createRouter()
|
||||
type: edu.type,
|
||||
},
|
||||
where: {
|
||||
id: edu.id
|
||||
}
|
||||
})
|
||||
id: edu.id,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await ctx.prisma.offersBackground.update({
|
||||
data: {
|
||||
educations: {
|
||||
create:
|
||||
{
|
||||
create: {
|
||||
endDate: edu.endDate,
|
||||
field: edu.field,
|
||||
school: edu.school,
|
||||
startDate: edu.startDate,
|
||||
type: edu.type,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.background.id
|
||||
}
|
||||
})
|
||||
id: input.background.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -579,9 +613,9 @@ export const offersProfileRouter = createRouter()
|
||||
specialization: exp.specialization,
|
||||
},
|
||||
where: {
|
||||
id: exp.id
|
||||
}
|
||||
})
|
||||
id: exp.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (exp.monthlySalary) {
|
||||
await ctx.prisma.offersCurrency.update({
|
||||
@ -590,9 +624,9 @@ export const offersProfileRouter = createRouter()
|
||||
value: exp.monthlySalary.value,
|
||||
},
|
||||
where: {
|
||||
id: exp.monthlySalary.id
|
||||
}
|
||||
})
|
||||
id: exp.monthlySalary.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (exp.totalCompensation) {
|
||||
@ -602,12 +636,16 @@ export const offersProfileRouter = createRouter()
|
||||
value: exp.totalCompensation.value,
|
||||
},
|
||||
where: {
|
||||
id: exp.totalCompensation.id
|
||||
}
|
||||
})
|
||||
id: exp.totalCompensation.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if (!exp.id) {
|
||||
if (exp.jobType === 'FULLTIME' && exp.totalCompensation?.currency !== undefined && exp.totalCompensation.value !== undefined) {
|
||||
if (
|
||||
exp.jobType === 'FULLTIME' &&
|
||||
exp.totalCompensation?.currency !== undefined &&
|
||||
exp.totalCompensation.value !== undefined
|
||||
) {
|
||||
if (exp.companyId) {
|
||||
await ctx.prisma.offersBackground.update({
|
||||
data: {
|
||||
@ -630,12 +668,12 @@ export const offersProfileRouter = createRouter()
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.background.id
|
||||
}
|
||||
})
|
||||
id: input.background.id,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await ctx.prisma.offersBackground.update({
|
||||
data: {
|
||||
@ -652,16 +690,15 @@ export const offersProfileRouter = createRouter()
|
||||
value: exp.totalCompensation?.value,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.background.id
|
||||
}
|
||||
})
|
||||
id: input.background.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (
|
||||
} else if (
|
||||
exp.jobType === 'INTERN' &&
|
||||
exp.monthlySalary?.currency !== undefined &&
|
||||
exp.monthlySalary.value !== undefined
|
||||
@ -686,13 +723,13 @@ export const offersProfileRouter = createRouter()
|
||||
},
|
||||
specialization: exp.specialization,
|
||||
title: exp.title,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.background.id
|
||||
}
|
||||
})
|
||||
id: input.background.id,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await ctx.prisma.offersBackground.update({
|
||||
data: {
|
||||
@ -708,44 +745,42 @@ export const offersProfileRouter = createRouter()
|
||||
},
|
||||
specialization: exp.specialization,
|
||||
title: exp.title,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.background.id
|
||||
}
|
||||
})
|
||||
id: input.background.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (const yoe of input.background.specificYoes) {
|
||||
if (yoe.id) {
|
||||
await ctx.prisma.offersSpecificYoe.update({
|
||||
data: {
|
||||
...yoe
|
||||
...yoe,
|
||||
},
|
||||
where: {
|
||||
id: yoe.id
|
||||
}
|
||||
})
|
||||
id: yoe.id,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await ctx.prisma.offersBackground.update({
|
||||
data: {
|
||||
specificYoes: {
|
||||
create:
|
||||
{
|
||||
create: {
|
||||
domain: yoe.domain,
|
||||
yoe: yoe.yoe,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.background.id
|
||||
}
|
||||
})
|
||||
id: input.background.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -760,42 +795,46 @@ export const offersProfileRouter = createRouter()
|
||||
negotiationStrategy: offerToUpdate.negotiationStrategy,
|
||||
},
|
||||
where: {
|
||||
id: offerToUpdate.id
|
||||
}
|
||||
})
|
||||
id: offerToUpdate.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (offerToUpdate.jobType === "INTERN" || offerToUpdate.jobType === "FULLTIME") {
|
||||
if (
|
||||
offerToUpdate.jobType === 'INTERN' ||
|
||||
offerToUpdate.jobType === 'FULLTIME'
|
||||
) {
|
||||
await ctx.prisma.offersOffer.update({
|
||||
data: {
|
||||
jobType: offerToUpdate.jobType
|
||||
jobType: offerToUpdate.jobType,
|
||||
},
|
||||
where: {
|
||||
id: offerToUpdate.id
|
||||
}
|
||||
})
|
||||
id: offerToUpdate.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (offerToUpdate.OffersIntern?.monthlySalary) {
|
||||
await ctx.prisma.offersIntern.update({
|
||||
data: {
|
||||
internshipCycle: offerToUpdate.OffersIntern.internshipCycle ?? undefined,
|
||||
internshipCycle:
|
||||
offerToUpdate.OffersIntern.internshipCycle ?? undefined,
|
||||
specialization: offerToUpdate.OffersIntern.specialization,
|
||||
startYear: offerToUpdate.OffersIntern.startYear ?? undefined,
|
||||
title: offerToUpdate.OffersIntern.title,
|
||||
},
|
||||
where: {
|
||||
id: offerToUpdate.OffersIntern.id,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
await ctx.prisma.offersCurrency.update({
|
||||
data: {
|
||||
currency: offerToUpdate.OffersIntern.monthlySalary.currency,
|
||||
value: offerToUpdate.OffersIntern.monthlySalary.value
|
||||
value: offerToUpdate.OffersIntern.monthlySalary.value,
|
||||
},
|
||||
where: {
|
||||
id: offerToUpdate.OffersIntern.monthlySalary.id
|
||||
}
|
||||
})
|
||||
id: offerToUpdate.OffersIntern.monthlySalary.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (offerToUpdate.OffersFullTime?.totalCompensation) {
|
||||
@ -807,54 +846,55 @@ export const offersProfileRouter = createRouter()
|
||||
},
|
||||
where: {
|
||||
id: offerToUpdate.OffersFullTime.id,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
if (offerToUpdate.OffersFullTime.baseSalary) {
|
||||
await ctx.prisma.offersCurrency.update({
|
||||
data: {
|
||||
currency: offerToUpdate.OffersFullTime.baseSalary.currency,
|
||||
value: offerToUpdate.OffersFullTime.baseSalary.value
|
||||
value: offerToUpdate.OffersFullTime.baseSalary.value,
|
||||
},
|
||||
where: {
|
||||
id: offerToUpdate.OffersFullTime.baseSalary.id
|
||||
}
|
||||
})
|
||||
id: offerToUpdate.OffersFullTime.baseSalary.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (offerToUpdate.OffersFullTime.bonus) {
|
||||
await ctx.prisma.offersCurrency.update({
|
||||
data: {
|
||||
currency: offerToUpdate.OffersFullTime.bonus.currency,
|
||||
value: offerToUpdate.OffersFullTime.bonus.value
|
||||
value: offerToUpdate.OffersFullTime.bonus.value,
|
||||
},
|
||||
where: {
|
||||
id: offerToUpdate.OffersFullTime.bonus.id
|
||||
}
|
||||
})
|
||||
id: offerToUpdate.OffersFullTime.bonus.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (offerToUpdate.OffersFullTime.stocks) {
|
||||
await ctx.prisma.offersCurrency.update({
|
||||
data: {
|
||||
currency: offerToUpdate.OffersFullTime.stocks.currency,
|
||||
value: offerToUpdate.OffersFullTime.stocks.value
|
||||
value: offerToUpdate.OffersFullTime.stocks.value,
|
||||
},
|
||||
where: {
|
||||
id: offerToUpdate.OffersFullTime.stocks.id
|
||||
}
|
||||
})
|
||||
id: offerToUpdate.OffersFullTime.stocks.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
await ctx.prisma.offersCurrency.update({
|
||||
data: {
|
||||
currency: offerToUpdate.OffersFullTime.totalCompensation.currency,
|
||||
value: offerToUpdate.OffersFullTime.totalCompensation.value
|
||||
currency:
|
||||
offerToUpdate.OffersFullTime.totalCompensation.currency,
|
||||
value: offerToUpdate.OffersFullTime.totalCompensation.value,
|
||||
},
|
||||
where: {
|
||||
id: offerToUpdate.OffersFullTime.totalCompensation.id
|
||||
}
|
||||
})
|
||||
id: offerToUpdate.OffersFullTime.totalCompensation.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
offerToUpdate.jobType === "INTERN" &&
|
||||
offerToUpdate.jobType === 'INTERN' &&
|
||||
offerToUpdate.OffersIntern &&
|
||||
offerToUpdate.OffersIntern.internshipCycle &&
|
||||
offerToUpdate.OffersIntern.monthlySalary?.currency &&
|
||||
@ -867,14 +907,19 @@ export const offersProfileRouter = createRouter()
|
||||
create: {
|
||||
OffersIntern: {
|
||||
create: {
|
||||
internshipCycle: offerToUpdate.OffersIntern.internshipCycle,
|
||||
internshipCycle:
|
||||
offerToUpdate.OffersIntern.internshipCycle,
|
||||
monthlySalary: {
|
||||
create: {
|
||||
currency: offerToUpdate.OffersIntern.monthlySalary?.currency,
|
||||
value: offerToUpdate.OffersIntern.monthlySalary?.value,
|
||||
currency:
|
||||
offerToUpdate.OffersIntern.monthlySalary
|
||||
?.currency,
|
||||
value:
|
||||
offerToUpdate.OffersIntern.monthlySalary?.value,
|
||||
},
|
||||
},
|
||||
specialization: offerToUpdate.OffersIntern.specialization,
|
||||
specialization:
|
||||
offerToUpdate.OffersIntern.specialization,
|
||||
startYear: offerToUpdate.OffersIntern.startYear,
|
||||
title: offerToUpdate.OffersIntern.title,
|
||||
},
|
||||
@ -889,13 +934,13 @@ export const offersProfileRouter = createRouter()
|
||||
location: offerToUpdate.location,
|
||||
monthYearReceived: offerToUpdate.monthYearReceived,
|
||||
negotiationStrategy: offerToUpdate.negotiationStrategy,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.id,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
}
|
||||
if (
|
||||
offerToUpdate.jobType === 'FULLTIME' &&
|
||||
@ -918,29 +963,39 @@ export const offersProfileRouter = createRouter()
|
||||
create: {
|
||||
baseSalary: {
|
||||
create: {
|
||||
currency: offerToUpdate.OffersFullTime.baseSalary?.currency,
|
||||
value: offerToUpdate.OffersFullTime.baseSalary?.value,
|
||||
currency:
|
||||
offerToUpdate.OffersFullTime.baseSalary
|
||||
?.currency,
|
||||
value:
|
||||
offerToUpdate.OffersFullTime.baseSalary?.value,
|
||||
},
|
||||
},
|
||||
bonus: {
|
||||
create: {
|
||||
currency: offerToUpdate.OffersFullTime.bonus?.currency,
|
||||
currency:
|
||||
offerToUpdate.OffersFullTime.bonus?.currency,
|
||||
value: offerToUpdate.OffersFullTime.bonus?.value,
|
||||
},
|
||||
},
|
||||
level: offerToUpdate.OffersFullTime.level,
|
||||
specialization: offerToUpdate.OffersFullTime.specialization,
|
||||
specialization:
|
||||
offerToUpdate.OffersFullTime.specialization,
|
||||
stocks: {
|
||||
create: {
|
||||
currency: offerToUpdate.OffersFullTime.stocks?.currency,
|
||||
currency:
|
||||
offerToUpdate.OffersFullTime.stocks?.currency,
|
||||
value: offerToUpdate.OffersFullTime.stocks?.value,
|
||||
},
|
||||
},
|
||||
title: offerToUpdate.OffersFullTime.title,
|
||||
totalCompensation: {
|
||||
create: {
|
||||
currency: offerToUpdate.OffersFullTime.totalCompensation?.currency,
|
||||
value: offerToUpdate.OffersFullTime.totalCompensation?.value,
|
||||
currency:
|
||||
offerToUpdate.OffersFullTime.totalCompensation
|
||||
?.currency,
|
||||
value:
|
||||
offerToUpdate.OffersFullTime.totalCompensation
|
||||
?.value,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -955,17 +1010,17 @@ export const offersProfileRouter = createRouter()
|
||||
location: offerToUpdate.location,
|
||||
monthYearReceived: offerToUpdate.monthYearReceived,
|
||||
negotiationStrategy: offerToUpdate.negotiationStrategy,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.id,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: add analysis to profile object then return
|
||||
|
||||
const result = await ctx.prisma.offersProfile.findFirst({
|
||||
include: {
|
||||
background: {
|
||||
@ -985,7 +1040,7 @@ export const offersProfileRouter = createRouter()
|
||||
include: {
|
||||
replies: true,
|
||||
replyingTo: true,
|
||||
user: true
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
offers: {
|
||||
@ -1013,7 +1068,7 @@ export const offersProfileRouter = createRouter()
|
||||
});
|
||||
|
||||
if (result) {
|
||||
return exclude(computeIsEditable(result, input.token), 'editToken')
|
||||
return createOfferProfileResponseMapper(result, input.token);
|
||||
}
|
||||
|
||||
throw new trpc.TRPCError({
|
||||
@ -1036,9 +1091,9 @@ export const offersProfileRouter = createRouter()
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const profile = await ctx.prisma.offersProfile.findFirst({
|
||||
where: {
|
||||
id: input.profileId,
|
||||
},
|
||||
where: {
|
||||
id: input.profileId,
|
||||
},
|
||||
});
|
||||
|
||||
const profileEditToken = profile?.editToken;
|
||||
@ -1048,25 +1103,21 @@ export const offersProfileRouter = createRouter()
|
||||
data: {
|
||||
user: {
|
||||
connect: {
|
||||
id: input.userId
|
||||
}
|
||||
}
|
||||
id: input.userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: input.profileId
|
||||
}
|
||||
})
|
||||
id: input.profileId,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
id: updated.id,
|
||||
profileName: updated.profileName,
|
||||
userId: updated.userId
|
||||
}
|
||||
return addToProfileResponseMapper(updated);
|
||||
}
|
||||
|
||||
throw new trpc.TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'Invalid token.',
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { z } from 'zod';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import {
|
||||
dashboardOfferDtoMapper,
|
||||
getOffersResponseMapper,
|
||||
} from '~/mappers/offers-mappers';
|
||||
|
||||
import { createRouter } from '../context';
|
||||
|
||||
const yoeCategoryMap: Record<number, string> = {
|
||||
@ -299,14 +304,14 @@ export const offersRouter = createRouter().query('list', {
|
||||
: data.length;
|
||||
const paginatedData = data.slice(startRecordIndex, endRecordIndex);
|
||||
|
||||
return {
|
||||
data: paginatedData,
|
||||
paging: {
|
||||
currPage: input.offset,
|
||||
numOfItemsInPage: paginatedData.length,
|
||||
return getOffersResponseMapper(
|
||||
paginatedData.map((offer) => dashboardOfferDtoMapper(offer)),
|
||||
{
|
||||
currentPage: input.offset,
|
||||
numOfItems: paginatedData.length,
|
||||
numOfPages: Math.ceil(data.length / input.limit),
|
||||
totalNumberOfOffers: data.length,
|
||||
totalItems: data.length,
|
||||
},
|
||||
};
|
||||
);
|
||||
},
|
||||
});
|
||||
|
130
apps/portal/src/types/offers-profile.d.ts
vendored
130
apps/portal/src/types/offers-profile.d.ts
vendored
@ -1,130 +0,0 @@
|
||||
export type OffersProfile = {
|
||||
background?: Background | null;
|
||||
createdAt: Date;
|
||||
// Discussions: Array<discussion>;
|
||||
editToken: string;
|
||||
id: string;
|
||||
offers: Array<Offer>;
|
||||
profileName: string;
|
||||
userId?: string | null;
|
||||
};
|
||||
|
||||
export type Background = {
|
||||
educations: Array<Education>;
|
||||
experiences: Array<Experience>;
|
||||
id: string;
|
||||
offersProfileId: string;
|
||||
specificYoes: Array<SpecificYoe>;
|
||||
totalYoe?: number | null;
|
||||
}
|
||||
|
||||
export type Experience = {
|
||||
backgroundId: string;
|
||||
company?: Company | null;
|
||||
companyId?: string | null;
|
||||
durationInMonths?: number | null;
|
||||
id: string;
|
||||
jobType?: string | null;
|
||||
level?: string | null;
|
||||
monthlySalary?: Valuation | null;
|
||||
monthlySalaryId?: string | null;
|
||||
specialization?: string | null;
|
||||
title?: string | null;
|
||||
totalCompensation?: Valuation | null;
|
||||
totalCompensationId?: string | null;
|
||||
}
|
||||
|
||||
export type Company = {
|
||||
createdAt: Date;
|
||||
description: string | null;
|
||||
id: string;
|
||||
logoUrl: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
export type Valuation = {
|
||||
currency: string;
|
||||
id: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export type Education = {
|
||||
backgroundId: string;
|
||||
endDate?: Date | null;
|
||||
field?: string | null;
|
||||
id: string;
|
||||
school?: string | null;
|
||||
startDate?: Date | null;
|
||||
type?: string | null;
|
||||
}
|
||||
|
||||
export type SpecificYoe = {
|
||||
backgroundId: string;
|
||||
domain: string;
|
||||
id: string;
|
||||
yoe: number;
|
||||
}
|
||||
|
||||
export type Offer = {
|
||||
OfferFullTime?: OfferFullTime | null;
|
||||
OfferIntern?: OfferIntern | null;
|
||||
comments?: string | null;
|
||||
company: Company;
|
||||
companyId: string;
|
||||
id: string;
|
||||
jobType: string;
|
||||
location: string;
|
||||
monthYearReceived: Date;
|
||||
negotiationStrategy?: string | null;
|
||||
offersFullTimeId?: string | null;
|
||||
offersInternId?: string | null;
|
||||
profileId: string;
|
||||
}
|
||||
|
||||
export type OfferFullTime = {
|
||||
baseSalary: Valuation;
|
||||
baseSalaryId: string;
|
||||
bonus: Valuation;
|
||||
bonusId: string;
|
||||
id: string;
|
||||
level: string;
|
||||
specialization: string;
|
||||
stocks: Valuation;
|
||||
stocksId: string;
|
||||
title?: string;
|
||||
totalCompensation: Valuation;
|
||||
totalCompensationId: string;
|
||||
}
|
||||
|
||||
export type OfferIntern = {
|
||||
id: string;
|
||||
internshipCycle: string;
|
||||
monthlySalary: Valuation;
|
||||
monthlySalaryId: string;
|
||||
specialization: string;
|
||||
startYear: number;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export type Reply = {
|
||||
createdAt: Date;
|
||||
id: string;
|
||||
message: string;
|
||||
// Profile: OffersProfile | null;
|
||||
profileId: string;
|
||||
replies: Array<Discussion>?;
|
||||
replyingTo: Discussion?;
|
||||
replyingToId: string | null;
|
||||
user: User?;
|
||||
userId: string | null;
|
||||
}
|
||||
|
||||
export type User = {
|
||||
email: string?;
|
||||
emailVerified: Date?;
|
||||
id: string;
|
||||
image: string?;
|
||||
name: string?;
|
||||
}
|
186
apps/portal/src/types/offers.d.ts
vendored
Normal file
186
apps/portal/src/types/offers.d.ts
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
import type { JobType } from '@prisma/client';
|
||||
|
||||
export type Profile = {
|
||||
analysis: ProfileAnalysis?;
|
||||
background: Background?;
|
||||
editToken: string?;
|
||||
id: string;
|
||||
isEditable: boolean;
|
||||
offers: Array<ProfileOffer>;
|
||||
profileName: string;
|
||||
};
|
||||
|
||||
export type Background = {
|
||||
educations: Array<Education>;
|
||||
experiences: Array<Experience>;
|
||||
id: string;
|
||||
specificYoes: Array<SpecificYoe>;
|
||||
totalYoe: number;
|
||||
};
|
||||
|
||||
export type Experience = {
|
||||
company: OffersCompany?;
|
||||
durationInMonths: number?;
|
||||
id: string;
|
||||
jobType: JobType?;
|
||||
level: string?;
|
||||
monthlySalary: Valuation?;
|
||||
specialization: string?;
|
||||
title: string?;
|
||||
totalCompensation: Valuation?;
|
||||
};
|
||||
|
||||
export type OffersCompany = {
|
||||
createdAt: Date;
|
||||
description: string;
|
||||
id: string;
|
||||
logoUrl: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export type Valuation = {
|
||||
currency: string;
|
||||
value: number;
|
||||
};
|
||||
|
||||
export type Education = {
|
||||
endDate: Date?;
|
||||
field: string?;
|
||||
id: string;
|
||||
school: string?;
|
||||
startDate: Date?;
|
||||
type: string?;
|
||||
};
|
||||
|
||||
export type SpecificYoe = {
|
||||
domain: string;
|
||||
id: string;
|
||||
yoe: number;
|
||||
};
|
||||
|
||||
export type DashboardOffer = {
|
||||
company: OffersCompany;
|
||||
id: string;
|
||||
income: Valuation;
|
||||
monthYearReceived: Date;
|
||||
profileId: string;
|
||||
title: string;
|
||||
totalYoe: number;
|
||||
};
|
||||
|
||||
export type ProfileOffer = {
|
||||
comments: string;
|
||||
company: OffersCompany;
|
||||
id: string;
|
||||
jobType: JobType;
|
||||
location: string;
|
||||
monthYearReceived: Date;
|
||||
negotiationStrategy: string;
|
||||
offersFullTime: FullTime?;
|
||||
offersIntern: Intern?;
|
||||
};
|
||||
|
||||
export type FullTime = {
|
||||
baseSalary: Valuation;
|
||||
bonus: Valuation;
|
||||
id: string;
|
||||
level: string;
|
||||
specialization: string;
|
||||
stocks: Valuation;
|
||||
title: string;
|
||||
totalCompensation: Valuation;
|
||||
};
|
||||
|
||||
export type Intern = {
|
||||
id: string;
|
||||
internshipCycle: string;
|
||||
monthlySalary: Valuation;
|
||||
specialization: string;
|
||||
startYear: number;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type Reply = {
|
||||
createdAt: Date;
|
||||
id: string;
|
||||
message: string;
|
||||
replies: Array<Reply>?;
|
||||
replyingToId: string?;
|
||||
user: User?;
|
||||
};
|
||||
|
||||
export type User = {
|
||||
email: string?;
|
||||
emailVerified: Date?;
|
||||
id: string;
|
||||
image: string?;
|
||||
name: string?;
|
||||
};
|
||||
|
||||
export type GetOffersResponse = {
|
||||
data: Array<DashboardOffer>;
|
||||
paging: Paging;
|
||||
};
|
||||
|
||||
export type Paging = {
|
||||
currentPage: number;
|
||||
numOfItems: number;
|
||||
numOfPages: number;
|
||||
totalItems: number;
|
||||
};
|
||||
|
||||
export type CreateOfferProfileResponse = {
|
||||
id: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
export type OffersDiscussion = {
|
||||
data: Array<Reply>;
|
||||
};
|
||||
|
||||
export type ProfileAnalysis = {
|
||||
companyAnalysis: Array<Analysis>;
|
||||
id: string;
|
||||
overallAnalysis: Analysis;
|
||||
overallHighestOffer: AnalysisHighestOffer;
|
||||
profileId: string;
|
||||
};
|
||||
|
||||
export type Analysis = {
|
||||
noOfOffers: number;
|
||||
percentile: number;
|
||||
topPercentileOffers: Array<AnalysisOffer>;
|
||||
};
|
||||
|
||||
export type AnalysisHighestOffer = {
|
||||
company: OffersCompany;
|
||||
id: string;
|
||||
level: string;
|
||||
location: string;
|
||||
specialization: string;
|
||||
totalYoe: number;
|
||||
};
|
||||
|
||||
export type AnalysisOffer = {
|
||||
company: OffersCompany;
|
||||
id: string;
|
||||
income: number;
|
||||
jobType: JobType;
|
||||
level: string;
|
||||
location: string;
|
||||
monthYearReceived: Date;
|
||||
negotiationStrategy: string;
|
||||
previousCompanies: Array<string>;
|
||||
profileName: string;
|
||||
specialization: string;
|
||||
title: string;
|
||||
totalYoe: number;
|
||||
};
|
||||
|
||||
export type AddToProfileResponse = {
|
||||
id: string;
|
||||
profileName: string;
|
||||
userId: string;
|
||||
};
|
Reference in New Issue
Block a user