mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-29 05:02:52 +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])
|
user User? @relation(fields: [userId], references: [id])
|
||||||
userId String?
|
userId String?
|
||||||
|
|
||||||
OffersAnalysis OffersAnalysis?
|
analysis OffersAnalysis?
|
||||||
}
|
}
|
||||||
|
|
||||||
model OffersBackground {
|
model OffersBackground {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
|
|
||||||
totalYoe Int?
|
totalYoe Int
|
||||||
specificYoes OffersSpecificYoe[]
|
specificYoes OffersSpecificYoe[]
|
||||||
|
|
||||||
experiences OffersExperience[] // For extensibility in the future
|
experiences OffersExperience[] // For extensibility in the future
|
||||||
@ -308,8 +308,8 @@ model OffersOffer {
|
|||||||
|
|
||||||
monthYearReceived DateTime
|
monthYearReceived DateTime
|
||||||
location String
|
location String
|
||||||
negotiationStrategy String?
|
negotiationStrategy String
|
||||||
comments String?
|
comments String
|
||||||
|
|
||||||
jobType JobType
|
jobType JobType
|
||||||
|
|
||||||
@ -320,7 +320,6 @@ model OffersOffer {
|
|||||||
offersFullTimeId String? @unique
|
offersFullTimeId String? @unique
|
||||||
|
|
||||||
OffersAnalysis OffersAnalysis? @relation("HighestOverallOffer")
|
OffersAnalysis OffersAnalysis? @relation("HighestOverallOffer")
|
||||||
|
|
||||||
OffersAnalysisTopOverallOffers OffersAnalysis[] @relation("TopOverallOffers")
|
OffersAnalysisTopOverallOffers OffersAnalysis[] @relation("TopOverallOffers")
|
||||||
OffersAnalysisTopCompanyOffers OffersAnalysis[] @relation("TopCompanyOffers")
|
OffersAnalysisTopCompanyOffers OffersAnalysis[] @relation("TopCompanyOffers")
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import Link from 'next/link';
|
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({
|
export default function OfferTableRow({
|
||||||
row: { company, date, id, profileId, salary, title, yoe },
|
row: { company, id, income, monthYearReceived, profileId, title, totalYoe },
|
||||||
}: OfferTableRowProps) {
|
}: OfferTableRowProps) {
|
||||||
return (
|
return (
|
||||||
<tr
|
<tr
|
||||||
@ -14,12 +16,12 @@ export default function OfferTableRow({
|
|||||||
<th
|
<th
|
||||||
className="whitespace-nowrap py-4 px-6 font-medium text-gray-900 dark:text-white"
|
className="whitespace-nowrap py-4 px-6 font-medium text-gray-900 dark:text-white"
|
||||||
scope="row">
|
scope="row">
|
||||||
{company}
|
{company.name}
|
||||||
</th>
|
</th>
|
||||||
<td className="py-4 px-6">{title}</td>
|
<td className="py-4 px-6">{title}</td>
|
||||||
<td className="py-4 px-6">{yoe}</td>
|
<td className="py-4 px-6">{totalYoe}</td>
|
||||||
<td className="py-4 px-6">{salary}</td>
|
<td className="py-4 px-6">{convertCurrencyToString(income)}</td>
|
||||||
<td className="py-4 px-6">{date}</td>
|
<td className="py-4 px-6">{formatDate(monthYearReceived)}</td>
|
||||||
<td className="space-x-4 py-4 px-6">
|
<td className="space-x-4 py-4 px-6">
|
||||||
<Link
|
<Link
|
||||||
className="font-medium text-indigo-600 hover:underline dark:text-indigo-500"
|
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 { HorizontalDivider, Select, Spinner, Tabs } from '@tih/ui';
|
||||||
|
|
||||||
import OffersTablePagination from '~/components/offers/table/OffersTablePagination';
|
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 { YOE_CATEGORY } from '~/components/offers/table/types';
|
||||||
|
|
||||||
import CurrencySelector from '~/utils/offers/currency/CurrencySelector';
|
import CurrencySelector from '~/utils/offers/currency/CurrencySelector';
|
||||||
import { formatDate } from '~/utils/offers/time';
|
|
||||||
import { trpc } from '~/utils/trpc';
|
import { trpc } from '~/utils/trpc';
|
||||||
|
|
||||||
import OffersRow from './OffersRow';
|
import OffersRow from './OffersRow';
|
||||||
|
|
||||||
|
import type { DashboardOffer, Paging } from '~/types/offers';
|
||||||
|
|
||||||
const NUMBER_OF_OFFERS_IN_PAGE = 10;
|
const NUMBER_OF_OFFERS_IN_PAGE = 10;
|
||||||
export type OffersTableProps = Readonly<{
|
export type OffersTableProps = Readonly<{
|
||||||
companyFilter: string;
|
companyFilter: string;
|
||||||
@ -25,18 +22,18 @@ export default function OffersTable({
|
|||||||
}: OffersTableProps) {
|
}: OffersTableProps) {
|
||||||
const [currency, setCurrency] = useState('SGD'); // TODO: Detect location
|
const [currency, setCurrency] = useState('SGD'); // TODO: Detect location
|
||||||
const [selectedTab, setSelectedTab] = useState(YOE_CATEGORY.ENTRY);
|
const [selectedTab, setSelectedTab] = useState(YOE_CATEGORY.ENTRY);
|
||||||
const [pagination, setPagination] = useState<PaginationType>({
|
const [pagination, setPagination] = useState<Paging>({
|
||||||
currentPage: 1,
|
currentPage: 0,
|
||||||
numOfItems: 1,
|
numOfItems: 0,
|
||||||
numOfPages: 0,
|
numOfPages: 0,
|
||||||
totalItems: 0,
|
totalItems: 0,
|
||||||
});
|
});
|
||||||
const [offers, setOffers] = useState<Array<OfferTableRowData>>([]);
|
const [offers, setOffers] = useState<Array<DashboardOffer>>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPagination({
|
setPagination({
|
||||||
currentPage: 1,
|
currentPage: 0,
|
||||||
numOfItems: 1,
|
numOfItems: 0,
|
||||||
numOfPages: 0,
|
numOfPages: 0,
|
||||||
totalItems: 0,
|
totalItems: 0,
|
||||||
});
|
});
|
||||||
@ -48,7 +45,7 @@ export default function OffersTable({
|
|||||||
companyId: companyFilter,
|
companyId: companyFilter,
|
||||||
limit: NUMBER_OF_OFFERS_IN_PAGE,
|
limit: NUMBER_OF_OFFERS_IN_PAGE,
|
||||||
location: 'Singapore, Singapore', // TODO: Geolocation
|
location: 'Singapore, Singapore', // TODO: Geolocation
|
||||||
offset: pagination.currentPage - 1,
|
offset: 0,
|
||||||
sortBy: '-monthYearReceived',
|
sortBy: '-monthYearReceived',
|
||||||
title: jobTitleFilter,
|
title: jobTitleFilter,
|
||||||
yoeCategory: selectedTab,
|
yoeCategory: selectedTab,
|
||||||
@ -56,28 +53,19 @@ export default function OffersTable({
|
|||||||
],
|
],
|
||||||
{
|
{
|
||||||
onSuccess: (response) => {
|
onSuccess: (response) => {
|
||||||
const filteredData = response.data.map((res) => {
|
// Const filteredData = response.data.map((res) => {
|
||||||
return {
|
// return {
|
||||||
company: res.company.name,
|
// company: res.company.name,
|
||||||
date: formatDate(res.monthYearReceived),
|
// date: res.monthYearReceived,
|
||||||
id: res.OffersFullTime
|
// id: res.id,
|
||||||
? res.OffersFullTime!.id
|
// profileId: res.profileId,
|
||||||
: res.OffersIntern!.id,
|
// income: res.income,
|
||||||
profileId: res.profileId,
|
// title: res.title,
|
||||||
salary: res.OffersFullTime
|
// yoe: res.totalYoe,
|
||||||
? res.OffersFullTime?.totalCompensation.value
|
// };
|
||||||
: res.OffersIntern?.monthlySalary.value,
|
// });
|
||||||
title: res.OffersFullTime ? res.OffersFullTime?.level : '',
|
setOffers(response.data);
|
||||||
yoe: 100,
|
setPagination(response.paging);
|
||||||
};
|
|
||||||
});
|
|
||||||
setOffers(filteredData);
|
|
||||||
setPagination({
|
|
||||||
currentPage: (response.paging.currPage as number) + 1,
|
|
||||||
numOfItems: response.paging.numOfItemsInPage,
|
|
||||||
numOfPages: response.paging.numOfPages,
|
|
||||||
totalItems: response.paging.totalNumberOfOffers,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -90,15 +78,15 @@ export default function OffersTable({
|
|||||||
label="Table Navigation"
|
label="Table Navigation"
|
||||||
tabs={[
|
tabs={[
|
||||||
{
|
{
|
||||||
label: 'Fresh Grad (0-3 YOE)',
|
label: 'Fresh Grad (0-2 YOE)',
|
||||||
value: YOE_CATEGORY.ENTRY,
|
value: YOE_CATEGORY.ENTRY,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Mid (4-7 YOE)',
|
label: 'Mid (3-5 YOE)',
|
||||||
value: YOE_CATEGORY.MID,
|
value: YOE_CATEGORY.MID,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Senior (8+ YOE)',
|
label: 'Senior (6+ YOE)',
|
||||||
value: YOE_CATEGORY.SENIOR,
|
value: YOE_CATEGORY.SENIOR,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -187,14 +175,11 @@ export default function OffersTable({
|
|||||||
)}
|
)}
|
||||||
<OffersTablePagination
|
<OffersTablePagination
|
||||||
endNumber={
|
endNumber={
|
||||||
(pagination.currentPage - 1) * NUMBER_OF_OFFERS_IN_PAGE +
|
pagination.currentPage * NUMBER_OF_OFFERS_IN_PAGE + offers.length
|
||||||
offers.length
|
|
||||||
}
|
}
|
||||||
handlePageChange={handlePageChange}
|
handlePageChange={handlePageChange}
|
||||||
pagination={pagination}
|
pagination={pagination}
|
||||||
startNumber={
|
startNumber={pagination.currentPage * NUMBER_OF_OFFERS_IN_PAGE + 1}
|
||||||
(pagination.currentPage - 1) * NUMBER_OF_OFFERS_IN_PAGE + 1
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { Pagination } from '@tih/ui';
|
import { Pagination } from '@tih/ui';
|
||||||
|
|
||||||
import type { PaginationType } from '~/components/offers/table/types';
|
import type { Paging } from '~/types/offers';
|
||||||
|
|
||||||
type OffersTablePaginationProps = Readonly<{
|
type OffersTablePaginationProps = Readonly<{
|
||||||
endNumber: number;
|
endNumber: number;
|
||||||
handlePageChange: (page: number) => void;
|
handlePageChange: (page: number) => void;
|
||||||
pagination: PaginationType;
|
pagination: Paging;
|
||||||
startNumber: number;
|
startNumber: number;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
@ -30,13 +30,13 @@ export default function OffersTablePagination({
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<Pagination
|
<Pagination
|
||||||
current={pagination.currentPage}
|
current={pagination.currentPage + 1}
|
||||||
end={pagination.numOfPages}
|
end={pagination.numOfPages}
|
||||||
label="Pagination"
|
label="Pagination"
|
||||||
pagePadding={1}
|
pagePadding={1}
|
||||||
start={1}
|
start={1}
|
||||||
onSelect={(currPage) => {
|
onSelect={(currPage) => {
|
||||||
handlePageChange(currPage);
|
handlePageChange(currPage - 1);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</nav>
|
</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
|
// eslint-disable-next-line no-shadow
|
||||||
export enum YOE_CATEGORY {
|
export enum YOE_CATEGORY {
|
||||||
INTERN = 0,
|
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) {
|
if (data?.offers) {
|
||||||
const filteredOffers: Array<OfferEntity> = data
|
const filteredOffers: Array<OfferEntity> = data
|
||||||
? data?.offers.map((res) => {
|
? data?.offers.map((res) => {
|
||||||
if (res.OfferFullTime) {
|
if (res.offersFullTime) {
|
||||||
const filteredOffer: OfferEntity = {
|
const filteredOffer: OfferEntity = {
|
||||||
base: convertCurrencyToString(
|
base: convertCurrencyToString(
|
||||||
res.OfferFullTime.baseSalary,
|
res.offersFullTime.baseSalary,
|
||||||
),
|
|
||||||
bonus: convertCurrencyToString(
|
|
||||||
res.OfferFullTime.bonus,
|
|
||||||
),
|
),
|
||||||
|
bonus: convertCurrencyToString(res.offersFullTime.bonus),
|
||||||
companyName: res.company.name,
|
companyName: res.company.name,
|
||||||
id: res.OfferFullTime.id,
|
id: res.offersFullTime.id,
|
||||||
jobLevel: res.OfferFullTime.level,
|
jobLevel: res.offersFullTime.level,
|
||||||
jobTitle: res.OfferFullTime.title,
|
jobTitle: res.offersFullTime.title,
|
||||||
location: res.location,
|
location: res.location,
|
||||||
negotiationStrategy: res.negotiationStrategy || '',
|
negotiationStrategy: res.negotiationStrategy || '',
|
||||||
otherComment: res.comments || '',
|
otherComment: res.comments || '',
|
||||||
receivedMonth: formatDate(res.monthYearReceived),
|
receivedMonth: formatDate(res.monthYearReceived),
|
||||||
stocks: convertCurrencyToString(res.OfferFullTime.stocks),
|
stocks: convertCurrencyToString(res.offersFullTime.stocks),
|
||||||
totalCompensation: convertCurrencyToString(
|
totalCompensation: convertCurrencyToString(
|
||||||
res.OfferFullTime.totalCompensation,
|
res.offersFullTime.totalCompensation,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,11 +67,11 @@ export default function OfferProfile() {
|
|||||||
}
|
}
|
||||||
const filteredOffer: OfferEntity = {
|
const filteredOffer: OfferEntity = {
|
||||||
companyName: res.company.name,
|
companyName: res.company.name,
|
||||||
id: res.OfferIntern!.id,
|
id: res.offersIntern!.id,
|
||||||
jobTitle: res.OfferIntern!.title,
|
jobTitle: res.offersIntern!.title,
|
||||||
location: res.location,
|
location: res.location,
|
||||||
monthlySalary: convertCurrencyToString(
|
monthlySalary: convertCurrencyToString(
|
||||||
res.OfferIntern!.monthlySalary,
|
res.offersIntern!.monthlySalary,
|
||||||
),
|
),
|
||||||
negotiationStrategy: res.negotiationStrategy || '',
|
negotiationStrategy: res.negotiationStrategy || '',
|
||||||
otherComment: res.comments || '',
|
otherComment: res.comments || '',
|
||||||
|
@ -7,7 +7,7 @@ function Test() {
|
|||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
const createMutation = trpc.useMutation(['offers.profile.create'], {
|
const createMutation = trpc.useMutation(['offers.profile.create'], {
|
||||||
onError(err: any) {
|
onError(err) {
|
||||||
alert(err);
|
alert(err);
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
@ -18,7 +18,7 @@ function Test() {
|
|||||||
const addToUserProfileMutation = trpc.useMutation(
|
const addToUserProfileMutation = trpc.useMutation(
|
||||||
['offers.profile.addToUserProfile'],
|
['offers.profile.addToUserProfile'],
|
||||||
{
|
{
|
||||||
onError(err: any) {
|
onError(err) {
|
||||||
alert(err);
|
alert(err);
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
@ -28,7 +28,7 @@ function Test() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const deleteCommentMutation = trpc.useMutation(['offers.comments.delete'], {
|
const deleteCommentMutation = trpc.useMutation(['offers.comments.delete'], {
|
||||||
onError(err: any) {
|
onError(err) {
|
||||||
alert(err);
|
alert(err);
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
@ -46,7 +46,7 @@ function Test() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateCommentMutation = trpc.useMutation(['offers.comments.update'], {
|
const updateCommentMutation = trpc.useMutation(['offers.comments.update'], {
|
||||||
onError(err: any) {
|
onError(err) {
|
||||||
alert(err);
|
alert(err);
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
@ -64,7 +64,7 @@ function Test() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createCommentMutation = trpc.useMutation(['offers.comments.create'], {
|
const createCommentMutation = trpc.useMutation(['offers.comments.create'], {
|
||||||
onError(err: any) {
|
onError(err) {
|
||||||
alert(err);
|
alert(err);
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
@ -74,17 +74,18 @@ function Test() {
|
|||||||
|
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
createCommentMutation.mutate({
|
createCommentMutation.mutate({
|
||||||
message: 'hello',
|
message: 'wassup bro',
|
||||||
profileId: 'cl96stky5002ew32gx2kale2x',
|
profileId: 'cl9efyn9p004ww3u42mjgl1vn',
|
||||||
// UserId: 'cl97dl51k001e7iygd5v5gt58'
|
replyingToId: 'cl9el4xj10001w3w21o3p2iny',
|
||||||
|
userId: 'cl9ehvpng0000w3ec2mpx0bdd'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLink = () => {
|
const handleLink = () => {
|
||||||
addToUserProfileMutation.mutate({
|
addToUserProfileMutation.mutate({
|
||||||
profileId: 'cl96stky5002ew32gx2kale2x',
|
profileId: 'cl9efyn9p004ww3u42mjgl1vn',
|
||||||
token: 'afca11e436d21bde24543718fa957c6c625335439dc504f24ee35eae7b5ef1ba',
|
token: 'afca11e436d21bde24543718fa957c6c625335439dc504f24ee35eae7b5ef1ba',
|
||||||
userId: 'cl97dl51k001e7iygd5v5gt58',
|
userId: 'cl9ehvpng0000w3ec2mpx0bdd',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ function Test() {
|
|||||||
],
|
],
|
||||||
experiences: [
|
experiences: [
|
||||||
{
|
{
|
||||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||||
durationInMonths: 24,
|
durationInMonths: 24,
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
level: 'Junior',
|
level: 'Junior',
|
||||||
@ -150,6 +151,8 @@ function Test() {
|
|||||||
value: 104100,
|
value: 104100,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
comments: 'I am a Raffles Institution almumni',
|
||||||
// Comments: '',
|
// Comments: '',
|
||||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
@ -179,25 +182,25 @@ function Test() {
|
|||||||
value: 104100,
|
value: 104100,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
comments: undefined,
|
comments: '',
|
||||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
|
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
|
||||||
// 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(
|
const data = trpc.useQuery(
|
||||||
[
|
[
|
||||||
`offers.profile.listOne`,
|
`offers.profile.listOne`,
|
||||||
{
|
{
|
||||||
profileId,
|
profileId,
|
||||||
token:
|
token:
|
||||||
'e7effd2a40adba2deb1ddea4fb9f1e6c3c98ab0a85a88ed1567fc2a107fdb445',
|
'd14666ff76e267c9e99445844b41410e83874936d0c07e664db73ff0ea76919e',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
@ -216,6 +219,7 @@ function Test() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Console.log(replies.data?.data)
|
||||||
const deleteMutation = trpc.useMutation(['offers.profile.delete']);
|
const deleteMutation = trpc.useMutation(['offers.profile.delete']);
|
||||||
|
|
||||||
const handleDelete = (id: string) => {
|
const handleDelete = (id: string) => {
|
||||||
@ -226,7 +230,7 @@ function Test() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateMutation = trpc.useMutation(['offers.profile.update'], {
|
const updateMutation = trpc.useMutation(['offers.profile.update'], {
|
||||||
onError(err: any) {
|
onError(err) {
|
||||||
alert(err);
|
alert(err);
|
||||||
},
|
},
|
||||||
onSuccess(response) {
|
onSuccess(response) {
|
||||||
@ -261,7 +265,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||||
durationInMonths: 24,
|
durationInMonths: 24,
|
||||||
id: 'cl96stky6002iw32gpt6t87s2',
|
id: 'cl96stky6002iw32gpt6t87s2',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
@ -368,7 +372,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||||
id: 'cl976t4de00047iygl0zbce11',
|
id: 'cl976t4de00047iygl0zbce11',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
@ -410,7 +414,7 @@ function Test() {
|
|||||||
totalCompensationId: 'cl96stky90039w32glbpktd0o',
|
totalCompensationId: 'cl96stky90039w32glbpktd0o',
|
||||||
},
|
},
|
||||||
OffersIntern: null,
|
OffersIntern: null,
|
||||||
comments: null,
|
comments: '',
|
||||||
company: {
|
company: {
|
||||||
createdAt: new Date('2022-10-12T16:19:05.196Z'),
|
createdAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
description:
|
description:
|
||||||
@ -421,7 +425,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||||
id: 'cl96stky80031w32gau9mu1gs',
|
id: 'cl96stky80031w32gau9mu1gs',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
@ -463,7 +467,7 @@ function Test() {
|
|||||||
totalCompensationId: 'cl96stky9003jw32gzumcoi7v',
|
totalCompensationId: 'cl96stky9003jw32gzumcoi7v',
|
||||||
},
|
},
|
||||||
OffersIntern: null,
|
OffersIntern: null,
|
||||||
comments: null,
|
comments: '',
|
||||||
company: {
|
company: {
|
||||||
createdAt: new Date('2022-10-12T16:19:05.196Z'),
|
createdAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
description:
|
description:
|
||||||
@ -474,7 +478,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||||
id: 'cl96stky9003bw32gc3l955vr',
|
id: 'cl96stky9003bw32gc3l955vr',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
@ -527,7 +531,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||||
id: 'cl976wf28000t7iyga4noyz7s',
|
id: 'cl976wf28000t7iyga4noyz7s',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
@ -580,7 +584,7 @@ function Test() {
|
|||||||
slug: 'meta',
|
slug: 'meta',
|
||||||
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
updatedAt: new Date('2022-10-12T16:19:05.196Z'),
|
||||||
},
|
},
|
||||||
companyId: 'cl98yuqk80007txhgjtjp8fk4',
|
companyId: 'cl9ec1mgg0000w33hg1a3612r',
|
||||||
id: 'cl96tbb3o0051w32gjrpaiiit',
|
id: 'cl96tbb3o0051w32gjrpaiiit',
|
||||||
jobType: 'FULLTIME',
|
jobType: 'FULLTIME',
|
||||||
location: 'Singapore, Singapore',
|
location: 'Singapore, Singapore',
|
||||||
@ -600,7 +604,7 @@ function Test() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>{createdData}</div>
|
<div>{createdData}</div>
|
||||||
<div>{JSON.stringify(replies.data)}</div>
|
<div>{JSON.stringify(replies.data?.data)}</div>
|
||||||
<button type="button" onClick={handleClick}>
|
<button type="button" onClick={handleClick}>
|
||||||
Click Me!
|
Click Me!
|
||||||
</button>
|
</button>
|
||||||
|
@ -8,9 +8,10 @@ import type {
|
|||||||
OffersOffer,
|
OffersOffer,
|
||||||
OffersProfile,
|
OffersProfile,
|
||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
import { JobType } from '@prisma/client';
|
|
||||||
import { TRPCError } from '@trpc/server';
|
import { TRPCError } from '@trpc/server';
|
||||||
|
|
||||||
|
import { profileAnalysisDtoMapper } from '~/mappers/offers-mappers';
|
||||||
|
|
||||||
import { createRouter } from '../context';
|
import { createRouter } from '../context';
|
||||||
|
|
||||||
const searchOfferPercentile = (
|
const searchOfferPercentile = (
|
||||||
@ -27,9 +28,19 @@ const searchOfferPercentile = (
|
|||||||
company: Company;
|
company: Company;
|
||||||
profile: OffersProfile & { background: OffersBackground | null };
|
profile: OffersProfile & { background: OffersBackground | null };
|
||||||
},
|
},
|
||||||
similarOffers: Array<any> | string,
|
similarOffers: Array<
|
||||||
|
OffersOffer & {
|
||||||
|
OffersFullTime:
|
||||||
|
| (OffersFullTime & {
|
||||||
|
totalCompensation: OffersCurrency;
|
||||||
|
})
|
||||||
|
| null;
|
||||||
|
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
|
||||||
|
company: Company;
|
||||||
|
profile: OffersProfile & { background: OffersBackground | null };
|
||||||
|
}
|
||||||
|
>,
|
||||||
) => {
|
) => {
|
||||||
|
|
||||||
for (let i = 0; i < similarOffers.length; i++) {
|
for (let i = 0; i < similarOffers.length; i++) {
|
||||||
if (similarOffers[i].id === offer.id) {
|
if (similarOffers[i].id === offer.id) {
|
||||||
return i;
|
return i;
|
||||||
@ -39,116 +50,6 @@ const searchOfferPercentile = (
|
|||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
const topPercentileDtoMapper = (topPercentileOffers: Array<any>) => {
|
|
||||||
return topPercentileOffers.map((offer) => {
|
|
||||||
const { background } = offer.profile;
|
|
||||||
return {
|
|
||||||
company: { id: offer.company.id, name: offer.company.name },
|
|
||||||
id: offer.id,
|
|
||||||
jobType: offer.jobType,
|
|
||||||
level: offer.OffersFullTime?.level,
|
|
||||||
monthYearReceived: offer.monthYearReceived,
|
|
||||||
monthlySalary: offer.OffersIntern?.monthlySalary?.value,
|
|
||||||
negotiationStrategy: offer.negotiationStrategy,
|
|
||||||
profile: {
|
|
||||||
background: {
|
|
||||||
experiences: background?.experiences.map(
|
|
||||||
(exp: { company: { id: any; name: any }; id: any }) => {
|
|
||||||
return {
|
|
||||||
company: { id: exp.company.id, name: exp.company.name },
|
|
||||||
id: exp.id,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
),
|
|
||||||
id: background?.id,
|
|
||||||
totalYoe: background?.totalYoe,
|
|
||||||
},
|
|
||||||
id: offer.profileId,
|
|
||||||
name: offer.profile.profileName,
|
|
||||||
},
|
|
||||||
specialization:
|
|
||||||
offer.jobType === JobType.FULLTIME
|
|
||||||
? offer.OffersFullTime?.specialization
|
|
||||||
: offer.OffersIntern?.specialization,
|
|
||||||
title:
|
|
||||||
offer.jobType === JobType.FULLTIME
|
|
||||||
? offer.OffersFullTime?.title
|
|
||||||
: offer.OffersIntern?.title,
|
|
||||||
totalCompensation: offer.OffersFullTime?.totalCompensation?.value,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const specificAnalysisDtoMapper = (
|
|
||||||
noOfOffers: number,
|
|
||||||
percentile: number,
|
|
||||||
topPercentileOffers: Array<any>,
|
|
||||||
) => {
|
|
||||||
return {
|
|
||||||
noOfOffers,
|
|
||||||
percentile,
|
|
||||||
topPercentileCompanyOffers: topPercentileDtoMapper(topPercentileOffers),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const highestOfferDtoMapper = (
|
|
||||||
offer: OffersOffer & {
|
|
||||||
OffersFullTime:
|
|
||||||
| (OffersFullTime & { totalCompensation: OffersCurrency })
|
|
||||||
| null;
|
|
||||||
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
|
|
||||||
company: Company;
|
|
||||||
profile: OffersProfile & { background: OffersBackground | null };
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
return {
|
|
||||||
company: { id: offer.company.id, name: offer.company.name },
|
|
||||||
id: offer.id,
|
|
||||||
level: offer.OffersFullTime?.level,
|
|
||||||
location: offer.location,
|
|
||||||
specialization:
|
|
||||||
offer.jobType === JobType.FULLTIME
|
|
||||||
? offer.OffersFullTime?.specialization
|
|
||||||
: offer.OffersIntern?.specialization,
|
|
||||||
totalYoe: offer.profile.background?.totalYoe,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const profileAnalysisDtoMapper = (
|
|
||||||
analysisId: string,
|
|
||||||
profileId: string,
|
|
||||||
overallHighestOffer: OffersOffer & {
|
|
||||||
OffersFullTime:
|
|
||||||
| (OffersFullTime & { totalCompensation: OffersCurrency })
|
|
||||||
| null;
|
|
||||||
OffersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
|
|
||||||
company: Company;
|
|
||||||
profile: OffersProfile & { background: OffersBackground | null };
|
|
||||||
},
|
|
||||||
noOfSimilarOffers: number,
|
|
||||||
overallPercentile: number,
|
|
||||||
topPercentileOffers: Array<any>,
|
|
||||||
noOfSimilarCompanyOffers: number,
|
|
||||||
companyPercentile: number,
|
|
||||||
topPercentileCompanyOffers: Array<any>,
|
|
||||||
) => {
|
|
||||||
return {
|
|
||||||
companyAnalysis: specificAnalysisDtoMapper(
|
|
||||||
noOfSimilarCompanyOffers,
|
|
||||||
companyPercentile,
|
|
||||||
topPercentileCompanyOffers,
|
|
||||||
),
|
|
||||||
id: analysisId,
|
|
||||||
overallAnalysis: specificAnalysisDtoMapper(
|
|
||||||
noOfSimilarOffers,
|
|
||||||
overallPercentile,
|
|
||||||
topPercentileOffers,
|
|
||||||
),
|
|
||||||
overallHighestOffer: highestOfferDtoMapper(overallHighestOffer),
|
|
||||||
profileId,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const offersAnalysisRouter = createRouter()
|
export const offersAnalysisRouter = createRouter()
|
||||||
.query('generate', {
|
.query('generate', {
|
||||||
input: z.object({
|
input: z.object({
|
||||||
@ -213,7 +114,7 @@ export const offersAnalysisRouter = createRouter()
|
|||||||
|
|
||||||
const overallHighestOffer = offers[0];
|
const overallHighestOffer = offers[0];
|
||||||
|
|
||||||
// TODO: Shift yoe to background to make it mandatory
|
// TODO: Shift yoe out of background to make it mandatory
|
||||||
if (
|
if (
|
||||||
!overallHighestOffer.profile.background ||
|
!overallHighestOffer.profile.background ||
|
||||||
!overallHighestOffer.profile.background.totalYoe
|
!overallHighestOffer.profile.background.totalYoe
|
||||||
@ -465,17 +366,7 @@ export const offersAnalysisRouter = createRouter()
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return profileAnalysisDtoMapper(
|
return profileAnalysisDtoMapper(analysis);
|
||||||
analysis.id,
|
|
||||||
analysis.profileId,
|
|
||||||
overallHighestOffer,
|
|
||||||
noOfSimilarOffers,
|
|
||||||
overallPercentile,
|
|
||||||
topPercentileOffers,
|
|
||||||
noOfSimilarCompanyOffers,
|
|
||||||
companyPercentile,
|
|
||||||
topPercentileCompanyOffers,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.query('get', {
|
.query('get', {
|
||||||
@ -574,16 +465,6 @@ export const offersAnalysisRouter = createRouter()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return profileAnalysisDtoMapper(
|
return profileAnalysisDtoMapper(analysis);
|
||||||
analysis.id,
|
|
||||||
analysis.profileId,
|
|
||||||
analysis.overallHighestOffer,
|
|
||||||
analysis.noOfSimilarOffers,
|
|
||||||
analysis.overallPercentile,
|
|
||||||
analysis.topOverallOffers,
|
|
||||||
analysis.noOfSimilarCompanyOffers,
|
|
||||||
analysis.companyPercentile,
|
|
||||||
analysis.topCompanyOffers,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,264 +1,329 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import * as trpc from '@trpc/server';
|
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()
|
const result = await ctx.prisma.offersProfile.findFirst({
|
||||||
.query('getComments', {
|
include: {
|
||||||
input: z.object({
|
discussion: {
|
||||||
profileId: z.string()
|
include: {
|
||||||
}),
|
replies: {
|
||||||
async resolve({ ctx, input }) {
|
|
||||||
|
|
||||||
const profile = await ctx.prisma.offersProfile.findFirst({
|
|
||||||
where: {
|
|
||||||
id: input.profileId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await ctx.prisma.offersProfile.findFirst({
|
|
||||||
include: {
|
include: {
|
||||||
discussion: {
|
user: true,
|
||||||
include: {
|
|
||||||
replies: {
|
|
||||||
include: {
|
|
||||||
user: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
replyingTo: 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) {
|
x.replies?.map((y) => {
|
||||||
return result.discussion
|
if (y.user == null) {
|
||||||
.filter((x: Reply) => x.replyingToId === null)
|
y.user = {
|
||||||
.map((x: Reply) => {
|
email: '',
|
||||||
if (x.user == null) {
|
emailVerified: null,
|
||||||
x.user = {
|
id: '',
|
||||||
email: "",
|
image: '',
|
||||||
emailVerified: null,
|
name: profile?.profileName ?? '<missing name>',
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
if (input.replyingToId) {
|
const replyType: Reply = {
|
||||||
await ctx.prisma.offersReply.update({
|
createdAt: x.createdAt,
|
||||||
data: {
|
id: x.id,
|
||||||
replyingTo: {
|
message: x.message,
|
||||||
connect: {
|
replies: x.replies.map((reply) => {
|
||||||
id: input.replyingToId
|
return {
|
||||||
}
|
createdAt: reply.createdAt,
|
||||||
}
|
id: reply.id,
|
||||||
},
|
message: reply.message,
|
||||||
where: {
|
replies: [],
|
||||||
id: createdReply.id
|
replyingToId: reply.replyingToId,
|
||||||
}
|
user: reply.user
|
||||||
})
|
}
|
||||||
}
|
}),
|
||||||
|
replyingToId: x.replyingToId,
|
||||||
|
user: x.user
|
||||||
|
}
|
||||||
|
|
||||||
if (input.userId) {
|
return replyType
|
||||||
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (result) {
|
return discussions
|
||||||
return result.discussion.filter((x) => x.replyingToId === null)
|
},
|
||||||
}
|
})
|
||||||
|
.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
|
const profileEditToken = profile?.editToken;
|
||||||
}
|
|
||||||
})
|
if (input.token === profileEditToken || input.userId) {
|
||||||
.mutation("update", {
|
const createdReply = await ctx.prisma.offersReply.create({
|
||||||
input: z.object({
|
data: {
|
||||||
id: z.string(),
|
message: input.message,
|
||||||
message: z.string(),
|
profile: {
|
||||||
profileId: z.string(),
|
connect: {
|
||||||
// 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,
|
id: input.profileId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (input.replyingToId) {
|
||||||
|
await ctx.prisma.offersReply.update({
|
||||||
|
data: {
|
||||||
|
replyingTo: {
|
||||||
|
connect: {
|
||||||
|
id: input.replyingToId,
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
},
|
||||||
const profileEditToken = profile?.editToken;
|
where: {
|
||||||
|
id: createdReply.id,
|
||||||
// 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.'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.mutation("delete", {
|
if (input.userId) {
|
||||||
input: z.object({
|
await ctx.prisma.offersReply.update({
|
||||||
id: z.string(),
|
data: {
|
||||||
profileId: z.string(),
|
user: {
|
||||||
// Have to pass in either userID or token for validation
|
connect: {
|
||||||
token: z.string().optional(),
|
id: input.userId,
|
||||||
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;
|
where: {
|
||||||
|
id: createdReply.id,
|
||||||
// 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.'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
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 { z } from 'zod';
|
||||||
import * as trpc from '@trpc/server';
|
import * as trpc from '@trpc/server';
|
||||||
|
|
||||||
import { createRouter } from '../context';
|
import {
|
||||||
|
addToProfileResponseMapper,
|
||||||
|
createOfferProfileResponseMapper,
|
||||||
|
profileDtoMapper,
|
||||||
|
} from '~/mappers/offers-mappers';
|
||||||
|
|
||||||
import type { OffersProfile } from '~/types/offers-profile';
|
import { createRouter } from '../context';
|
||||||
|
|
||||||
const valuation = z.object({
|
const valuation = z.object({
|
||||||
currency: z.string(),
|
currency: z.string(),
|
||||||
@ -19,41 +23,45 @@ const company = z.object({
|
|||||||
logoUrl: z.string().nullish(),
|
logoUrl: z.string().nullish(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
slug: z.string(),
|
slug: z.string(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
})
|
});
|
||||||
|
|
||||||
const offer = z.object({
|
const offer = z.object({
|
||||||
OffersFullTime: z.object({
|
OffersFullTime: z
|
||||||
baseSalary: valuation.nullish(),
|
.object({
|
||||||
baseSalaryId: z.string().nullish(),
|
baseSalary: valuation.nullish(),
|
||||||
bonus: valuation.nullish(),
|
baseSalaryId: z.string().nullish(),
|
||||||
bonusId: z.string().nullish(),
|
bonus: valuation.nullish(),
|
||||||
id: z.string().optional(),
|
bonusId: z.string().nullish(),
|
||||||
level: z.string().nullish(),
|
id: z.string().optional(),
|
||||||
specialization: z.string(),
|
level: z.string().nullish(),
|
||||||
stocks: valuation.nullish(),
|
specialization: z.string(),
|
||||||
stocksId: z.string().nullish(),
|
stocks: valuation.nullish(),
|
||||||
title: z.string(),
|
stocksId: z.string().nullish(),
|
||||||
totalCompensation: valuation.nullish(),
|
title: z.string(),
|
||||||
totalCompensationId: z.string().nullish(),
|
totalCompensation: valuation.nullish(),
|
||||||
}).nullish(),
|
totalCompensationId: z.string().nullish(),
|
||||||
OffersIntern: z.object({
|
})
|
||||||
id: z.string().optional(),
|
.nullish(),
|
||||||
internshipCycle: z.string().nullish(),
|
OffersIntern: z
|
||||||
monthlySalary: valuation.nullish(),
|
.object({
|
||||||
specialization: z.string(),
|
id: z.string().optional(),
|
||||||
startYear: z.number().nullish(),
|
internshipCycle: z.string().nullish(),
|
||||||
title: z.string(),
|
monthlySalary: valuation.nullish(),
|
||||||
totalCompensation: valuation.nullish(), // Full time
|
specialization: z.string(),
|
||||||
}).nullish(),
|
startYear: z.number().nullish(),
|
||||||
comments: z.string().nullish(),
|
title: z.string(),
|
||||||
|
totalCompensation: valuation.nullish(), // Full time
|
||||||
|
})
|
||||||
|
.nullish(),
|
||||||
|
comments: z.string(),
|
||||||
company: company.nullish(),
|
company: company.nullish(),
|
||||||
companyId: z.string(),
|
companyId: z.string(),
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
jobType: z.string(),
|
jobType: z.string(),
|
||||||
location: z.string(),
|
location: z.string(),
|
||||||
monthYearReceived: z.date(),
|
monthYearReceived: z.date(),
|
||||||
negotiationStrategy: z.string().nullish(),
|
negotiationStrategy: z.string(),
|
||||||
offersFullTimeId: z.string().nullish(),
|
offersFullTimeId: z.string().nullish(),
|
||||||
offersInternId: z.string().nullish(),
|
offersInternId: z.string().nullish(),
|
||||||
profileId: z.string().nullish(),
|
profileId: z.string().nullish(),
|
||||||
@ -72,7 +80,7 @@ const experience = z.object({
|
|||||||
specialization: z.string().nullish(),
|
specialization: z.string().nullish(),
|
||||||
title: z.string().nullish(),
|
title: z.string().nullish(),
|
||||||
totalCompensation: valuation.nullish(),
|
totalCompensation: valuation.nullish(),
|
||||||
totalCompensationId: z.string().nullish()
|
totalCompensationId: z.string().nullish(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const education = z.object({
|
const education = z.object({
|
||||||
@ -91,32 +99,8 @@ const reply = z.object({
|
|||||||
messages: z.string().nullish(),
|
messages: z.string().nullish(),
|
||||||
profileId: z.string().nullish(),
|
profileId: z.string().nullish(),
|
||||||
replyingToId: z.string().nullish(),
|
replyingToId: z.string().nullish(),
|
||||||
userId: z.string().nullish()
|
userId: z.string().nullish(),
|
||||||
})
|
});
|
||||||
|
|
||||||
type WithIsEditable<T> = T & {
|
|
||||||
isEditable: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
function computeIsEditable(
|
|
||||||
profileInput: OffersProfile,
|
|
||||||
editToken?: string,
|
|
||||||
): WithIsEditable<OffersProfile> {
|
|
||||||
return {
|
|
||||||
...profileInput,
|
|
||||||
isEditable: profileInput.editToken === editToken,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function exclude<Key extends keyof WithIsEditable<OffersProfile>>(
|
|
||||||
profile: WithIsEditable<OffersProfile>,
|
|
||||||
...keys: Array<Key>
|
|
||||||
): Omit<WithIsEditable<OffersProfile>, Key> {
|
|
||||||
for (const key of keys) {
|
|
||||||
delete profile[key];
|
|
||||||
}
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const offersProfileRouter = createRouter()
|
export const offersProfileRouter = createRouter()
|
||||||
.query('listOne', {
|
.query('listOne', {
|
||||||
@ -127,6 +111,86 @@ export const offersProfileRouter = createRouter()
|
|||||||
async resolve({ ctx, input }) {
|
async resolve({ ctx, input }) {
|
||||||
const result = await ctx.prisma.offersProfile.findFirst({
|
const result = await ctx.prisma.offersProfile.findFirst({
|
||||||
include: {
|
include: {
|
||||||
|
analysis: {
|
||||||
|
include: {
|
||||||
|
overallHighestOffer: {
|
||||||
|
include: {
|
||||||
|
OffersFullTime: {
|
||||||
|
include: {
|
||||||
|
totalCompensation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OffersIntern: {
|
||||||
|
include: {
|
||||||
|
monthlySalary: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
company: true,
|
||||||
|
profile: {
|
||||||
|
include: {
|
||||||
|
background: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
topCompanyOffers: {
|
||||||
|
include: {
|
||||||
|
OffersFullTime: {
|
||||||
|
include: {
|
||||||
|
totalCompensation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OffersIntern: {
|
||||||
|
include: {
|
||||||
|
monthlySalary: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
company: true,
|
||||||
|
profile: {
|
||||||
|
include: {
|
||||||
|
background: {
|
||||||
|
include: {
|
||||||
|
experiences: {
|
||||||
|
include: {
|
||||||
|
company: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
topOverallOffers: {
|
||||||
|
include: {
|
||||||
|
OffersFullTime: {
|
||||||
|
include: {
|
||||||
|
totalCompensation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OffersIntern: {
|
||||||
|
include: {
|
||||||
|
monthlySalary: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
company: true,
|
||||||
|
profile: {
|
||||||
|
include: {
|
||||||
|
background: {
|
||||||
|
include: {
|
||||||
|
experiences: {
|
||||||
|
include: {
|
||||||
|
company: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
background: {
|
background: {
|
||||||
include: {
|
include: {
|
||||||
educations: true,
|
educations: true,
|
||||||
@ -144,7 +208,7 @@ export const offersProfileRouter = createRouter()
|
|||||||
include: {
|
include: {
|
||||||
replies: true,
|
replies: true,
|
||||||
replyingTo: true,
|
replyingTo: true,
|
||||||
user: true
|
user: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
offers: {
|
offers: {
|
||||||
@ -172,7 +236,7 @@ export const offersProfileRouter = createRouter()
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
return exclude(computeIsEditable(result, input.token), 'editToken')
|
return profileDtoMapper(result, input.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new trpc.TRPCError({
|
throw new trpc.TRPCError({
|
||||||
@ -389,7 +453,8 @@ export const offersProfileRouter = createRouter()
|
|||||||
title: x.OffersFullTime.title,
|
title: x.OffersFullTime.title,
|
||||||
totalCompensation: {
|
totalCompensation: {
|
||||||
create: {
|
create: {
|
||||||
currency: x.OffersFullTime.totalCompensation?.currency,
|
currency:
|
||||||
|
x.OffersFullTime.totalCompensation?.currency,
|
||||||
value: x.OffersFullTime.totalCompensation?.value,
|
value: x.OffersFullTime.totalCompensation?.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -417,41 +482,9 @@ export const offersProfileRouter = createRouter()
|
|||||||
},
|
},
|
||||||
profileName: randomUUID().substring(0, 10),
|
profileName: randomUUID().substring(0, 10),
|
||||||
},
|
},
|
||||||
include: {
|
|
||||||
background: {
|
|
||||||
include: {
|
|
||||||
educations: true,
|
|
||||||
experiences: {
|
|
||||||
include: {
|
|
||||||
company: true,
|
|
||||||
monthlySalary: true,
|
|
||||||
totalCompensation: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
specificYoes: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
offers: {
|
|
||||||
include: {
|
|
||||||
OffersFullTime: {
|
|
||||||
include: {
|
|
||||||
baseSalary: true,
|
|
||||||
bonus: true,
|
|
||||||
stocks: true,
|
|
||||||
totalCompensation: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
OffersIntern: {
|
|
||||||
include: {
|
|
||||||
monthlySalary: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
// TODO: add analysis to profile object then return
|
|
||||||
return profile;
|
return createOfferProfileResponseMapper(profile, token);
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.mutation('delete', {
|
.mutation('delete', {
|
||||||
@ -468,11 +501,13 @@ export const offersProfileRouter = createRouter()
|
|||||||
const profileEditToken = profileToDelete?.editToken;
|
const profileEditToken = profileToDelete?.editToken;
|
||||||
|
|
||||||
if (profileEditToken === input.token) {
|
if (profileEditToken === input.token) {
|
||||||
return await ctx.prisma.offersProfile.delete({
|
const deletedProfile = await ctx.prisma.offersProfile.delete({
|
||||||
where: {
|
where: {
|
||||||
id: input.profileId,
|
id: input.profileId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return deletedProfile.id;
|
||||||
}
|
}
|
||||||
// TODO: Throw 401
|
// TODO: Throw 401
|
||||||
throw new trpc.TRPCError({
|
throw new trpc.TRPCError({
|
||||||
@ -493,7 +528,7 @@ export const offersProfileRouter = createRouter()
|
|||||||
backgroundId: z.string().optional(),
|
backgroundId: z.string().optional(),
|
||||||
domain: z.string(),
|
domain: z.string(),
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
yoe: z.number()
|
yoe: z.number(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
totalYoe: z.number(),
|
totalYoe: z.number(),
|
||||||
@ -505,7 +540,7 @@ export const offersProfileRouter = createRouter()
|
|||||||
offers: z.array(offer),
|
offers: z.array(offer),
|
||||||
profileName: z.string(),
|
profileName: z.string(),
|
||||||
token: z.string(),
|
token: z.string(),
|
||||||
userId: z.string().nullish()
|
userId: z.string().nullish(),
|
||||||
}),
|
}),
|
||||||
async resolve({ ctx, input }) {
|
async resolve({ ctx, input }) {
|
||||||
const profileToUpdate = await ctx.prisma.offersProfile.findFirst({
|
const profileToUpdate = await ctx.prisma.offersProfile.findFirst({
|
||||||
@ -522,17 +557,17 @@ export const offersProfileRouter = createRouter()
|
|||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.id,
|
id: input.id,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await ctx.prisma.offersBackground.update({
|
await ctx.prisma.offersBackground.update({
|
||||||
data: {
|
data: {
|
||||||
totalYoe: input.background.totalYoe
|
totalYoe: input.background.totalYoe,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.background.id
|
id: input.background.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
for (const edu of input.background.educations) {
|
for (const edu of input.background.educations) {
|
||||||
if (edu.id) {
|
if (edu.id) {
|
||||||
@ -545,27 +580,26 @@ export const offersProfileRouter = createRouter()
|
|||||||
type: edu.type,
|
type: edu.type,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: edu.id
|
id: edu.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
await ctx.prisma.offersBackground.update({
|
await ctx.prisma.offersBackground.update({
|
||||||
data: {
|
data: {
|
||||||
educations: {
|
educations: {
|
||||||
create:
|
create: {
|
||||||
{
|
|
||||||
endDate: edu.endDate,
|
endDate: edu.endDate,
|
||||||
field: edu.field,
|
field: edu.field,
|
||||||
school: edu.school,
|
school: edu.school,
|
||||||
startDate: edu.startDate,
|
startDate: edu.startDate,
|
||||||
type: edu.type,
|
type: edu.type,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.background.id
|
id: input.background.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,9 +613,9 @@ export const offersProfileRouter = createRouter()
|
|||||||
specialization: exp.specialization,
|
specialization: exp.specialization,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: exp.id
|
id: exp.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
if (exp.monthlySalary) {
|
if (exp.monthlySalary) {
|
||||||
await ctx.prisma.offersCurrency.update({
|
await ctx.prisma.offersCurrency.update({
|
||||||
@ -590,9 +624,9 @@ export const offersProfileRouter = createRouter()
|
|||||||
value: exp.monthlySalary.value,
|
value: exp.monthlySalary.value,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: exp.monthlySalary.id
|
id: exp.monthlySalary.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exp.totalCompensation) {
|
if (exp.totalCompensation) {
|
||||||
@ -602,12 +636,16 @@ export const offersProfileRouter = createRouter()
|
|||||||
value: exp.totalCompensation.value,
|
value: exp.totalCompensation.value,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: exp.totalCompensation.id
|
id: exp.totalCompensation.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
} else if (!exp.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) {
|
if (exp.companyId) {
|
||||||
await ctx.prisma.offersBackground.update({
|
await ctx.prisma.offersBackground.update({
|
||||||
data: {
|
data: {
|
||||||
@ -630,12 +668,12 @@ export const offersProfileRouter = createRouter()
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.background.id
|
id: input.background.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
await ctx.prisma.offersBackground.update({
|
await ctx.prisma.offersBackground.update({
|
||||||
data: {
|
data: {
|
||||||
@ -652,16 +690,15 @@ export const offersProfileRouter = createRouter()
|
|||||||
value: exp.totalCompensation?.value,
|
value: exp.totalCompensation?.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.background.id
|
id: input.background.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
} else if (
|
||||||
else if (
|
|
||||||
exp.jobType === 'INTERN' &&
|
exp.jobType === 'INTERN' &&
|
||||||
exp.monthlySalary?.currency !== undefined &&
|
exp.monthlySalary?.currency !== undefined &&
|
||||||
exp.monthlySalary.value !== undefined
|
exp.monthlySalary.value !== undefined
|
||||||
@ -686,13 +723,13 @@ export const offersProfileRouter = createRouter()
|
|||||||
},
|
},
|
||||||
specialization: exp.specialization,
|
specialization: exp.specialization,
|
||||||
title: exp.title,
|
title: exp.title,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.background.id
|
id: input.background.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
await ctx.prisma.offersBackground.update({
|
await ctx.prisma.offersBackground.update({
|
||||||
data: {
|
data: {
|
||||||
@ -708,44 +745,42 @@ export const offersProfileRouter = createRouter()
|
|||||||
},
|
},
|
||||||
specialization: exp.specialization,
|
specialization: exp.specialization,
|
||||||
title: exp.title,
|
title: exp.title,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.background.id
|
id: input.background.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const yoe of input.background.specificYoes) {
|
for (const yoe of input.background.specificYoes) {
|
||||||
if (yoe.id) {
|
if (yoe.id) {
|
||||||
await ctx.prisma.offersSpecificYoe.update({
|
await ctx.prisma.offersSpecificYoe.update({
|
||||||
data: {
|
data: {
|
||||||
...yoe
|
...yoe,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: yoe.id
|
id: yoe.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
await ctx.prisma.offersBackground.update({
|
await ctx.prisma.offersBackground.update({
|
||||||
data: {
|
data: {
|
||||||
specificYoes: {
|
specificYoes: {
|
||||||
create:
|
create: {
|
||||||
{
|
|
||||||
domain: yoe.domain,
|
domain: yoe.domain,
|
||||||
yoe: yoe.yoe,
|
yoe: yoe.yoe,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.background.id
|
id: input.background.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,42 +795,46 @@ export const offersProfileRouter = createRouter()
|
|||||||
negotiationStrategy: offerToUpdate.negotiationStrategy,
|
negotiationStrategy: offerToUpdate.negotiationStrategy,
|
||||||
},
|
},
|
||||||
where: {
|
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({
|
await ctx.prisma.offersOffer.update({
|
||||||
data: {
|
data: {
|
||||||
jobType: offerToUpdate.jobType
|
jobType: offerToUpdate.jobType,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: offerToUpdate.id
|
id: offerToUpdate.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offerToUpdate.OffersIntern?.monthlySalary) {
|
if (offerToUpdate.OffersIntern?.monthlySalary) {
|
||||||
await ctx.prisma.offersIntern.update({
|
await ctx.prisma.offersIntern.update({
|
||||||
data: {
|
data: {
|
||||||
internshipCycle: offerToUpdate.OffersIntern.internshipCycle ?? undefined,
|
internshipCycle:
|
||||||
|
offerToUpdate.OffersIntern.internshipCycle ?? undefined,
|
||||||
specialization: offerToUpdate.OffersIntern.specialization,
|
specialization: offerToUpdate.OffersIntern.specialization,
|
||||||
startYear: offerToUpdate.OffersIntern.startYear ?? undefined,
|
startYear: offerToUpdate.OffersIntern.startYear ?? undefined,
|
||||||
title: offerToUpdate.OffersIntern.title,
|
title: offerToUpdate.OffersIntern.title,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: offerToUpdate.OffersIntern.id,
|
id: offerToUpdate.OffersIntern.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
await ctx.prisma.offersCurrency.update({
|
await ctx.prisma.offersCurrency.update({
|
||||||
data: {
|
data: {
|
||||||
currency: offerToUpdate.OffersIntern.monthlySalary.currency,
|
currency: offerToUpdate.OffersIntern.monthlySalary.currency,
|
||||||
value: offerToUpdate.OffersIntern.monthlySalary.value
|
value: offerToUpdate.OffersIntern.monthlySalary.value,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: offerToUpdate.OffersIntern.monthlySalary.id
|
id: offerToUpdate.OffersIntern.monthlySalary.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offerToUpdate.OffersFullTime?.totalCompensation) {
|
if (offerToUpdate.OffersFullTime?.totalCompensation) {
|
||||||
@ -807,54 +846,55 @@ export const offersProfileRouter = createRouter()
|
|||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: offerToUpdate.OffersFullTime.id,
|
id: offerToUpdate.OffersFullTime.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
if (offerToUpdate.OffersFullTime.baseSalary) {
|
if (offerToUpdate.OffersFullTime.baseSalary) {
|
||||||
await ctx.prisma.offersCurrency.update({
|
await ctx.prisma.offersCurrency.update({
|
||||||
data: {
|
data: {
|
||||||
currency: offerToUpdate.OffersFullTime.baseSalary.currency,
|
currency: offerToUpdate.OffersFullTime.baseSalary.currency,
|
||||||
value: offerToUpdate.OffersFullTime.baseSalary.value
|
value: offerToUpdate.OffersFullTime.baseSalary.value,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: offerToUpdate.OffersFullTime.baseSalary.id
|
id: offerToUpdate.OffersFullTime.baseSalary.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (offerToUpdate.OffersFullTime.bonus) {
|
if (offerToUpdate.OffersFullTime.bonus) {
|
||||||
await ctx.prisma.offersCurrency.update({
|
await ctx.prisma.offersCurrency.update({
|
||||||
data: {
|
data: {
|
||||||
currency: offerToUpdate.OffersFullTime.bonus.currency,
|
currency: offerToUpdate.OffersFullTime.bonus.currency,
|
||||||
value: offerToUpdate.OffersFullTime.bonus.value
|
value: offerToUpdate.OffersFullTime.bonus.value,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: offerToUpdate.OffersFullTime.bonus.id
|
id: offerToUpdate.OffersFullTime.bonus.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (offerToUpdate.OffersFullTime.stocks) {
|
if (offerToUpdate.OffersFullTime.stocks) {
|
||||||
await ctx.prisma.offersCurrency.update({
|
await ctx.prisma.offersCurrency.update({
|
||||||
data: {
|
data: {
|
||||||
currency: offerToUpdate.OffersFullTime.stocks.currency,
|
currency: offerToUpdate.OffersFullTime.stocks.currency,
|
||||||
value: offerToUpdate.OffersFullTime.stocks.value
|
value: offerToUpdate.OffersFullTime.stocks.value,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: offerToUpdate.OffersFullTime.stocks.id
|
id: offerToUpdate.OffersFullTime.stocks.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
await ctx.prisma.offersCurrency.update({
|
await ctx.prisma.offersCurrency.update({
|
||||||
data: {
|
data: {
|
||||||
currency: offerToUpdate.OffersFullTime.totalCompensation.currency,
|
currency:
|
||||||
value: offerToUpdate.OffersFullTime.totalCompensation.value
|
offerToUpdate.OffersFullTime.totalCompensation.currency,
|
||||||
|
value: offerToUpdate.OffersFullTime.totalCompensation.value,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: offerToUpdate.OffersFullTime.totalCompensation.id
|
id: offerToUpdate.OffersFullTime.totalCompensation.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (
|
if (
|
||||||
offerToUpdate.jobType === "INTERN" &&
|
offerToUpdate.jobType === 'INTERN' &&
|
||||||
offerToUpdate.OffersIntern &&
|
offerToUpdate.OffersIntern &&
|
||||||
offerToUpdate.OffersIntern.internshipCycle &&
|
offerToUpdate.OffersIntern.internshipCycle &&
|
||||||
offerToUpdate.OffersIntern.monthlySalary?.currency &&
|
offerToUpdate.OffersIntern.monthlySalary?.currency &&
|
||||||
@ -867,14 +907,19 @@ export const offersProfileRouter = createRouter()
|
|||||||
create: {
|
create: {
|
||||||
OffersIntern: {
|
OffersIntern: {
|
||||||
create: {
|
create: {
|
||||||
internshipCycle: offerToUpdate.OffersIntern.internshipCycle,
|
internshipCycle:
|
||||||
|
offerToUpdate.OffersIntern.internshipCycle,
|
||||||
monthlySalary: {
|
monthlySalary: {
|
||||||
create: {
|
create: {
|
||||||
currency: offerToUpdate.OffersIntern.monthlySalary?.currency,
|
currency:
|
||||||
value: offerToUpdate.OffersIntern.monthlySalary?.value,
|
offerToUpdate.OffersIntern.monthlySalary
|
||||||
|
?.currency,
|
||||||
|
value:
|
||||||
|
offerToUpdate.OffersIntern.monthlySalary?.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
specialization: offerToUpdate.OffersIntern.specialization,
|
specialization:
|
||||||
|
offerToUpdate.OffersIntern.specialization,
|
||||||
startYear: offerToUpdate.OffersIntern.startYear,
|
startYear: offerToUpdate.OffersIntern.startYear,
|
||||||
title: offerToUpdate.OffersIntern.title,
|
title: offerToUpdate.OffersIntern.title,
|
||||||
},
|
},
|
||||||
@ -889,13 +934,13 @@ export const offersProfileRouter = createRouter()
|
|||||||
location: offerToUpdate.location,
|
location: offerToUpdate.location,
|
||||||
monthYearReceived: offerToUpdate.monthYearReceived,
|
monthYearReceived: offerToUpdate.monthYearReceived,
|
||||||
negotiationStrategy: offerToUpdate.negotiationStrategy,
|
negotiationStrategy: offerToUpdate.negotiationStrategy,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.id,
|
id: input.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
offerToUpdate.jobType === 'FULLTIME' &&
|
offerToUpdate.jobType === 'FULLTIME' &&
|
||||||
@ -918,29 +963,39 @@ export const offersProfileRouter = createRouter()
|
|||||||
create: {
|
create: {
|
||||||
baseSalary: {
|
baseSalary: {
|
||||||
create: {
|
create: {
|
||||||
currency: offerToUpdate.OffersFullTime.baseSalary?.currency,
|
currency:
|
||||||
value: offerToUpdate.OffersFullTime.baseSalary?.value,
|
offerToUpdate.OffersFullTime.baseSalary
|
||||||
|
?.currency,
|
||||||
|
value:
|
||||||
|
offerToUpdate.OffersFullTime.baseSalary?.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
bonus: {
|
bonus: {
|
||||||
create: {
|
create: {
|
||||||
currency: offerToUpdate.OffersFullTime.bonus?.currency,
|
currency:
|
||||||
|
offerToUpdate.OffersFullTime.bonus?.currency,
|
||||||
value: offerToUpdate.OffersFullTime.bonus?.value,
|
value: offerToUpdate.OffersFullTime.bonus?.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
level: offerToUpdate.OffersFullTime.level,
|
level: offerToUpdate.OffersFullTime.level,
|
||||||
specialization: offerToUpdate.OffersFullTime.specialization,
|
specialization:
|
||||||
|
offerToUpdate.OffersFullTime.specialization,
|
||||||
stocks: {
|
stocks: {
|
||||||
create: {
|
create: {
|
||||||
currency: offerToUpdate.OffersFullTime.stocks?.currency,
|
currency:
|
||||||
|
offerToUpdate.OffersFullTime.stocks?.currency,
|
||||||
value: offerToUpdate.OffersFullTime.stocks?.value,
|
value: offerToUpdate.OffersFullTime.stocks?.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
title: offerToUpdate.OffersFullTime.title,
|
title: offerToUpdate.OffersFullTime.title,
|
||||||
totalCompensation: {
|
totalCompensation: {
|
||||||
create: {
|
create: {
|
||||||
currency: offerToUpdate.OffersFullTime.totalCompensation?.currency,
|
currency:
|
||||||
value: offerToUpdate.OffersFullTime.totalCompensation?.value,
|
offerToUpdate.OffersFullTime.totalCompensation
|
||||||
|
?.currency,
|
||||||
|
value:
|
||||||
|
offerToUpdate.OffersFullTime.totalCompensation
|
||||||
|
?.value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -955,17 +1010,17 @@ export const offersProfileRouter = createRouter()
|
|||||||
location: offerToUpdate.location,
|
location: offerToUpdate.location,
|
||||||
monthYearReceived: offerToUpdate.monthYearReceived,
|
monthYearReceived: offerToUpdate.monthYearReceived,
|
||||||
negotiationStrategy: offerToUpdate.negotiationStrategy,
|
negotiationStrategy: offerToUpdate.negotiationStrategy,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.id,
|
id: input.id,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: add analysis to profile object then return
|
|
||||||
const result = await ctx.prisma.offersProfile.findFirst({
|
const result = await ctx.prisma.offersProfile.findFirst({
|
||||||
include: {
|
include: {
|
||||||
background: {
|
background: {
|
||||||
@ -985,7 +1040,7 @@ export const offersProfileRouter = createRouter()
|
|||||||
include: {
|
include: {
|
||||||
replies: true,
|
replies: true,
|
||||||
replyingTo: true,
|
replyingTo: true,
|
||||||
user: true
|
user: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
offers: {
|
offers: {
|
||||||
@ -1013,7 +1068,7 @@ export const offersProfileRouter = createRouter()
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
return exclude(computeIsEditable(result, input.token), 'editToken')
|
return createOfferProfileResponseMapper(result, input.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new trpc.TRPCError({
|
throw new trpc.TRPCError({
|
||||||
@ -1036,9 +1091,9 @@ export const offersProfileRouter = createRouter()
|
|||||||
}),
|
}),
|
||||||
async resolve({ ctx, input }) {
|
async resolve({ ctx, input }) {
|
||||||
const profile = await ctx.prisma.offersProfile.findFirst({
|
const profile = await ctx.prisma.offersProfile.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: input.profileId,
|
id: input.profileId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const profileEditToken = profile?.editToken;
|
const profileEditToken = profile?.editToken;
|
||||||
@ -1048,25 +1103,21 @@ export const offersProfileRouter = createRouter()
|
|||||||
data: {
|
data: {
|
||||||
user: {
|
user: {
|
||||||
connect: {
|
connect: {
|
||||||
id: input.userId
|
id: input.userId,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
id: input.profileId
|
id: input.profileId,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return {
|
return addToProfileResponseMapper(updated);
|
||||||
id: updated.id,
|
|
||||||
profileName: updated.profileName,
|
|
||||||
userId: updated.userId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new trpc.TRPCError({
|
throw new trpc.TRPCError({
|
||||||
code: 'UNAUTHORIZED',
|
code: 'UNAUTHORIZED',
|
||||||
message: 'Invalid token.',
|
message: 'Invalid token.',
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { TRPCError } from '@trpc/server';
|
import { TRPCError } from '@trpc/server';
|
||||||
|
|
||||||
|
import {
|
||||||
|
dashboardOfferDtoMapper,
|
||||||
|
getOffersResponseMapper,
|
||||||
|
} from '~/mappers/offers-mappers';
|
||||||
|
|
||||||
import { createRouter } from '../context';
|
import { createRouter } from '../context';
|
||||||
|
|
||||||
const yoeCategoryMap: Record<number, string> = {
|
const yoeCategoryMap: Record<number, string> = {
|
||||||
@ -299,14 +304,14 @@ export const offersRouter = createRouter().query('list', {
|
|||||||
: data.length;
|
: data.length;
|
||||||
const paginatedData = data.slice(startRecordIndex, endRecordIndex);
|
const paginatedData = data.slice(startRecordIndex, endRecordIndex);
|
||||||
|
|
||||||
return {
|
return getOffersResponseMapper(
|
||||||
data: paginatedData,
|
paginatedData.map((offer) => dashboardOfferDtoMapper(offer)),
|
||||||
paging: {
|
{
|
||||||
currPage: input.offset,
|
currentPage: input.offset,
|
||||||
numOfItemsInPage: paginatedData.length,
|
numOfItems: paginatedData.length,
|
||||||
numOfPages: Math.ceil(data.length / input.limit),
|
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