[offers][feat] Add delete OfferProfile API

pull/358/head
BryannYeap 2 years ago
parent 77ad895098
commit d5edb6da60

@ -201,7 +201,7 @@ model OffersBackground {
educations OffersEducation[] // For extensibility in the future
profile OffersProfile @relation(fields: [offersProfileId], references: [id])
profile OffersProfile @relation(fields: [offersProfileId], references: [id], onDelete: Cascade)
offersProfileId String @unique
}
@ -211,7 +211,7 @@ model OffersSpecificYoe {
yoe Int
domain String
background OffersBackground @relation(fields: [backgroundId], references: [id])
background OffersBackground @relation(fields: [backgroundId], references: [id], onDelete: Cascade)
backgroundId String
}
@ -237,7 +237,7 @@ model OffersExperience {
monthlySalary OffersCurrency? @relation("ExperienceMonthlySalary", fields: [monthlySalaryId], references: [id])
monthlySalaryId String? @unique
background OffersBackground @relation(fields: [backgroundId], references: [id])
background OffersBackground @relation(fields: [backgroundId], references: [id], onDelete: Cascade)
backgroundId String
}
@ -270,12 +270,11 @@ model OffersEducation {
type String?
field String?
// Add more fields
school String?
startDate DateTime?
endDate DateTime?
background OffersBackground @relation(fields: [backgroundId], references: [id])
background OffersBackground @relation(fields: [backgroundId], references: [id], onDelete: Cascade)
backgroundId String
}
@ -289,14 +288,14 @@ model OffersReply {
replyingTo OffersReply? @relation("ReplyThread", fields: [replyingToId], references: [id])
replies OffersReply[] @relation("ReplyThread")
profile OffersProfile @relation(fields: [profileId], references: [id])
profile OffersProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
profileId String
}
model OffersOffer {
id String @id @default(cuid())
profile OffersProfile @relation(fields: [profileId], references: [id])
profile OffersProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
profileId String
company Company @relation(fields: [companyId], references: [id])
@ -309,10 +308,10 @@ model OffersOffer {
jobType JobType
OffersIntern OffersIntern? @relation(fields: [offersInternId], references: [id])
OffersIntern OffersIntern? @relation(fields: [offersInternId], references: [id], onDelete: Cascade)
offersInternId String? @unique
OffersFullTime OffersFullTime? @relation(fields: [offersFullTimeId], references: [id])
OffersFullTime OffersFullTime? @relation(fields: [offersFullTimeId], references: [id], onDelete: Cascade)
offersFullTimeId String? @unique
}
@ -323,7 +322,7 @@ model OffersIntern {
specialization String
internshipCycle String
startYear Int
monthlySalary OffersCurrency @relation(fields: [monthlySalaryId], references: [id])
monthlySalary OffersCurrency @relation(fields: [monthlySalaryId], references: [id], onDelete: Cascade)
monthlySalaryId String @unique
OffersOffer OffersOffer?
@ -334,13 +333,13 @@ model OffersFullTime {
title String
specialization String
level String
totalCompensation OffersCurrency @relation("OfferTotalCompensation", fields: [totalCompensationId], references: [id])
totalCompensation OffersCurrency @relation("OfferTotalCompensation", fields: [totalCompensationId], references: [id], onDelete: Cascade)
totalCompensationId String @unique
baseSalary OffersCurrency @relation("OfferBaseSalary", fields: [baseSalaryId], references: [id])
baseSalary OffersCurrency @relation("OfferBaseSalary", fields: [baseSalaryId], references: [id], onDelete: Cascade)
baseSalaryId String @unique
bonus OffersCurrency @relation("OfferBonus", fields: [bonusId], references: [id])
bonus OffersCurrency @relation("OfferBonus", fields: [bonusId], references: [id], onDelete: Cascade)
bonusId String @unique
stocks OffersCurrency @relation("OfferStocks", fields: [stocksId], references: [id])
stocks OffersCurrency @relation("OfferStocks", fields: [stocksId], references: [id], onDelete: Cascade)
stocksId String @unique
OffersOffer OffersOffer?

@ -14,7 +14,39 @@ function Test() {
},
]);
return <>{JSON.stringify(data.data)}</>;
const deleteMutation = trpc.useMutation(['offers.profile.delete']);
const handleDelete = (id: string) => {
deleteMutation.mutate({ id });
};
return (
<ul>
<li>
<b>{JSON.stringify(data.data?.paging)}</b>
</li>
<li>
<ul>
{data.data?.data.map((offer) => {
return (
<li key={offer.id}>
<button
className="text-danger-600"
type="button"
onClick={() => {
handleDelete(offer.profileId);
}}>
DELETE THIS PROFILE AND ALL ITS OFFERS
</button>
<div>{JSON.stringify(offer)}</div>
<br />
</li>
);
})}
</ul>
</li>
</ul>
);
}
export default Test;

@ -3,136 +3,142 @@ import React, { useState } from 'react';
import { trpc } from '~/utils/trpc';
function Test() {
// F const data = trpc.useQuery([
// 'offers.profile.',
// {
// limit: 3,
// location: 'Singapore, Singapore',
// offset: 0,
// yoeCategory: 0,
// },
// ]);
// F const data = trpc.useQuery([
// 'offers.profile.',
// {
// limit: 3,
// location: 'Singapore, Singapore',
// offset: 0,
// yoeCategory: 0,
// },
// ]);
const [createdData, setCreatedData] = useState("")
const [createdData, setCreatedData] = useState('');
const createMutation = trpc.useMutation(['offers.profile.create'], {
onError(error: any) {
alert(error)
const createMutation = trpc.useMutation(['offers.profile.create'], {
onError(error: any) {
alert(error);
},
onSuccess(data) {
setCreatedData(JSON.stringify(data));
},
});
const handleClick = () => {
createMutation.mutate({
background: {
educations: [
{
endDate: new Date('2018-09-30T07:58:54.000Z'),
field: 'Computer Science',
school: 'National University of Singapore',
startDate: new Date('2014-09-30T07:58:54.000Z'),
type: 'Bachelors',
},
],
experiences: [
{
companyId: 'cl93m87pl0000tx1ofbafqz6f',
durationInMonths: 24,
jobType: 'FULLTIME',
level: 'Junior',
// "monthlySalary": undefined,
specialization: 'Front End',
title: 'Software Engineer',
totalCompensation: {
currency: 'SGD',
value: 104100,
},
},
],
specificYoes: [
{
domain: 'Front End',
yoe: 2,
},
{
domain: 'Full Stack',
yoe: 2,
},
],
totalYoe: 4,
},
offers: [
{
comments: '',
companyId: 'cl93m87pl0000tx1ofbafqz6f',
job: {
base: {
currency: 'SGD',
value: 84000,
},
bonus: {
currency: 'SGD',
value: 20000,
},
level: 'Junior',
specialization: 'Front End',
stocks: {
currency: 'SGD',
value: 100,
},
title: 'Software Engineer',
totalCompensation: {
currency: 'SGD',
value: 104100,
},
},
jobType: 'FULLTIME',
location: 'Singapore, Singapore',
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
negotiationStrategy: 'Leveraged having multiple offers',
},
onSuccess(data) {
setCreatedData(JSON.stringify(data))
{
comments: '',
companyId: 'cl93m87pl0000tx1ofbafqz6f',
job: {
base: {
currency: 'SGD',
value: 84000,
},
bonus: {
currency: 'SGD',
value: 20000,
},
level: 'Junior',
specialization: 'Front End',
stocks: {
currency: 'SGD',
value: 100,
},
title: 'Software Engineer',
totalCompensation: {
currency: 'SGD',
value: 104100,
},
},
jobType: 'FULLTIME',
location: 'Singapore, Singapore',
monthYearReceived: new Date('2022-09-30T07:58:54.000Z'),
negotiationStrategy: 'Leveraged having multiple offers',
},
],
});
};
const handleClick = () => {
createMutation.mutate({
"background": {
"educations": [
{
"endDate": new Date("2018-09-30T07:58:54.000Z"),
"field": "Computer Science",
"school": "National University of Singapore",
"startDate": new Date("2014-09-30T07:58:54.000Z"),
"type": "Bachelors"
}
],
"experiences": [
{
"companyId": "cl92szctf0008i9nfxk54bhxn",
"durationInMonths": 24,
"jobType": "FULLTIME",
"level": "Junior",
// "monthlySalary": undefined,
"specialization": "Front End",
"title": "Software Engineer",
"totalCompensation": {
"currency": "SGD",
"value": 104100
}
}
],
"specificYoes": [
{
"domain": "Front End",
"yoe": 2
},
{
"domain": "Full Stack",
"yoe": 2
}
],
"totalYoe": 4
},
"offers": [
const profileId = 'cl92wiw30006vw3hg7dxa14fo'; // Remember to change this filed after testing deleting
const data = trpc.useQuery([
`offers.profile.listOne`,
{
"comments": "",
"companyId": "cl92szctf0008i9nfxk54bhxn",
"job": {
"base": {
"currency": "SGD",
"value": 84000
},
"bonus": {
"currency": "SGD",
"value": 20000
},
"level": "Junior",
"specialization": "Front End",
"stocks": {
"currency": "SGD",
"value": 100
},
"title": "Software Engineer",
"totalCompensation": {
"currency": "SGD",
"value": 104100
}
},
"jobType": "FULLTIME",
"location": "Singapore, Singapore",
"monthYearReceived": new Date("2022-09-30T07:58:54.000Z"),
"negotiationStrategy": "Leveraged having multiple offers"
profileId,
},
{
"comments": "",
"companyId": "cl92szctf0008i9nfxk54bhxn",
"job": {
"base": {
"currency": "SGD",
"value": 84000
},
"bonus": {
"currency": "SGD",
"value": 20000
},
"level": "Junior",
"specialization": "Front End",
"stocks": {
"currency": "SGD",
"value": 100
},
"title": "Software Engineer",
"totalCompensation": {
"currency": "SGD",
"value": 104100
}
},
"jobType": "FULLTIME",
"location": "Singapore, Singapore",
"monthYearReceived": new Date("2022-09-30T07:58:54.000Z"),
"negotiationStrategy": "Leveraged having multiple offers"
}
]
});
};
]);
const data = trpc.useQuery([
`offers.profile.listOne`,
{
profileId: "cl92wc64a004gw3hgq4pfln2m"
}
])
const deleteMutation = trpc.useMutation(['offers.profile.delete']);
const handleDelete = (id: string) => {
deleteMutation.mutate({ id });
};
return (
// <ul>
@ -140,14 +146,22 @@ function Test() {
// return <li key={x.id}>{JSON.stringify(x)}</li>;
// })}
// </ul>
<>
<div>
{createdData}
</div>
<button type="button" onClick={handleClick}>Click Me!</button>
<>
<div>{createdData}</div>
<button type="button" onClick={handleClick}>
Click Me!
</button>
<button
className="text-danger-600"
type="button"
onClick={() => {
handleDelete(profileId);
}}>
DELETE THIS PROFILE
</button>
<div>{JSON.stringify(data.data)}</div>
{/* <button type="button" onClick}>Get One</button> */}
{/* <button type="button" onClick}>Get One</button> */}
</>
);
}

@ -1,323 +1,353 @@
import crypto, { randomUUID } from "crypto";
import { z } from "zod";
import { Prisma } from "@prisma/client";
import crypto, { randomUUID } from 'crypto';
import { z } from 'zod';
import { Prisma } from '@prisma/client';
import { createRouter } from "./context";
import { createRouter } from './context';
const valuation = z.object({
currency: z.string(),
value: z.number(),
})
currency: z.string(),
value: z.number(),
});
// TODO: handle both full time and intern
const offer = z.object({
comments: z.string(),
companyId: z.string(),
job: z.object({
base: valuation.optional(), // Full time
bonus: valuation.optional(), // Full time
internshipCycle: z.string().optional(), // Intern
level: z.string().optional(), // Full time
monthlySalary: valuation.optional(), // Intern
specialization: z.string(),
startYear: z.number().optional(), // Intern
stocks: valuation.optional(), // Full time
title: z.string(),
totalCompensation: valuation.optional(), // Full time
}),
jobType: z.string(),
location: z.string(),
monthYearReceived: z.date(),
negotiationStrategy: z.string(),
})
comments: z.string(),
companyId: z.string(),
job: z.object({
base: valuation.optional(), // Full time
bonus: valuation.optional(), // Full time
internshipCycle: z.string().optional(), // Intern
level: z.string().optional(), // Full time
monthlySalary: valuation.optional(), // Intern
specialization: z.string(),
startYear: z.number().optional(), // Intern
stocks: valuation.optional(), // Full time
title: z.string(),
totalCompensation: valuation.optional(), // Full time
}),
jobType: z.string(),
location: z.string(),
monthYearReceived: z.date(),
negotiationStrategy: z.string(),
});
const experience = z.object({
companyId: z.string().optional(),
durationInMonths: z.number().optional(),
jobType: z.string().optional(),
level: z.string().optional(),
monthlySalary: valuation.optional(),
specialization: z.string().optional(),
title: z.string().optional(),
totalCompensation: valuation.optional(),
})
companyId: z.string().optional(),
durationInMonths: z.number().optional(),
jobType: z.string().optional(),
level: z.string().optional(),
monthlySalary: valuation.optional(),
specialization: z.string().optional(),
title: z.string().optional(),
totalCompensation: valuation.optional(),
});
const education = z.object({
endDate: z.date().optional(),
field: z.string().optional(),
school: z.string().optional(),
startDate: z.date().optional(),
type: z.string().optional(),
})
endDate: z.date().optional(),
field: z.string().optional(),
school: z.string().optional(),
startDate: z.date().optional(),
type: z.string().optional(),
});
export const offersProfileRouter = createRouter()
.query('listOne', {
input: z.object({
profileId: z.string(),
}),
async resolve({ ctx, input }) {
return await ctx.prisma.offersProfile.findFirst({
.query('listOne', {
input: z.object({
profileId: z.string(),
}),
async resolve({ ctx, input }) {
return await ctx.prisma.offersProfile.findFirst({
include: {
background: {
include: {
educations: true,
experiences: {
include: {
background: {
include: {
educations: true,
experiences: {
include: {
company: true,
monthlySalary: true,
totalCompensation: true
}
},
specificYoes: true
}
},
discussion: {
include: {
replies: true,
replyingTo: true
}
},
offers: {
include: {
OffersFullTime: {
include: {
baseSalary: true,
bonus: true,
stocks: true,
totalCompensation: true
}
},
OffersIntern: {
include: {
monthlySalary: true
}
},
company: true
}
}
company: true,
monthlySalary: true,
totalCompensation: true,
},
where: {
id: input.profileId
}
})
}
})
.mutation(
'create',
{
input: z.object({
background: z.object({
educations: z.array(education),
experiences: z.array(experience),
specificYoes: z.array(z.object({
domain: z.string(),
yoe: z.number(),
})),
totalYoe: z.number().optional(),
}),
offers: z.array(offer)
}),
async resolve({ ctx, input }) {
// TODO: add more
const token = crypto
.createHash("sha256")
.update(Date.now().toString())
.digest("hex");
},
specificYoes: true,
},
},
discussion: {
include: {
replies: true,
replyingTo: true,
},
},
offers: {
include: {
OffersFullTime: {
include: {
baseSalary: true,
bonus: true,
stocks: true,
totalCompensation: true,
},
},
OffersIntern: {
include: {
monthlySalary: true,
},
},
company: true,
},
},
},
where: {
id: input.profileId,
},
});
},
})
.mutation('create', {
input: z.object({
background: z.object({
educations: z.array(education),
experiences: z.array(experience),
specificYoes: z.array(
z.object({
domain: z.string(),
yoe: z.number(),
}),
),
totalYoe: z.number().optional(),
}),
offers: z.array(offer),
}),
async resolve({ ctx, input }) {
// TODO: add more
const token = crypto
.createHash('sha256')
.update(Date.now().toString())
.digest('hex');
const profile = await ctx.prisma.offersProfile.create({
data: {
background: {
const profile = await ctx.prisma.offersProfile.create({
data: {
background: {
create: {
educations: {
create: input.background.educations.map((x) => ({
endDate: x.endDate,
field: x.field,
school: x.school,
startDate: x.startDate,
type: x.type,
})),
},
experiences: {
create: input.background.experiences.map((x) => {
if (
x.jobType === 'FULLTIME' &&
x.totalCompensation?.currency !== undefined &&
x.totalCompensation.value !== undefined
) {
return {
company: {
connect: {
id: x.companyId,
},
},
durationInMonths: x.durationInMonths,
jobType: x.jobType,
level: x.level,
specialization: x.specialization,
title: x.title,
totalCompensation: {
create: {
educations: {
create:
input.background.educations.map((x) => ({
endDate: x.endDate,
field: x.field,
school: x.school,
startDate: x.startDate,
type: x.type
}))
},
experiences: {
create:
input.background.experiences.map((x) => {
if (x.jobType === "FULLTIME" && x.totalCompensation?.currency !== undefined && x.totalCompensation.value !== undefined) {
return {
company: {
connect: {
id: x.companyId
}
},
durationInMonths: x.durationInMonths,
jobType: x.jobType,
level: x.level,
specialization: x.specialization,
title: x.title,
totalCompensation: {
create: {
currency: x.totalCompensation?.currency,
value: x.totalCompensation?.value,
}
},
}
}
if (x.jobType === "INTERN" && x.monthlySalary?.currency !== undefined && x.monthlySalary.value !== undefined) {
return {
company: {
connect: {
id: x.companyId
}
},
durationInMonths: x.durationInMonths,
jobType: x.jobType,
monthlySalary: {
create: {
currency: x.monthlySalary?.currency,
value: x.monthlySalary?.value
}
},
specialization: x.specialization,
title: x.title,
}
}
throw Prisma.PrismaClientKnownRequestError
currency: x.totalCompensation?.currency,
value: x.totalCompensation?.value,
},
},
};
}
if (
x.jobType === 'INTERN' &&
x.monthlySalary?.currency !== undefined &&
x.monthlySalary.value !== undefined
) {
return {
company: {
connect: {
id: x.companyId,
},
},
durationInMonths: x.durationInMonths,
jobType: x.jobType,
monthlySalary: {
create: {
currency: x.monthlySalary?.currency,
value: x.monthlySalary?.value,
},
},
specialization: x.specialization,
title: x.title,
};
}
})
},
specificYoes: {
create:
input.background.specificYoes.map((x) => {
return {
domain: x.domain,
yoe: x.yoe
}
})
},
totalYoe: input.background.totalYoe,
}
throw Prisma.PrismaClientKnownRequestError;
}),
},
specificYoes: {
create: input.background.specificYoes.map((x) => {
return {
domain: x.domain,
yoe: x.yoe,
};
}),
},
totalYoe: input.background.totalYoe,
},
},
editToken: token,
offers: {
create: input.offers.map((x) => {
if (
x.jobType === 'INTERN' &&
x.job.internshipCycle !== undefined &&
x.job.monthlySalary?.currency !== undefined &&
x.job.monthlySalary.value !== undefined &&
x.job.startYear !== undefined
) {
return {
OffersIntern: {
create: {
internshipCycle: x.job.internshipCycle,
monthlySalary: {
create: {
currency: x.job.monthlySalary?.currency,
value: x.job.monthlySalary?.value,
},
},
specialization: x.job.specialization,
startYear: x.job.startYear,
title: x.job.title,
},
editToken: token,
offers: {
create:
input.offers.map((x) => {
if (x.jobType === "INTERN" && x.job.internshipCycle !== undefined && x.job.monthlySalary?.currency !== undefined && x.job.monthlySalary.value !== undefined && x.job.startYear !== undefined) {
return {
OffersIntern: {
create: {
internshipCycle: x.job.internshipCycle,
monthlySalary: {
create: {
currency: x.job.monthlySalary?.currency,
value: x.job.monthlySalary?.value
}
},
specialization: x.job.specialization,
startYear: x.job.startYear,
title: x.job.title,
}
},
comments: x.comments,
company: {
connect: {
id: x.companyId
}
},
jobType: x.jobType,
location: x.location,
monthYearReceived: x.monthYearReceived,
negotiationStrategy: x.negotiationStrategy
}
}
if (x.jobType === "FULLTIME" && x.job.base?.currency !== undefined && x.job.base?.value !== undefined && x.job.bonus?.currency !== undefined && x.job.bonus?.value !== undefined && x.job.stocks?.currency !== undefined && x.job.stocks?.value !== undefined && x.job.totalCompensation?.currency !== undefined && x.job.totalCompensation?.value !== undefined && x.job.level !== undefined) {
return {
OffersFullTime: {
create: {
baseSalary: {
create: {
currency: x.job.base?.currency,
value: x.job.base?.value
}
},
bonus: {
create: {
currency: x.job.bonus?.currency,
value: x.job.bonus?.value
}
},
level: x.job.level,
specialization: x.job.specialization,
stocks: {
create: {
currency: x.job.stocks?.currency,
value: x.job.stocks?.value,
}
},
title: x.job.title,
totalCompensation: {
create: {
currency: x.job.totalCompensation?.currency,
value: x.job.totalCompensation?.value,
}
},
}
},
comments: x.comments,
company: {
connect: {
id: x.companyId
}
},
jobType: x.jobType,
location: x.location,
monthYearReceived: x.monthYearReceived,
negotiationStrategy: x.negotiationStrategy
}
}
// Throw error
throw Prisma.PrismaClientKnownRequestError
})
},
comments: x.comments,
company: {
connect: {
id: x.companyId,
},
},
jobType: x.jobType,
location: x.location,
monthYearReceived: x.monthYearReceived,
negotiationStrategy: x.negotiationStrategy,
};
}
if (
x.jobType === 'FULLTIME' &&
x.job.base?.currency !== undefined &&
x.job.base?.value !== undefined &&
x.job.bonus?.currency !== undefined &&
x.job.bonus?.value !== undefined &&
x.job.stocks?.currency !== undefined &&
x.job.stocks?.value !== undefined &&
x.job.totalCompensation?.currency !== undefined &&
x.job.totalCompensation?.value !== undefined &&
x.job.level !== undefined
) {
return {
OffersFullTime: {
create: {
baseSalary: {
create: {
currency: x.job.base?.currency,
value: x.job.base?.value,
},
},
bonus: {
create: {
currency: x.job.bonus?.currency,
value: x.job.bonus?.value,
},
},
level: x.job.level,
specialization: x.job.specialization,
stocks: {
create: {
currency: x.job.stocks?.currency,
value: x.job.stocks?.value,
},
},
title: x.job.title,
totalCompensation: {
create: {
currency: x.job.totalCompensation?.currency,
value: x.job.totalCompensation?.value,
},
},
},
},
comments: x.comments,
company: {
connect: {
id: x.companyId,
},
profileName: randomUUID().substring(0,10),
},
jobType: x.jobType,
location: x.location,
monthYearReceived: x.monthYearReceived,
negotiationStrategy: x.negotiationStrategy,
};
}
// Throw error
throw Prisma.PrismaClientKnownRequestError;
}),
},
profileName: randomUUID().substring(0, 10),
},
include: {
background: {
include: {
educations: true,
experiences: {
include: {
company: true,
monthlySalary: true,
totalCompensation: true,
},
},
specificYoes: true,
},
},
offers: {
include: {
OffersFullTime: {
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
}
}
}
}
baseSalary: true,
bonus: true,
stocks: true,
totalCompensation: true,
},
});
},
OffersIntern: {
include: {
monthlySalary: true,
},
},
},
},
},
});
// TODO: add analysis to profile object then return
return profile
}
// TODO: add analysis to profile object then return
return profile;
},
})
.mutation('delete', {
input: z.object({
id: z.string(),
}),
async resolve({ ctx, input }) {
return await ctx.prisma.offersProfile.delete({
where: {
id: input.id,
},
});
},
);
});

@ -200,6 +200,7 @@ export const offersRouter = createRouter().query('list', {
},
});
// FILTERING
data = data.filter((offer) => {
let validRecord = true;
@ -235,6 +236,7 @@ export const offersRouter = createRouter().query('list', {
return validRecord;
});
// SORTING
data = data.sort((offer1, offer2) => {
const defaultReturn =
offer2.monthYearReceived.getTime() - offer1.monthYearReceived.getTime();

Loading…
Cancel
Save