From da2feab5c918f2e08aef288a0e26fa6b84928a62 Mon Sep 17 00:00:00 2001 From: soulsam480 <soulsam480@hotmail.com> Date: Sat, 10 Sep 2022 07:26:26 +0530 Subject: [PATCH] refactor: trpc v10 migration WIP --- api/package.json | 2 +- api/src/lib/auth.ts | 7 +- api/src/rpc/createRouter.ts | 9 - api/src/rpc/middlewares.ts | 36 +++ api/src/rpc/procedures.ts | 7 + api/src/rpc/routers/_appRouter.ts | 43 +--- api/src/rpc/routers/accounts.ts | 80 +++---- api/src/rpc/routers/auth.ts | 210 ++++++++---------- api/src/rpc/routers/batch.ts | 72 +++--- api/src/rpc/routers/course.ts | 83 +++---- api/src/rpc/routers/department.ts | 68 +++--- api/src/rpc/routers/institute.ts | 131 +++++------ api/src/rpc/routers/notifications.ts | 91 ++++---- api/src/rpc/routers/student/basics.ts | 54 +++-- api/src/rpc/routers/student/certifications.ts | 69 +++--- api/src/rpc/routers/student/education.ts | 77 +++---- api/src/rpc/routers/student/experience.ts | 75 +++---- api/src/rpc/routers/student/index.ts | 144 ++++++------ api/src/rpc/routers/student/project.ts | 75 +++---- api/src/rpc/routers/student/score.ts | 41 ++-- api/src/rpc/routers/ticket.ts | 107 +++++---- api/src/rpc/trpc.ts | 7 + app/package.json | 11 +- app/src/api/account.ts | 4 +- app/src/components/globals/AppLayout.tsx | 2 +- app/src/components/globals/DefaultLayout.tsx | 2 +- .../institute/student/GenerateUrlDialog.tsx | 2 +- .../student/profile/Basics/index.tsx | 6 +- app/src/contexts/WsClient.tsx | 2 +- app/src/contexts/currenctAccount.tsx | 5 +- app/src/contexts/student/basics.tsx | 6 +- app/src/contexts/student/certification.ts | 8 +- app/src/contexts/student/education.tsx | 10 +- app/src/contexts/student/experience.tsx | 8 +- app/src/contexts/student/index.ts | 6 +- app/src/contexts/student/projects.ts | 8 +- app/src/contexts/student/score.tsx | 4 +- app/src/contexts/student/skills.ts | 2 +- app/src/contexts/useBatch.ts | 30 ++- app/src/contexts/useCourse.ts | 30 ++- app/src/contexts/useDepartment.ts | 29 ++- app/src/contexts/useInstitute.ts | 16 +- app/src/contexts/useStudents.ts | 27 ++- app/src/contexts/useTicket.tsx | 33 ++- app/src/pages/_app.tsx | 55 +---- app/src/pages/login.tsx | 2 +- app/src/pages/reset-password.tsx | 2 +- app/src/types/index.ts | 37 ++- app/src/utils/hooks/index.ts | 3 +- app/src/utils/trpc.ts | 63 ++++-- app/tsconfig.json | 3 +- pnpm-lock.yaml | 200 ++++++++--------- 52 files changed, 980 insertions(+), 1124 deletions(-) delete mode 100644 api/src/rpc/createRouter.ts create mode 100644 api/src/rpc/middlewares.ts create mode 100644 api/src/rpc/procedures.ts create mode 100644 api/src/rpc/trpc.ts diff --git a/api/package.json b/api/package.json index d1ec42f..4e88f68 100644 --- a/api/package.json +++ b/api/package.json @@ -32,7 +32,7 @@ "@fastify/cors": "^7.0.0", "@fastify/websocket": "^5.0.0", "@swc/helpers": "^0.3.17", - "@trpc/server": "^9.27.1", + "@trpc/server": "10.0.0-proxy-alpha.73", "@typegoose/typegoose": "^9.11.0", "bull": "^4.8.5", "bullmq": "^1.89.1", diff --git a/api/src/lib/auth.ts b/api/src/lib/auth.ts index 9e835b1..c2bc76e 100644 --- a/api/src/lib/auth.ts +++ b/api/src/lib/auth.ts @@ -7,6 +7,7 @@ import * as bcrypt from 'bcryptjs' import { WithExcludeClient } from '../db' // don't make it an alias, the seed command will fail import { getEnv, isInstituteRole } from '../lib/helpers' +import { SessionUser } from '../rpc/context' export interface JwtPayload { id: number @@ -24,7 +25,11 @@ export async function hashPass(password: string) { export function prismaQueryHelper(client: WithExcludeClient) { return { - async getAccount(role: 'STUDENT' | 'INSTITUTE' | 'INSTITUTE_MOD' | 'ADMIN', email?: string, id?: number) { + async getAccount(session: SessionUser) { + const role = session?.user?.role + const email = session?.user?.email + const id = session?.user?.id + let account: any if (role === 'STUDENT') { diff --git a/api/src/rpc/createRouter.ts b/api/src/rpc/createRouter.ts deleted file mode 100644 index dc4423c..0000000 --- a/api/src/rpc/createRouter.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as trpc from '@trpc/server' -import { Context } from './context' - -/** - * Helper function to create a router with context - */ -export function createRouter() { - return trpc.router<Context>() -} diff --git a/api/src/rpc/middlewares.ts b/api/src/rpc/middlewares.ts new file mode 100644 index 0000000..0ae3e56 --- /dev/null +++ b/api/src/rpc/middlewares.ts @@ -0,0 +1,36 @@ +import { TRPCError } from '@trpc/server' +import { isRole } from '../lib' +import { trpc } from './trpc' + +export const sessionMiddleware = trpc.middleware(async ({ ctx, next }) => { + if (ctx.session === null) + throw new TRPCError({ + code: 'UNAUTHORIZED', + }) + + const nextCtx = await next({ + ctx: { session: ctx.session }, + }) + + return nextCtx +}) + +export const instituteMiddleware = trpc.middleware(async ({ ctx, next }) => { + if (ctx.session === null || !isRole(ctx.session.user.role).instituteOrMod) + throw new TRPCError({ code: 'UNAUTHORIZED' }) + + return await next({ + ctx: { session: ctx.session }, + }) +}) + +export const adminMiddleware = trpc.middleware(async ({ ctx, next }) => { + if (ctx.session?.user.role !== 'ADMIN') + throw new TRPCError({ + code: 'UNAUTHORIZED', + }) + + return await next({ + ctx: { session: ctx.session }, + }) +}) diff --git a/api/src/rpc/procedures.ts b/api/src/rpc/procedures.ts new file mode 100644 index 0000000..bf72750 --- /dev/null +++ b/api/src/rpc/procedures.ts @@ -0,0 +1,7 @@ +import { adminMiddleware, instituteMiddleware, sessionMiddleware } from './middlewares' +import { trpc } from './trpc' + +export const procedureWithSession = trpc.procedure.use(sessionMiddleware) +export const procedureWithInstitute = trpc.procedure.use(instituteMiddleware) +export const procedureWithStudent = trpc.procedure.use(sessionMiddleware) +export const procedureWithAdmin = trpc.procedure.use(sessionMiddleware).use(adminMiddleware) diff --git a/api/src/rpc/routers/_appRouter.ts b/api/src/rpc/routers/_appRouter.ts index 46ee73a..f25deb9 100644 --- a/api/src/rpc/routers/_appRouter.ts +++ b/api/src/rpc/routers/_appRouter.ts @@ -1,9 +1,5 @@ +import { trpc } from '../trpc' import { batchRouter } from './batch' -/** - * This file contains the root router of your tRPC-backend - */ -import superjson from 'superjson' -import { createRouter } from '../createRouter' import { accountRouter } from './accounts' import { authRouter } from './auth' import { courseRouter } from './course' @@ -13,31 +9,16 @@ import { studentRouter } from './student' import { ticketRouter } from './ticket' import { notificationRouter } from './notifications' -/** - * Create your application's root router - * If you want to use SSG, you need export this - * @link https://trpc.io/docs/ssg - * @link https://trpc.io/docs/router - */ -export const appRouter = createRouter() - /** - * Add data transformers - * @link https://trpc.io/docs/data-transformers - */ - .transformer(superjson) - /** - * Optionally do custom error (type safe!) formatting - * @link https://trpc.io/docs/error-formatting - */ - // .formatError(({ shape, error }) => { }) - .merge('account.', accountRouter) - .merge('auth.', authRouter) - .merge('institute.', instituteRouter) - .merge('department.', departmentRouter) - .merge('course.', courseRouter) - .merge('batch.', batchRouter) - .merge('student.', studentRouter) - .merge('ticket.', ticketRouter) - .merge('notification.', notificationRouter) +export const appRouter = trpc.router({ + account: accountRouter, + auth: authRouter, + institute: instituteRouter, + department: departmentRouter, + course: courseRouter, + batch: batchRouter, + student: studentRouter, + ticket: ticketRouter, + notification: notificationRouter, +}) export type AppRouter = typeof appRouter diff --git a/api/src/rpc/routers/accounts.ts b/api/src/rpc/routers/accounts.ts index 5cc734e..899e0a0 100644 --- a/api/src/rpc/routers/accounts.ts +++ b/api/src/rpc/routers/accounts.ts @@ -1,69 +1,43 @@ import { TRPCError } from '@trpc/server' -import { createRouter } from '../createRouter' +import { trpc } from '../trpc' import { z } from 'zod' import { createInstituteSchema, tourSchema } from '@mirai/app' - -export const accountRouter = createRouter() - .middleware(async ({ ctx, next }) => { - if (ctx.session === null) - throw new TRPCError({ - code: 'UNAUTHORIZED', - }) - - const nextCtx = await next({ - ctx: { ...ctx, session: ctx.session }, +import { procedureWithAdmin, procedureWithSession } from '../procedures' + +export const accountRouter = trpc.router({ + toggle_tour: procedureWithSession.input(tourSchema).mutation(async ({ ctx, input: { id, showTour } }) => { + await ctx.prisma.account.update({ + where: { id }, + data: { showTour }, + select: { id: true, showTour: true }, }) + }), - return nextCtx - }) - .mutation('toggle_tour', { - input: tourSchema, - async resolve({ ctx, input: { id, showTour } }) { - await ctx.prisma.account.update({ - where: { id }, - data: { showTour }, - select: { id: true, showTour: true }, - }) - }, - }) - .middleware(async ({ ctx, next }) => { - if (ctx.session?.user.role !== 'ADMIN') + create_institute: procedureWithAdmin.input(createInstituteSchema).mutation(async ({ ctx, input }) => { + const isDupId = await ctx.prisma.institute.findFirst({ where: { code: input.code }, select: { id: true } }) + + if (isDupId !== null) { throw new TRPCError({ - code: 'UNAUTHORIZED', + message: 'Duplicate Institute code', + code: 'BAD_REQUEST', }) + } - const nextCtx = await next({ - ctx: { ...ctx, session: ctx.session }, + const institute = await ctx.prisma.institute.create({ + data: input, }) - return nextCtx - }) - .mutation('create_institute', { - input: createInstituteSchema, - async resolve({ ctx, input }) { - const isDupId = await ctx.prisma.institute.findFirst({ where: { code: input.code }, select: { id: true } }) - - if (isDupId != null) { - throw new TRPCError({ - message: 'Duplicate Institute code', - code: 'BAD_REQUEST', - }) - } + return institute + }), - const institute = await ctx.prisma.institute.create({ - data: input, - }) - - return institute - }, - }) - .mutation('update_institute', { - input: createInstituteSchema.merge(z.object({ instituteId: z.number() })), - async resolve({ ctx, input }) { + update_institute: procedureWithAdmin + .input(createInstituteSchema.merge(z.object({ instituteId: z.number() }))) + .mutation(async ({ ctx, input }) => { const { instituteId, ...data } = input + await ctx.prisma.institute.update({ where: { id: instituteId }, data, }) - }, - }) + }), +}) diff --git a/api/src/rpc/routers/auth.ts b/api/src/rpc/routers/auth.ts index 692d1b4..2f68201 100644 --- a/api/src/rpc/routers/auth.ts +++ b/api/src/rpc/routers/auth.ts @@ -2,59 +2,61 @@ import { TRPCError } from '@trpc/server' import { nanoid } from 'nanoid' import { LoginSchema, signupSchema } from '@mirai/app' import { z } from 'zod' -import { createRouter } from '../createRouter' +import { trpc } from '../trpc' import { authToken, comparePassword, hashPass, prismaQueryHelper } from '../../lib' +import { sessionMiddleware } from '../middlewares' +import { procedureWithSession } from '../procedures' export const accountRole = ['STUDENT', 'INSTITUTE', 'INSTITUTE_MOD', 'ADMIN'] -export const authRouter = createRouter() - .mutation('sign_up', { - input: signupSchema, - async resolve({ ctx, input }) { - const { role, instituteId, studentId, ...rest } = input +export const authRouter = trpc.router({ + sign_up: trpc.procedure.input(signupSchema).mutation(async ({ ctx, input }) => { + const { role, instituteId, studentId, ...rest } = input - if (['INSTITUTE_MOD', 'INSTITUTE'].includes(role) && instituteId === undefined) - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'Missing institute Id for account', - }) - - if (role === 'STUDENT' && studentId === undefined) - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'Missing student Id for account', - }) + if (['INSTITUTE_MOD', 'INSTITUTE'].includes(role) && instituteId === undefined) + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'Missing institute Id for account', + }) - const relID = - role === 'ADMIN' - ? null - : ['INSTITUTE_MOD', 'INSTITUTE'].includes(role) - ? (instituteId as number) - : (studentId as number) - - const account = await ctx.prisma.account.create({ - data: { - ...rest, - accountToken: nanoid(), - role, - // TODO: check owner relation - instituteId: ['INSTITUTE_MOD', 'INSTITUTE'].includes(role) ? relID : null, - studentId: role === 'STUDENT' ? relID : null, - isOwner: role === 'INSTITUTE', - emailVerified: false, - }, + if (role === 'STUDENT' && studentId === undefined) + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'Missing student Id for account', }) - return { ...account, password: undefined } - }, - }) - .mutation('reset_password', { - input: z.object({ - password: z.string().min(1), - accountId: z.number(), - token: z.string().min(1), - }), - async resolve({ input, ctx }) { + const relID = + role === 'ADMIN' + ? null + : ['INSTITUTE_MOD', 'INSTITUTE'].includes(role) + ? (instituteId as number) + : (studentId as number) + + const account = await ctx.prisma.account.create({ + data: { + ...rest, + accountToken: nanoid(), + role, + // TODO: check owner relation + instituteId: ['INSTITUTE_MOD', 'INSTITUTE'].includes(role) ? relID : null, + studentId: role === 'STUDENT' ? relID : null, + isOwner: role === 'INSTITUTE', + emailVerified: false, + }, + }) + + return { ...account, password: undefined } + }), + + reset_password: trpc.procedure + .input( + z.object({ + password: z.string().min(1), + accountId: z.number(), + token: z.string().min(1), + }), + ) + .mutation(async ({ input, ctx }) => { const account = await ctx.prisma.account.findFirst({ where: { id: input.accountId }, select: { accountToken: true, id: true, instituteId: true }, @@ -84,75 +86,59 @@ export const authRouter = createRouter() return { status: 'success', } - }, - }) - .mutation('login', { - input: LoginSchema, - async resolve({ ctx, input: { email, password } }) { - const account = await ctx.prisma.account.findFirst({ - where: { email }, - }) + }), - if (account === null) - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'Account doeesn"t exist with the provided email', - }) + login: trpc.procedure.input(LoginSchema).mutation(async ({ ctx, input: { email, password } }) => { + const account = await ctx.prisma.account.findFirst({ + where: { email }, + }) - if (account.password === null) - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'Please add password to continue !', - }) + if (account === null) + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'Account doeesn"t exist with the provided email', + }) - const isSamePassword = await comparePassword(password, account.password) + if (account.password === null) + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'Please add password to continue !', + }) - if (isSamePassword) - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'Invalid email or password', - }) + const isSamePassword = await comparePassword(password, account.password) - return { - ...account, - password: undefined, - } - }, - }) - .middleware(async ({ ctx, next }) => { - if (ctx.session === null) + if (isSamePassword) throw new TRPCError({ - code: 'UNAUTHORIZED', + code: 'BAD_REQUEST', + message: 'Invalid email or password', }) - const nextCtx = await next({ - ctx: { ...ctx, session: ctx.session }, - }) + return { + ...account, + password: undefined, + } + }), - return nextCtx - }) - .query('account', { - async resolve({ ctx }) { - const account = await prismaQueryHelper(ctx.prisma).getAccount( - ctx.session.user.role, - undefined, - ctx.session.user.id, - ) - - if (account === null) - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'User account data not found', - }) + account: procedureWithSession.query(async ({ ctx }) => { + const account = await prismaQueryHelper(ctx.prisma).getAccount(ctx.session) - return account - }, - }) - .query('account_token', { - input: z.object({ - accountId: z.number(), - }), - async resolve({ ctx, input }) { + if (account === null) + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'User account data not found', + }) + + return account + }), + + account_token: trpc.procedure + .use(sessionMiddleware) + .input( + z.object({ + accountId: z.number(), + }), + ) + .query(async ({ ctx, input }) => { const accountData = await ctx.prisma.account.findFirst({ where: { id: input.accountId }, select: { id: true, name: true }, @@ -171,18 +157,18 @@ export const authRouter = createRouter() return { token, } - }, - }) - .query('auth_token', { - input: z.object({ - user: z.any(), }), - async resolve({ input }) { + + auth_token: trpc.procedure + .use(sessionMiddleware) + .input(z.object({ user: z.any() })) + .query(async ({ input }) => { const token = authToken.encode(input as any) return token - }, - }) + }), +}) + // TODO: add otp login setup // .mutation('login', { // input: z.object({ diff --git a/api/src/rpc/routers/batch.ts b/api/src/rpc/routers/batch.ts index a28fefc..08b237d 100644 --- a/api/src/rpc/routers/batch.ts +++ b/api/src/rpc/routers/batch.ts @@ -1,57 +1,45 @@ import { TRPCError } from '@trpc/server' import { createBatchSchema } from '@mirai/app' -import { isInstituteRole } from '../../lib' -import { createRouter } from '../createRouter' +import { trpc } from '../trpc' import { z } from 'zod' +import { procedureWithInstitute } from '../procedures' -export const batchRouter = createRouter() - .middleware(async ({ ctx, next }) => { - if (ctx.session == null || !isInstituteRole(ctx.session.user.role).is) throw new TRPCError({ code: 'UNAUTHORIZED' }) - - const newCtx = await next({ - // might seem dumb, but it's done like this to keep TS happy - ctx: { ...ctx, user: ctx.session }, +export const batchRouter = trpc.router({ + create: procedureWithInstitute.input(createBatchSchema).mutation(async ({ ctx, input }) => { + const batch = await ctx.prisma.batch.create({ + data: input, }) - return newCtx - }) - .mutation('create', { - input: createBatchSchema, - async resolve({ ctx, input }) { - const batch = await ctx.prisma.batch.create({ - data: input, - }) + return batch + }), - return batch - }, - }) - .mutation('update', { - input: createBatchSchema.extend({ id: z.number() }), - async resolve({ ctx, input }) { + update: procedureWithInstitute + .input(createBatchSchema.extend({ id: z.number() })) + .mutation(async ({ ctx, input }) => { const { id, ...data } = input await ctx.prisma.batch.update({ where: { id }, data, }) - }, - }) - .query('getAll', { - input: z.number(), - async resolve({ ctx, input }) { - const batches = await ctx.prisma.batch.findMany({ - where: { instituteId: input }, - }) - - return batches - }, - }) - .query('get', { - input: z.object({ - instituteId: z.number(), - batchId: z.number(), }), - async resolve({ ctx, input }) { + + getAll: procedureWithInstitute.input(z.number()).query(async ({ ctx, input }) => { + const batches = await ctx.prisma.batch.findMany({ + where: { instituteId: input }, + }) + + return batches + }), + + get: procedureWithInstitute + .input( + z.object({ + instituteId: z.number(), + batchId: z.number(), + }), + ) + .query(async ({ ctx, input }) => { const batch = await ctx.prisma.batch.findFirst({ where: { id: input.batchId, @@ -67,5 +55,5 @@ export const batchRouter = createRouter() } return batch - }, - }) + }), +}) diff --git a/api/src/rpc/routers/course.ts b/api/src/rpc/routers/course.ts index 7676ee5..9d884a9 100644 --- a/api/src/rpc/routers/course.ts +++ b/api/src/rpc/routers/course.ts @@ -1,63 +1,52 @@ import { TRPCError } from '@trpc/server' import { createCourseSchema } from '@mirai/app' -import { createRouter } from '../createRouter' -import { isInstituteRole } from '../../lib' +import { trpc } from '../trpc' import { z } from 'zod' +import { procedureWithInstitute } from '../procedures' -export const courseRouter = createRouter() - .middleware(async ({ ctx, next }) => { - if (ctx.session === null || !isInstituteRole(ctx.session.user.role).is) - throw new TRPCError({ code: 'UNAUTHORIZED' }) - - return await next({ - // might seem dumb, but it's done like this to keep TS happy - ctx: { ...ctx, user: ctx.session }, +export const courseRouter = trpc.router({ + create: procedureWithInstitute.input(createCourseSchema).mutation(async ({ ctx, input }) => { + const program = await ctx.prisma.course.create({ + data: input, }) - }) - .mutation('create', { - input: createCourseSchema, - async resolve({ ctx, input }) { - const program = await ctx.prisma.course.create({ - data: input, - }) - return program - }, - }) - .mutation('update', { - input: createCourseSchema.partial().extend({ id: z.number() }).omit({ instituteId: true }), - async resolve({ ctx, input }) { + return program + }), + + update: procedureWithInstitute + .input(createCourseSchema.partial().extend({ id: z.number() }).omit({ instituteId: true })) + .mutation(async ({ ctx, input }) => { const { id, ...data } = input await ctx.prisma.course.update({ where: { id }, data, }) - }, - }) - .query('getAll', { - input: z.number(), - async resolve({ ctx, input }) { - const courses = await ctx.prisma.course.findMany({ - where: { instituteId: input }, - include: { - department: { - select: { - name: true, - }, + }), + + getAll: procedureWithInstitute.input(z.number()).query(async ({ ctx, input }) => { + const courses = await ctx.prisma.course.findMany({ + where: { instituteId: input }, + include: { + department: { + select: { + name: true, }, }, - }) + }, + }) - return courses - }, - }) - .query('get', { - input: z.object({ - instituteId: z.number(), - courseId: z.number(), - }), - async resolve({ ctx, input: { courseId, instituteId } }) { + return courses + }), + + get: procedureWithInstitute + .input( + z.object({ + instituteId: z.number(), + courseId: z.number(), + }), + ) + .query(async ({ ctx, input: { courseId, instituteId } }) => { const course = await ctx.prisma.course.findFirst({ where: { id: courseId, instituteId }, }) @@ -70,5 +59,5 @@ export const courseRouter = createRouter() } return course - }, - }) + }), +}) diff --git a/api/src/rpc/routers/department.ts b/api/src/rpc/routers/department.ts index a72b412..f2c815a 100644 --- a/api/src/rpc/routers/department.ts +++ b/api/src/rpc/routers/department.ts @@ -1,53 +1,43 @@ import { TRPCError } from '@trpc/server' -import { createRouter } from '../createRouter' +import { trpc } from '../trpc' import { createDepartmentSchema } from '@mirai/app' import { z } from 'zod' -import { isInstituteRole } from '../../lib' +import { procedureWithInstitute } from '../procedures' -export const departmentRouter = createRouter() - .middleware(async ({ ctx, next }) => { - if (ctx.session == null || !isInstituteRole(ctx.session.user.role).is) throw new TRPCError({ code: 'UNAUTHORIZED' }) - - return await next({ - // might seem dumb, but it's done like this to keep TS happy - ctx: { ...ctx, user: ctx.session }, +export const departmentRouter = trpc.router({ + create: procedureWithInstitute.input(createDepartmentSchema).mutation(async ({ ctx, input }) => { + const department = await ctx.prisma.department.create({ + data: input, }) - }) - .mutation('create', { - input: createDepartmentSchema, - async resolve({ ctx, input }) { - const department = await ctx.prisma.department.create({ - data: input, - }) - return department - }, - }) - .mutation('update', { - input: createDepartmentSchema.extend({ id: z.number() }), - async resolve({ ctx, input }) { + return department + }), + + update: procedureWithInstitute + .input(createDepartmentSchema.extend({ id: z.number() })) + .mutation(async ({ ctx, input }) => { const { id, ...data } = input await ctx.prisma.department.update({ where: { id }, data, }) - }, - }) - .query('getAll', { - input: z.number(), - async resolve({ ctx, input }) { - const departments = await ctx.prisma.department.findMany({ where: { instituteId: input } }) - - return departments - }, - }) - .query('get', { - input: z.object({ - instituteId: z.number(), - departmentId: z.number(), }), - async resolve({ ctx, input }) { + + getAll: procedureWithInstitute.input(z.number()).query(async ({ ctx, input }) => { + const departments = await ctx.prisma.department.findMany({ where: { instituteId: input } }) + + return departments + }), + + get: procedureWithInstitute + .input( + z.object({ + instituteId: z.number(), + departmentId: z.number(), + }), + ) + .query(async ({ ctx, input }) => { const department = await ctx.prisma.department.findFirst({ where: { id: input.departmentId, instituteId: input.instituteId }, }) @@ -60,5 +50,5 @@ export const departmentRouter = createRouter() } return department - }, - }) + }), +}) diff --git a/api/src/rpc/routers/institute.ts b/api/src/rpc/routers/institute.ts index 3c1f04e..d69ef4d 100644 --- a/api/src/rpc/routers/institute.ts +++ b/api/src/rpc/routers/institute.ts @@ -1,93 +1,78 @@ import { TRPCError } from '@trpc/server' -import { createRouter } from '../createRouter' +import { trpc } from '../trpc' import { z } from 'zod' import { generateOnboardingUrlSchema, studentsQuerySchema } from '@mirai/app' import { onBoardingTokens } from '../../lib' import dayjs from 'dayjs' +import { procedureWithSession } from '../procedures' -export const instituteRouter = createRouter() - .middleware(async ({ ctx, next }) => { - if (ctx.session === null) throw new TRPCError({ code: 'UNAUTHORIZED' }) - - const nextCtx = await next({ - // might seem dumb, but it's done like this to keep TS happy - ctx: { ...ctx, session: ctx.session }, +export const instituteRouter = trpc.router({ + get: procedureWithSession.input(z.number()).query(async ({ ctx, input }) => { + const instituteData = await ctx.prisma.institute.findFirst({ + where: { id: input }, + include: { + account: { + select: ctx.prisma.$exclude('account', ['password', 'otp', 'otpExpiry', 'emailToken']), + }, + }, }) - return nextCtx - }) - .query('get', { - input: z.number(), - resolve: async ({ ctx, input }) => { - const instituteData = await ctx.prisma.institute.findFirst({ - where: { id: input }, - include: { - account: { - select: ctx.prisma.$exclude('account', ['password', 'otp', 'otpExpiry', 'emailToken']), - }, - }, + if (instituteData == null) + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'Institute not found !', }) - if (instituteData == null) - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'Institute not found !', - }) + return instituteData + }), - return instituteData - }, - }) // TODO: paginate - .query('get_all_students', { - input: studentsQuerySchema, - async resolve({ ctx, input }) { - const { name, uniId, ...rest } = input + get_all_students: procedureWithSession.input(studentsQuerySchema).query(async ({ ctx, input }) => { + const { name, uniId, ...rest } = input - const students = await ctx.prisma.student.findMany({ - where: { - ...rest, - // we need to conditionally construct queries because with an - // empty string, prisma advanced queries won't work - uniId: uniId !== undefined ? { contains: uniId } : undefined, - basics: name !== undefined ? { name: { startsWith: name, mode: 'insensitive' } } : undefined, - }, - select: { - instituteId: true, - uniId: true, - Batch: { select: { name: true, id: true } }, - Department: { select: { name: true, id: true } }, - basics: true, - course: { select: { programName: true, id: true } }, - id: true, - code: true, - }, - }) + const students = await ctx.prisma.student.findMany({ + where: { + ...rest, + // we need to conditionally construct queries because with an + // empty string, prisma advanced queries won't work + uniId: uniId !== undefined ? { contains: uniId } : undefined, + basics: name !== undefined ? { name: { startsWith: name, mode: 'insensitive' } } : undefined, + }, + select: { + instituteId: true, + uniId: true, + Batch: { select: { name: true, id: true } }, + Department: { select: { name: true, id: true } }, + basics: true, + course: { select: { programName: true, id: true } }, + id: true, + code: true, + }, + }) - return students - }, - }) - .mutation('gen_onboarding_token', { - input: generateOnboardingUrlSchema, - async resolve({ input }) { - const token = onBoardingTokens.encode({ - ...input, - createdAt: dayjs().toISOString(), - }) + return students + }), + + gen_onboarding_token: procedureWithSession.input(generateOnboardingUrlSchema).mutation(async ({ input }) => { + const token = onBoardingTokens.encode({ + ...input, + createdAt: dayjs().toISOString(), + }) + + return token + }), - return token - }, - }) - .middleware(async ({ ctx, next }) => { - if (ctx.session.user.role !== 'ADMIN') throw new TRPCError({ code: 'UNAUTHORIZED' }) + get_all: procedureWithSession + .use(async ({ ctx, next }) => { + if (ctx.session.user.role !== 'ADMIN') throw new TRPCError({ code: 'UNAUTHORIZED' }) - const nextCtx = await next({ ctx }) + const nextCtx = await next({ ctx }) - return nextCtx - }) - .query('get_all', { - async resolve({ ctx }) { + return nextCtx + }) + .query(async ({ ctx }) => { const institutes = await ctx.prisma.institute.findMany() return institutes - }, - }) + }), +}) diff --git a/api/src/rpc/routers/notifications.ts b/api/src/rpc/routers/notifications.ts index 60bb690..30d8612 100644 --- a/api/src/rpc/routers/notifications.ts +++ b/api/src/rpc/routers/notifications.ts @@ -1,48 +1,51 @@ import { HydratedDocument } from 'mongoose' import { z } from 'zod' import { notification, Notification } from '../../entities' -import { createRouter } from '../createRouter' - -export const notificationRouter = createRouter().query('get_all', { - input: z.object({ - ts: z.number().optional(), - }), - async resolve({ input: { ts } }) { - let data - - if (ts !== undefined) { - const decrypedDate = new Date(ts * 1000) - - data = await notification - .find({ - createdAt: { - $lt: new Date(decrypedDate), - }, - }) - .populate('data') - .sort({ createdAt: -1 }) - .limit(6) - } else { - data = await notification.find().populate('data').sort({ createdAt: -1 }).limit(6) - } - - const hasMore = data.length === 6 - let nextCursor = null - - if (hasMore) { - const nextCursorRecord = data[5] - - const unixTimestamp = Math.floor((nextCursorRecord?.createdAt?.getTime() ?? 0) / 1000) - - nextCursor = unixTimestamp.toString() - - data.pop() - } - - return { - data: data as Array<HydratedDocument<Notification>>, - hasMore, - nextCursor, - } - }, +import { trpc } from '../trpc' + +export const notificationRouter = trpc.router({ + get_all: trpc.procedure + .input( + z.object({ + ts: z.number().optional(), + }), + ) + .query(async ({ input: { ts } }) => { + let data + + if (ts !== undefined) { + const decrypedDate = new Date(ts * 1000) + + data = await notification + .find({ + createdAt: { + $lt: new Date(decrypedDate), + }, + }) + .populate('data') + .sort({ createdAt: -1 }) + .limit(6) + } else { + data = await notification.find().populate('data').sort({ createdAt: -1 }).limit(6) + } + + const hasMore = data.length === 6 + let nextCursor = null + + if (hasMore) { + const nextCursorRecord = data[5] + + const unixTimestamp = Math.floor((nextCursorRecord?.createdAt?.getTime() ?? 0) / 1000) + + nextCursor = unixTimestamp.toString() + + data.pop() + } + + return { + data: data as Array<HydratedDocument<Notification>>, + hasMore, + nextCursor, + } + }), }) diff --git a/api/src/rpc/routers/student/basics.ts b/api/src/rpc/routers/student/basics.ts index 82ac272..133e402 100644 --- a/api/src/rpc/routers/student/basics.ts +++ b/api/src/rpc/routers/student/basics.ts @@ -1,36 +1,34 @@ -import { createRouter } from '../../createRouter' +import { trpc } from '../../trpc' import { z } from 'zod' import { createStudentBasicsSchema } from '@mirai/app' import { TRPCError } from '@trpc/server' +import { procedureWithStudent } from '../../procedures' -export const basicsRouter = createRouter() - .query('get', { - input: z.number(), - async resolve({ ctx, input }) { - const studentBasics = await ctx.prisma.studentBasics.findFirst({ - where: { studentId: input }, +export const basicsRouter = trpc.router({ + get: procedureWithStudent.input(z.number()).query(async ({ ctx, input }) => { + const studentBasics = await ctx.prisma.studentBasics.findFirst({ + where: { studentId: input }, + }) + + if (studentBasics === null) { + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'Student basics not found !', }) + } - if (studentBasics === null) { - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'Student basics not found !', - }) - } + return studentBasics + }), - return studentBasics - }, - }) - .mutation('manage', { - input: createStudentBasicsSchema, - async resolve({ ctx, input }) { - const { studentId, ...data } = input - const result = await ctx.prisma.studentBasics.upsert({ - where: { studentId }, - create: input, - update: data, - }) + manage: procedureWithStudent.input(createStudentBasicsSchema).mutation(async ({ ctx, input }) => { + const { studentId, ...data } = input + + const result = await ctx.prisma.studentBasics.upsert({ + where: { studentId }, + create: input, + update: data, + }) - return result - }, - }) + return result + }), +}) diff --git a/api/src/rpc/routers/student/certifications.ts b/api/src/rpc/routers/student/certifications.ts index e59b728..92aa549 100644 --- a/api/src/rpc/routers/student/certifications.ts +++ b/api/src/rpc/routers/student/certifications.ts @@ -1,24 +1,23 @@ import { createCertificationSchema } from '@mirai/app' import { TRPCError } from '@trpc/server' import { z } from 'zod' -import { createRouter } from '../../createRouter' +import { procedureWithStudent } from '../../procedures' +import { trpc } from '../../trpc' -export const certificationRouter = createRouter() - .mutation('create', { - input: createCertificationSchema, - async resolve({ ctx, input }) { - const certificationData = await ctx.prisma.studentCertification.create({ - data: { - ...input, - }, - }) +export const certificationRouter = trpc.router({ + create: procedureWithStudent.input(createCertificationSchema).mutation(async ({ ctx, input }) => { + const certificationData = await ctx.prisma.studentCertification.create({ + data: { + ...input, + }, + }) - return certificationData - }, - }) - .mutation('update', { - input: createCertificationSchema.omit({ studentId: true }).partial().extend({ id: z.number() }), - async resolve({ ctx, input }) { + return certificationData + }), + + update: procedureWithStudent + .input(createCertificationSchema.omit({ studentId: true }).partial().extend({ id: z.number() })) + .mutation(async ({ ctx, input }) => { const { id, ...data } = input const certificationData = await ctx.prisma.studentCertification.update({ @@ -29,25 +28,21 @@ export const certificationRouter = createRouter() }) return certificationData - }, - }) - .mutation('remove', { - input: z.number(), - async resolve({ ctx, input }) { - try { - await ctx.prisma.studentCertification.delete({ where: { id: input } }) - } catch (error) { - throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unable to delete certification' }) - } - }, - }) - .query('get_all', { - input: z.number(), - async resolve({ ctx, input }) { - const certifications = await ctx.prisma.studentCertification.findMany({ - where: { studentId: input }, - }) + }), + + remove: procedureWithStudent.input(z.number()).mutation(async ({ ctx, input }) => { + try { + await ctx.prisma.studentCertification.delete({ where: { id: input } }) + } catch (error) { + throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unable to delete certification' }) + } + }), + + get_all: procedureWithStudent.input(z.number()).query(async ({ ctx, input }) => { + const certifications = await ctx.prisma.studentCertification.findMany({ + where: { studentId: input }, + }) - return certifications - }, - }) + return certifications + }), +}) diff --git a/api/src/rpc/routers/student/education.ts b/api/src/rpc/routers/student/education.ts index 400ff33..dcfd929 100644 --- a/api/src/rpc/routers/student/education.ts +++ b/api/src/rpc/routers/student/education.ts @@ -1,22 +1,21 @@ import { createStudentEducationSchema } from '@mirai/app' import { TRPCError } from '@trpc/server' import { z } from 'zod' -import { createRouter } from '../../createRouter' +import { procedureWithStudent } from '../../procedures' +import { trpc } from '../../trpc' -export const educationRouter = createRouter() - .mutation('create', { - input: createStudentEducationSchema, - async resolve({ ctx, input }) { - const result = await ctx.prisma.studentEducation.createMany({ - data: input, - }) +export const educationRouter = trpc.router({ + create: procedureWithStudent.input(createStudentEducationSchema).mutation(async ({ ctx, input }) => { + const result = await ctx.prisma.studentEducation.createMany({ + data: input, + }) - return result - }, - }) - .mutation('update', { - input: createStudentEducationSchema.omit({ studentId: true }).partial().extend({ id: z.number() }), - async resolve({ ctx, input }) { + return result + }), + + update: procedureWithStudent + .input(createStudentEducationSchema.omit({ studentId: true }).partial().extend({ id: z.number() })) + .mutation(async ({ ctx, input }) => { const { id, ...data } = input const result = await ctx.prisma.studentEducation.update({ where: { id }, @@ -24,31 +23,27 @@ export const educationRouter = createRouter() }) return result - }, - }) - .mutation('remove', { - input: z.number(), - async resolve({ ctx, input }) { - try { - await ctx.prisma.studentEducation.delete({ where: { id: input } }) - } catch (error) { - throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unable to delete experience' }) - } - }, - }) - .query('get_all', { - input: z.number(), - async resolve({ ctx, input }) { - const educationDetails = await ctx.prisma.studentEducation.findMany({ - where: { studentId: input }, - }) + }), - if (educationDetails === null) { - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'Student education details not found !', - }) - } - return educationDetails - }, - }) + remove: procedureWithStudent.input(z.number()).mutation(async ({ ctx, input }) => { + try { + await ctx.prisma.studentEducation.delete({ where: { id: input } }) + } catch (error) { + throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unable to delete experience' }) + } + }), + + get_all: procedureWithStudent.input(z.number()).query(async ({ ctx, input }) => { + const educationDetails = await ctx.prisma.studentEducation.findMany({ + where: { studentId: input }, + }) + + if (educationDetails === null) { + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'Student education details not found !', + }) + } + return educationDetails + }), +}) diff --git a/api/src/rpc/routers/student/experience.ts b/api/src/rpc/routers/student/experience.ts index 811d826..15944a5 100644 --- a/api/src/rpc/routers/student/experience.ts +++ b/api/src/rpc/routers/student/experience.ts @@ -1,24 +1,23 @@ import { createExperienceSchema } from '@mirai/app' import { TRPCError } from '@trpc/server' import { z } from 'zod' -import { createRouter } from '../../createRouter' +import { procedureWithStudent } from '../../procedures' +import { trpc } from '../../trpc' -export const experienceRouter = createRouter() - .mutation('create', { - input: createExperienceSchema, - async resolve({ ctx, input }) { - const experienceData = await ctx.prisma.studentWorkExperience.create({ - data: { - ...input, - }, - }) +export const experienceRouter = trpc.router({ + create: procedureWithStudent.input(createExperienceSchema).mutation(async ({ ctx, input }) => { + const experienceData = await ctx.prisma.studentWorkExperience.create({ + data: { + ...input, + }, + }) - return experienceData - }, - }) - .mutation('update', { - input: createExperienceSchema.omit({ studentId: true }).partial().extend({ id: z.number() }), - async resolve({ ctx, input }) { + return experienceData + }), + + update: procedureWithStudent + .input(createExperienceSchema.omit({ studentId: true }).partial().extend({ id: z.number() })) + .mutation(async ({ ctx, input }) => { const { id, ...data } = input const experienceData = await ctx.prisma.studentWorkExperience.update({ @@ -29,28 +28,24 @@ export const experienceRouter = createRouter() }) return experienceData - }, - }) - .mutation('remove', { - input: z.number(), - async resolve({ ctx, input }) { - try { - await ctx.prisma.studentWorkExperience.delete({ where: { id: input } }) - } catch (error) { - throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unable to delete experience' }) - } - }, - }) - .query('get_all', { - input: z.number(), - async resolve({ ctx, input }) { - const experiences = await ctx.prisma.studentWorkExperience.findMany({ - where: { studentId: input }, - orderBy: { - startedAt: 'desc', - }, - }) + }), + + remove: procedureWithStudent.input(z.number()).mutation(async ({ ctx, input }) => { + try { + await ctx.prisma.studentWorkExperience.delete({ where: { id: input } }) + } catch (error) { + throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unable to delete experience' }) + } + }), + + get_all: procedureWithStudent.input(z.number()).query(async ({ ctx, input }) => { + const experiences = await ctx.prisma.studentWorkExperience.findMany({ + where: { studentId: input }, + orderBy: { + startedAt: 'desc', + }, + }) - return experiences - }, - }) + return experiences + }), +}) diff --git a/api/src/rpc/routers/student/index.ts b/api/src/rpc/routers/student/index.ts index b5e820b..89d0450 100644 --- a/api/src/rpc/routers/student/index.ts +++ b/api/src/rpc/routers/student/index.ts @@ -1,95 +1,81 @@ -import { basicsRouter } from './basics' -import { TRPCError } from '@trpc/server' import { z } from 'zod' -import { createRouter } from '../../createRouter' +import { basicsRouter } from './basics' +import { trpc } from '../../trpc' import { certificationRouter } from './certifications' import { experienceRouter } from './experience' import { scoreRouter } from './score' import { educationRouter } from './education' import { projectRouter } from './project' +import { procedureWithStudent } from '../../procedures' -export const studentRouter = createRouter() - // TODO: add logic for student-student queries, where a student shouldn't be - // able to query another - .middleware(async ({ ctx, next }) => { - if (ctx.session === null) - throw new TRPCError({ - code: 'UNAUTHORIZED', - }) - - const nextCtx = await next({ - ctx: { ...ctx, user: ctx.session }, - }) - - return nextCtx - }) - - .query('get', { - input: z.number(), - async resolve({ ctx, input }) { - const studentData = await ctx.prisma.student.findFirst({ - where: { id: input }, - include: { - basics: true, - certifications: true, - education: true, - experience: { - orderBy: { - startedAt: 'desc', - }, +export const studentRouter = trpc.router({ + get: procedureWithStudent.input(z.number()).query(async ({ ctx, input }) => { + const studentData = await ctx.prisma.student.findFirst({ + where: { id: input }, + include: { + basics: true, + certifications: true, + education: true, + experience: { + orderBy: { + startedAt: 'desc', }, - projects: true, - score: true, - course: { - select: { - branchName: true, - programName: true, - programDuration: true, - scoreType: true, - }, + }, + projects: true, + score: true, + course: { + select: { + branchName: true, + programName: true, + programDuration: true, + scoreType: true, }, - Batch: { - select: { - name: true, - startsAt: true, - endsAt: true, - }, + }, + Batch: { + select: { + name: true, + startsAt: true, + endsAt: true, }, - Department: { - select: { - name: true, - }, + }, + Department: { + select: { + name: true, }, - institute: { - select: { - name: true, - }, + }, + institute: { + select: { + name: true, }, }, - }) + }, + }) - return studentData - }, - }) - .merge('basics.', basicsRouter) - .merge('score.', scoreRouter) - .merge('education.', educationRouter) - .merge('experience.', experienceRouter) - .merge('project.', projectRouter) - .merge('certification.', certificationRouter) + return studentData + }), - .mutation('skills.update', { - input: z.object({ - studentId: z.number(), - skills: z.array(z.any()), - }), - async resolve({ ctx, input: { skills, studentId } }) { - const skillData = await ctx.prisma.student.update({ - where: { id: studentId }, - data: { skills }, - select: { skills: true }, - }) + basics: basicsRouter, + score: scoreRouter, + education: educationRouter, + experience: experienceRouter, + project: projectRouter, + certification: certificationRouter, + skills: trpc.router({ + update: procedureWithStudent + .input( + z.object({ + studentId: z.number(), + skills: z.array(z.any()), + }), + ) + .mutation(async ({ ctx, input: { skills, studentId } }) => { + const skillData = await ctx.prisma.student.update({ + where: { id: studentId }, + data: { skills }, + select: { skills: true }, + }) - return skillData - }, - }) + return skillData + }), + }), +}) diff --git a/api/src/rpc/routers/student/project.ts b/api/src/rpc/routers/student/project.ts index 566de71..b3e8f6c 100644 --- a/api/src/rpc/routers/student/project.ts +++ b/api/src/rpc/routers/student/project.ts @@ -1,24 +1,23 @@ import { createProjectSchema } from '@mirai/app' import { TRPCError } from '@trpc/server' import { z } from 'zod' -import { createRouter } from '../../createRouter' +import { procedureWithStudent } from '../../procedures' +import { trpc } from '../../trpc' -export const projectRouter = createRouter() - .mutation('create', { - input: createProjectSchema, - async resolve({ ctx, input }) { - const projectData = await ctx.prisma.studentProject.create({ - data: { - ...input, - }, - }) +export const projectRouter = trpc.router({ + create: procedureWithStudent.input(createProjectSchema).mutation(async ({ ctx, input }) => { + const projectData = await ctx.prisma.studentProject.create({ + data: { + ...input, + }, + }) - return projectData - }, - }) - .mutation('update', { - input: createProjectSchema.omit({ studentId: true }).partial().extend({ id: z.number() }), - async resolve({ ctx, input }) { + return projectData + }), + + update: procedureWithStudent + .input(createProjectSchema.omit({ studentId: true }).partial().extend({ id: z.number() })) + .mutation(async ({ ctx, input }) => { const { id, ...data } = input const projectData = await ctx.prisma.studentProject.update({ @@ -29,28 +28,24 @@ export const projectRouter = createRouter() }) return projectData - }, - }) - .mutation('remove', { - input: z.number(), - async resolve({ ctx, input }) { - try { - await ctx.prisma.studentProject.delete({ where: { id: input } }) - } catch (error) { - throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unable to delete project' }) - } - }, - }) - .query('get_all', { - input: z.number(), - async resolve({ ctx, input }) { - const projects = await ctx.prisma.studentProject.findMany({ - where: { studentId: input }, - orderBy: { - startedAt: 'desc', - }, - }) + }), + + remove: procedureWithStudent.input(z.number()).mutation(async ({ ctx, input }) => { + try { + await ctx.prisma.studentProject.delete({ where: { id: input } }) + } catch (error) { + throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unable to delete project' }) + } + }), + + get_all: procedureWithStudent.input(z.number()).query(async ({ ctx, input }) => { + const projects = await ctx.prisma.studentProject.findMany({ + where: { studentId: input }, + orderBy: { + startedAt: 'desc', + }, + }) - return projects - }, - }) + return projects + }), +}) diff --git a/api/src/rpc/routers/student/score.ts b/api/src/rpc/routers/student/score.ts index 24a1a3d..aaa93c7 100644 --- a/api/src/rpc/routers/student/score.ts +++ b/api/src/rpc/routers/student/score.ts @@ -1,29 +1,28 @@ -import { createRouter } from '../../createRouter' +import { trpc } from '../../trpc' import { z } from 'zod' import { TRPCError } from '@trpc/server' import { semUpdateSchema } from '@mirai/app' +import { procedureWithStudent } from '../../procedures' -export const scoreRouter = createRouter() - .query('get', { - input: z.number(), - async resolve({ ctx, input }) { - const scoreDetails = await ctx.prisma.studentScore.findFirst({ - where: { studentId: input }, +export const scoreRouter = trpc.router({ + get: procedureWithStudent.input(z.number()).query(async ({ ctx, input }) => { + const scoreDetails = await ctx.prisma.studentScore.findFirst({ + where: { studentId: input }, + }) + + if (scoreDetails === null) { + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'Student score details not found !', }) + } - if (scoreDetails === null) { - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'Student score details not found !', - }) - } + return scoreDetails + }), - return scoreDetails - }, - }) - .mutation('update_score_card', { - input: z.object({ studentId: z.number(), data: z.record(semUpdateSchema) }), - async resolve({ ctx, input: { data, studentId } }) { + update_score_card: procedureWithStudent + .input(z.object({ studentId: z.number(), data: z.record(semUpdateSchema) })) + .mutation(async ({ ctx, input: { data, studentId } }) => { const scoreData = await ctx.prisma.studentScore.update({ where: { studentId }, data: { @@ -32,5 +31,5 @@ export const scoreRouter = createRouter() }) return scoreData - }, - }) + }), +}) diff --git a/api/src/rpc/routers/ticket.ts b/api/src/rpc/routers/ticket.ts index 7a02d37..e20ca7e 100644 --- a/api/src/rpc/routers/ticket.ts +++ b/api/src/rpc/routers/ticket.ts @@ -3,57 +3,54 @@ import { TRPCError } from '@trpc/server' import { z } from 'zod' import { hashPass, isRole, isUniqueId } from '../../lib' import { flowProducer } from '../../queues' -import { createRouter } from '../createRouter' - -export const ticketRouter = createRouter() - .query('get', { - input: z.number(), - async resolve({ ctx, input }) { - const ticket = await ctx.prisma.ticket.findFirst({ - where: { id: input }, - }) +import { trpc } from '../trpc' - if (ticket === null) - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'Institute not found !', - }) +const adminInstitutedMiddleware = trpc.middleware(async ({ ctx, next }) => { + if (ctx.session === null || !(isRole(ctx.session.user.role).admin || isRole(ctx.session.user.role).instituteOrMod)) + throw new TRPCError({ code: 'UNAUTHORIZED' }) - return ticket - }, + return await next({ + ctx: { ...ctx, session: ctx.session }, }) - .mutation('create', { - input: createTicketSchema, - async resolve({ ctx, input }) { - if (!(await isUniqueId(input.meta.data.uniId, input.instituteId))) { - throw new TRPCError({ code: 'BAD_REQUEST', message: 'University ID should be unique' }) - } +}) - const pass = input.meta.data.password - - if (pass !== undefined) { - input.meta.data.password = await hashPass(pass) - } +export const ticketRouter = trpc.router({ + get: trpc.procedure.input(z.number()).query(async ({ ctx, input }) => { + const ticket = await ctx.prisma.ticket.findFirst({ + where: { id: input }, + }) - const newToken = await ctx.prisma.ticket.create({ - data: input, + if (ticket === null) + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'Institute not found !', }) - return newToken - }, - }) - .middleware(async ({ ctx, next }) => { - if (ctx.session === null || !(isRole(ctx.session.user.role).admin || isRole(ctx.session.user.role).instituteOrMod)) - throw new TRPCError({ code: 'UNAUTHORIZED' }) + return ticket + }), + + create: trpc.procedure.input(createTicketSchema).mutation(async ({ ctx, input }) => { + if (!(await isUniqueId(input.meta.data.uniId, input.instituteId))) { + throw new TRPCError({ code: 'BAD_REQUEST', message: 'University ID should be unique' }) + } + + const pass = input.meta.data.password - return await next({ - ctx: { ...ctx, session: ctx.session }, + if (pass !== undefined) { + input.meta.data.password = await hashPass(pass) + } + + const newToken = await ctx.prisma.ticket.create({ + data: input, }) - }) - // TODO: paginate - .query('get_all', { - input: ticketListingInput, - async resolve({ ctx, input: { type, createdAt, sort, ...rest } }) { + + return newToken + }), + + get_all: trpc.procedure + .use(adminInstitutedMiddleware) + .input(ticketListingInput) + .query(async ({ ctx, input: { type, createdAt, sort, ...rest } }) => { const jsonQueries = [] type !== undefined && @@ -74,11 +71,12 @@ export const ticketRouter = createRouter() }) return tickets - }, - }) - .mutation('action', { - input: bulkTicketResolveSchema, - async resolve({ input }) { + }), + + action: trpc.procedure + .use(adminInstitutedMiddleware) + .input(bulkTicketResolveSchema) + .mutation(async ({ input }) => { try { await flowProducer.add({ name: `tickt-review-batch-${input.key}-${Date.now()}`, @@ -100,11 +98,12 @@ export const ticketRouter = createRouter() } catch (error) { return { success: false, data: error } } - }, - }) - .mutation('remove', { - input: z.number(), - async resolve({ ctx, input }) { + }), + + remove: trpc.procedure + .use(adminInstitutedMiddleware) + .input(z.number()) + .mutation(async ({ ctx, input }) => { try { await ctx.prisma.ticket.delete({ where: { id: input }, @@ -112,5 +111,5 @@ export const ticketRouter = createRouter() } catch (error) { throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unable to delete ticket' }) } - }, - }) + }), +}) diff --git a/api/src/rpc/trpc.ts b/api/src/rpc/trpc.ts new file mode 100644 index 0000000..7647f0c --- /dev/null +++ b/api/src/rpc/trpc.ts @@ -0,0 +1,7 @@ +import { initTRPC } from '@trpc/server' +import { Context } from './context' +import superjson from 'superjson' + +export const trpc = initTRPC.context<Context>().create({ + transformer: superjson, +}) diff --git a/app/package.json b/app/package.json index 8b94825..fb8e5bb 100644 --- a/app/package.json +++ b/app/package.json @@ -19,10 +19,12 @@ "@hookform/resolvers": "^2.9.7", "@popperjs/core": "^2.11.6", "@prisma/client": "*", - "@trpc/client": "^9.27.1", - "@trpc/next": "^9.27.1", - "@trpc/react": "^9.27.1", - "@trpc/server": "*", + "@tanstack/react-query": "^4.2.3", + "@tanstack/react-query-devtools": "^4.2.3", + "@trpc/client": "10.0.0-proxy-alpha.73", + "@trpc/next": "10.0.0-proxy-alpha.73", + "@trpc/react": "10.0.0-proxy-alpha.73", + "@trpc/server": "10.0.0-proxy-alpha.73", "axios": "^0.27.2", "clsx": "^1.2.1", "daisyui": "^2.24.0", @@ -35,7 +37,6 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-hook-form": "^7.34.2", - "react-query": "^3.39.2", "react-shepherd": "^4.1.0", "react-use": "^17.4.0", "reconnecting-websocket": "^4.4.0", diff --git a/app/src/api/account.ts b/app/src/api/account.ts index 33b822a..1d0df07 100644 --- a/app/src/api/account.ts +++ b/app/src/api/account.ts @@ -2,6 +2,6 @@ import { tourSchema } from '../schemas' import { trpcClient } from '../utils/trpc' import { z } from 'zod' -export function toggleTour(opts: z.infer<typeof tourSchema>) { - return trpcClient.mutation('account.toggle_tour', opts) +export async function toggleTour(opts: z.infer<typeof tourSchema>) { + return await trpcClient.account.toggle_tour.mutate(opts) } diff --git a/app/src/components/globals/AppLayout.tsx b/app/src/components/globals/AppLayout.tsx index a3e49fc..27af475 100644 --- a/app/src/components/globals/AppLayout.tsx +++ b/app/src/components/globals/AppLayout.tsx @@ -1,6 +1,6 @@ import Head from 'next/head' import { ReactNode } from 'react' -import { ReactQueryDevtools } from 'react-query/devtools' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' import { NavBar } from './NavBar' import { SideBar } from './SideBar' diff --git a/app/src/components/globals/DefaultLayout.tsx b/app/src/components/globals/DefaultLayout.tsx index 29c57b8..953b114 100644 --- a/app/src/components/globals/DefaultLayout.tsx +++ b/app/src/components/globals/DefaultLayout.tsx @@ -1,6 +1,6 @@ import Head from 'next/head' import { ReactNode } from 'react' -import { ReactQueryDevtools } from 'react-query/devtools' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' interface DefaultLayoutProps { children: ReactNode diff --git a/app/src/components/institute/student/GenerateUrlDialog.tsx b/app/src/components/institute/student/GenerateUrlDialog.tsx index 6452b70..cb1f07c 100644 --- a/app/src/components/institute/student/GenerateUrlDialog.tsx +++ b/app/src/components/institute/student/GenerateUrlDialog.tsx @@ -42,7 +42,7 @@ export const GenerateUrlDialog: React.FC<Props> = ({ onClose }) => { async function generateUrl(data: z.infer<typeof generateOnboardingUrlSchema>) { try { - const payload = await trpcClient.mutation('institute.gen_onboarding_token', data) + const payload = await trpcClient.institute.gen_onboarding_token.mutate(data) const { origin } = location diff --git a/app/src/components/student/profile/Basics/index.tsx b/app/src/components/student/profile/Basics/index.tsx index 858ee64..9f88502 100644 --- a/app/src/components/student/profile/Basics/index.tsx +++ b/app/src/components/student/profile/Basics/index.tsx @@ -78,15 +78,15 @@ export const Basics: React.FC<Props> = () => { const validator = z.string().min(1, 'Field is required') let score = 0 - const { currentAddress = {}, permanentAddress = {} } = val + const { currentAddress, permanentAddress } = val ADDRES_FIELDS.forEach((field) => { - if (!validator.safeParse(currentAddress[field]).success) { + if (!validator.safeParse(((currentAddress ?? {}) as any)[field]).success) { setError(`currentAddress.${field}`, { message: `${field} is required` }) score++ } - if (!validator.safeParse(permanentAddress[field]).success) { + if (!validator.safeParse(((permanentAddress ?? {}) as any)[field]).success) { setError(`permanentAddress.${field}`, { message: `${field} is required` }) score++ diff --git a/app/src/contexts/WsClient.tsx b/app/src/contexts/WsClient.tsx index 72ad219..8bd1fa9 100644 --- a/app/src/contexts/WsClient.tsx +++ b/app/src/contexts/WsClient.tsx @@ -28,7 +28,7 @@ function WSClientProvider({ children }: WSProviderProps): JSX.Element { if (conn === null && status === 'authenticated' && isInstituteRole(data.user.role).is && !isConnecting.current) { isConnecting.current = true - createWsConn(() => trpcClient.query('auth.auth_token', data)) + createWsConn(async () => await trpcClient.auth.auth_token.query(data)) .then(setConn) .catch((e) => { // eslint-disable-next-line no-console diff --git a/app/src/contexts/currenctAccount.tsx b/app/src/contexts/currenctAccount.tsx index 59c652f..41ce6ee 100644 --- a/app/src/contexts/currenctAccount.tsx +++ b/app/src/contexts/currenctAccount.tsx @@ -20,9 +20,8 @@ export const CurrentAccount: React.FC<Props> = ({ children }) => { const setUser = useSetAtom(userAtom) const { data, status } = useSession() - const { isLoading = true } = trpc.useQuery(['auth.account'], { - refetchOnWindowFocus: false, - enabled: status === 'authenticated' && Boolean(data), + const { isLoading = true } = trpc.auth.account.useQuery(undefined, { + enabled: status === 'authenticated' && data !== null, onSuccess(data) { data !== null && setUser(data) }, diff --git a/app/src/contexts/student/basics.tsx b/app/src/contexts/student/basics.tsx index 3db6e30..eaa5a36 100644 --- a/app/src/contexts/student/basics.tsx +++ b/app/src/contexts/student/basics.tsx @@ -8,7 +8,7 @@ export function useBasics() { const userData = useUser() const setBasics = useSetAtom(studentBasicsAtom) - const { mutateAsync: manage, isLoading } = trpc.useMutation(['student.basics.manage'], { + const { mutateAsync: manage, isLoading } = trpc.student.basics.manage.useMutation({ onError() { setAlert({ type: 'danger', @@ -28,9 +28,9 @@ export function useBasics() { async function invalidate() { if (userData.studentId === null) return - const data = await trpcClient.query('student.basics.get', userData.studentId) + const data = await trpcClient.student.basics.get.query(userData.studentId) - void setBasics(data as any) + void setBasics(data) } return { diff --git a/app/src/contexts/student/certification.ts b/app/src/contexts/student/certification.ts index 8c7fe8f..ff724fb 100644 --- a/app/src/contexts/student/certification.ts +++ b/app/src/contexts/student/certification.ts @@ -9,7 +9,7 @@ export function useCertification() { const userData = useUser() const setCertifications = useSetAtom(studentCertificationsAtom) - const { mutateAsync: create, isLoading: isCreateLoading } = trpc.useMutation(['student.certification.create'], { + const { mutateAsync: create, isLoading: isCreateLoading } = trpc.student.certification.create.useMutation({ onError() { setAlert({ type: 'danger', @@ -26,7 +26,7 @@ export function useCertification() { }, }) - const { mutateAsync: update, isLoading: isUpdateLoading } = trpc.useMutation(['student.certification.update'], { + const { mutateAsync: update, isLoading: isUpdateLoading } = trpc.student.certification.update.useMutation({ onError() { setAlert({ type: 'danger', @@ -45,7 +45,7 @@ export function useCertification() { async function removeCertification(id: number) { try { - await trpcClient.mutation('student.certification.remove', id) + await trpcClient.student.certification.remove.mutate(id) setAlert({ type: 'success', @@ -65,7 +65,7 @@ export function useCertification() { async function invalidate() { if (userData.studentId === null) return - const data = await trpcClient.query('student.certification.get_all', userData.studentId) + const data = await trpcClient.student.certification.get_all.query(userData.studentId) void setCertifications(data) } diff --git a/app/src/contexts/student/education.tsx b/app/src/contexts/student/education.tsx index 6683a34..3e701c0 100644 --- a/app/src/contexts/student/education.tsx +++ b/app/src/contexts/student/education.tsx @@ -9,7 +9,7 @@ export function useEducation() { const userData = useUser() const setEducation = useSetAtom(studentEducationAtom) - const { mutateAsync: create, isLoading: isCreateLoading } = trpc.useMutation(['student.education.create'], { + const { mutateAsync: create, isLoading: isCreateLoading } = trpc.student.education.create.useMutation({ onError() { setAlert({ type: 'danger', @@ -21,10 +21,12 @@ export function useEducation() { type: 'success', message: 'Student education created successfully !', }) + void invalidate() }, }) - const { mutateAsync: update, isLoading: isUpdateLoading } = trpc.useMutation(['student.education.update'], { + + const { mutateAsync: update, isLoading: isUpdateLoading } = trpc.student.education.update.useMutation({ onError() { setAlert({ type: 'danger', @@ -42,7 +44,7 @@ export function useEducation() { async function deleteEducation(id: number) { try { - await trpcClient.mutation('student.education.remove', id) + await trpcClient.student.education.remove.mutate(id) setAlert({ type: 'success', @@ -62,7 +64,7 @@ export function useEducation() { async function invalidate() { if (userData.studentId === null) return - const data = await trpcClient.query('student.education.get_all', userData.studentId) + const data = await trpcClient.student.education.get_all.query(userData.studentId) void setEducation(data) } diff --git a/app/src/contexts/student/experience.tsx b/app/src/contexts/student/experience.tsx index 69cefee..bf810ec 100644 --- a/app/src/contexts/student/experience.tsx +++ b/app/src/contexts/student/experience.tsx @@ -9,7 +9,7 @@ export function useExperience() { const userData = useUser() const setExperiences = useSetAtom(studentExperienceAtom) - const { mutateAsync: create, isLoading: isCreateLoading } = trpc.useMutation(['student.experience.create'], { + const { mutateAsync: create, isLoading: isCreateLoading } = trpc.student.experience.create.useMutation({ onError() { setAlert({ type: 'danger', @@ -26,7 +26,7 @@ export function useExperience() { }, }) - const { mutateAsync: update, isLoading: isUpdateLoading } = trpc.useMutation(['student.experience.update'], { + const { mutateAsync: update, isLoading: isUpdateLoading } = trpc.student.experience.update.useMutation({ onError() { setAlert({ type: 'danger', @@ -45,7 +45,7 @@ export function useExperience() { async function deleteExperience(id: number) { try { - await trpcClient.mutation('student.experience.remove', id) + await trpcClient.student.experience.remove.mutate(id) setAlert({ type: 'success', @@ -65,7 +65,7 @@ export function useExperience() { async function invalidate() { if (userData.studentId === null) return - const data = await trpcClient.query('student.experience.get_all', userData.studentId) + const data = await trpcClient.student.experience.get_all.query(userData.studentId) void setExperiences(data) } diff --git a/app/src/contexts/student/index.ts b/app/src/contexts/student/index.ts index c4efe6b..4a3967c 100644 --- a/app/src/contexts/student/index.ts +++ b/app/src/contexts/student/index.ts @@ -1,16 +1,16 @@ import { useAtomValue, useSetAtom } from 'jotai' import { loggedInAtom, studentAtom, useUser } from '../../stores' -import { QueryOptions } from '../../types' +import { AnyObject } from '../../types' import { trpc } from '../../utils' -export function useStudent(opts?: QueryOptions<'student.get'>) { +export function useStudent(opts: AnyObject = {}) { opts = opts ?? {} const isLoggedIn = useAtomValue(loggedInAtom) const userData = useUser() const setStudentVal = useSetAtom(studentAtom) - const { data: student = null, isLoading } = trpc.useQuery(['student.get', userData.studentId as number], { + const { data: student = null, isLoading } = trpc.student.get.useQuery(userData.studentId as number, { ...opts, onSuccess(data) { opts?.onSuccess !== undefined && opts.onSuccess(data) diff --git a/app/src/contexts/student/projects.ts b/app/src/contexts/student/projects.ts index f9e631c..668c695 100644 --- a/app/src/contexts/student/projects.ts +++ b/app/src/contexts/student/projects.ts @@ -9,7 +9,7 @@ export function useProject() { const userData = useUser() const setProjects = useSetAtom(studentProjectsAtom) - const { mutateAsync: create, isLoading: isCreateLoading } = trpc.useMutation(['student.project.create'], { + const { mutateAsync: create, isLoading: isCreateLoading } = trpc.student.project.create.useMutation({ onError() { setAlert({ type: 'danger', @@ -26,7 +26,7 @@ export function useProject() { }, }) - const { mutateAsync: update, isLoading: isUpdateLoading } = trpc.useMutation(['student.project.update'], { + const { mutateAsync: update, isLoading: isUpdateLoading } = trpc.student.project.update.useMutation({ onError() { setAlert({ type: 'danger', @@ -45,7 +45,7 @@ export function useProject() { async function deleteProject(id: number) { try { - await trpcClient.mutation('student.project.remove', id) + await trpcClient.student.project.remove.mutate(id) setAlert({ type: 'success', @@ -66,7 +66,7 @@ export function useProject() { async function invalidate() { if (userData.studentId === null) return - const data = await trpcClient.query('student.project.get_all', userData.studentId) + const data = await trpcClient.student.project.get_all.query(userData.studentId) void setProjects(data) } diff --git a/app/src/contexts/student/score.tsx b/app/src/contexts/student/score.tsx index 313b264..04d144b 100644 --- a/app/src/contexts/student/score.tsx +++ b/app/src/contexts/student/score.tsx @@ -8,7 +8,7 @@ export function useScore() { const userData = useUser() const setScore = useSetAtom(studentScoreAtom) - const { mutate: updateScoreCard, isLoading } = trpc.useMutation(['student.score.update_score_card'], { + const { mutate: updateScoreCard, isLoading } = trpc.student.score.update_score_card.useMutation({ onError() { setAlert({ type: 'danger', @@ -30,7 +30,7 @@ export function useScore() { async function invalidate() { if (userData.studentId === null) return - const data = await trpcClient.query('student.score.get', userData.studentId) + const data = await trpcClient.student.score.get.query(userData.studentId) void setScore(data as any) } diff --git a/app/src/contexts/student/skills.ts b/app/src/contexts/student/skills.ts index 4a46d35..48817f1 100644 --- a/app/src/contexts/student/skills.ts +++ b/app/src/contexts/student/skills.ts @@ -7,7 +7,7 @@ export function useSkills() { const setAlert = useAlert() const setSkills = useSetAtom(studentSkillsAtom) - const { mutateAsync: update, isLoading } = trpc.useMutation(['student.skills.update'], { + const { mutateAsync: update, isLoading } = trpc.student.skills.update.useMutation({ onError() { setAlert({ type: 'danger', diff --git a/app/src/contexts/useBatch.ts b/app/src/contexts/useBatch.ts index 21c0e32..7dbdafe 100644 --- a/app/src/contexts/useBatch.ts +++ b/app/src/contexts/useBatch.ts @@ -4,16 +4,16 @@ import { getUserHome, trpc, useStrictQueryCheck } from '../utils' import { useAtomValue, useSetAtom } from 'jotai' import { useRouter } from 'next/router' import { loggedInAtom, useUser } from '../stores' -import { QueryOptions } from '../types' +import { AnyObject } from '../types' -export function useBatches(opts?: QueryOptions<'batch.getAll'>) { +export function useBatches(opts?: AnyObject) { opts = opts ?? {} const userData = useUser() const isLoggedIn = useAtomValue(loggedInAtom) const router = useRouter() - const { data: batches = [], isLoading } = trpc.useQuery(['batch.getAll', userData.instituteId as number], { + const { data: batches = [], isLoading } = trpc.batch.getAll.useQuery(userData.instituteId as number, { ...opts, enabled: isLoggedIn, onError(e) { @@ -26,7 +26,7 @@ export function useBatches(opts?: QueryOptions<'batch.getAll'>) { return { batches, isLoading } } -export function useBatch(opts?: QueryOptions<'batch.get'>) { +export function useBatch(opts?: AnyObject) { opts = opts ?? {} const userData = useUser() const isLoggedIn = useAtomValue(loggedInAtom) @@ -42,14 +42,12 @@ export function useBatch(opts?: QueryOptions<'batch.get'>) { skipPath: '/institute/batch/create', }) - const { data: batch, isLoading } = trpc.useQuery( - [ - 'batch.get', - { - instituteId: userData.instituteId as number, - batchId: +(router.query.batchId as string), - }, - ], + const { data: batch, isLoading } = trpc.batch.get.useQuery( + { + instituteId: userData.instituteId as number, + batchId: +(router.query.batchId as string), + }, + { onError(e) { if (e.data?.code === 'NOT_FOUND') { @@ -62,9 +60,9 @@ export function useBatch(opts?: QueryOptions<'batch.get'>) { }, ) - const update = trpc.useMutation(['batch.update'], { + const update = trpc.batch.update.useMutation({ onSuccess() { - void utils.invalidateQueries(['batch.getAll']) + void utils.batch.getAll.invalidate() setAlert({ type: 'success', message: 'Batch updated', @@ -73,9 +71,9 @@ export function useBatch(opts?: QueryOptions<'batch.get'>) { }, }) - const create = trpc.useMutation(['batch.create'], { + const create = trpc.batch.create.useMutation({ onSuccess() { - void utils.invalidateQueries(['batch.getAll']) + void utils.batch.getAll.invalidate() setAlert({ type: 'success', diff --git a/app/src/contexts/useCourse.ts b/app/src/contexts/useCourse.ts index 089428f..c88482c 100644 --- a/app/src/contexts/useCourse.ts +++ b/app/src/contexts/useCourse.ts @@ -3,17 +3,17 @@ import { useRouter } from 'next/router' import { useEffect, useMemo } from 'react' import { loaderAtom, useAlert } from '../components/lib' import { loggedInAtom, useUser } from '../stores' -import { QueryOptions } from '../types' +import { AnyObject } from '../types' import { getUserHome, trpc, useStrictQueryCheck } from '../utils' -export function useCourses(opts?: QueryOptions<'course.getAll'>) { +export function useCourses(opts?: AnyObject) { opts = opts ?? {} const userData = useUser() const isLoggedIn = useAtomValue(loggedInAtom) const router = useRouter() - const { data: courses = [], isLoading } = trpc.useQuery(['course.getAll', userData.instituteId as number], { + const { data: courses = [], isLoading } = trpc.course.getAll.useQuery(userData.instituteId as number, { ...opts, enabled: isLoggedIn, onError(e) { @@ -29,7 +29,7 @@ export function useCourses(opts?: QueryOptions<'course.getAll'>) { } } -export function useCourse(opts?: QueryOptions<'course.get'>) { +export function useCourse(opts?: AnyObject) { opts = opts ?? {} const router = useRouter() @@ -46,14 +46,12 @@ export function useCourse(opts?: QueryOptions<'course.get'>) { skipPath: '/institute/course/create', }) - const { data: course, isLoading } = trpc.useQuery( - [ - 'course.get', - { - instituteId: userData.instituteId as number, - courseId: +(router.query.courseId as string), - }, - ], + const { data: course, isLoading } = trpc.course.get.useQuery( + { + instituteId: userData.instituteId as number, + courseId: +(router.query.courseId as string), + }, + { onError(e) { if (e.data?.code === 'NOT_FOUND') { @@ -67,9 +65,9 @@ export function useCourse(opts?: QueryOptions<'course.get'>) { }, ) - const update = trpc.useMutation(['course.update'], { + const update = trpc.course.update.useMutation({ onSuccess() { - void utils.invalidateQueries(['course.getAll']) + void utils.course.getAll.invalidate() setAlert({ type: 'success', @@ -80,9 +78,9 @@ export function useCourse(opts?: QueryOptions<'course.get'>) { }, }) - const create = trpc.useMutation(['course.create'], { + const create = trpc.course.create.useMutation({ onSuccess() { - void utils.invalidateQueries(['course.getAll']) + void utils.course.getAll.invalidate() setAlert({ type: 'success', diff --git a/app/src/contexts/useDepartment.ts b/app/src/contexts/useDepartment.ts index 8af71f3..3b58609 100644 --- a/app/src/contexts/useDepartment.ts +++ b/app/src/contexts/useDepartment.ts @@ -1,19 +1,19 @@ import { useEffect, useMemo } from 'react' import { useAtomValue, useSetAtom } from 'jotai' import { useRouter } from 'next/router' -import { QueryOptions } from '../types' +import { AnyObject } from '../types' import { loggedInAtom, useUser } from '../stores' import { getUserHome, trpc, useStrictQueryCheck } from '../utils' import { loaderAtom, useAlert } from '../components/lib' -export function useDepartments(opts?: QueryOptions<'department.getAll'>) { +export function useDepartments(opts?: AnyObject) { opts = opts ?? {} const userData = useUser() const isLoggedIn = useAtomValue(loggedInAtom) const router = useRouter() - const { data: departments = [], isLoading } = trpc.useQuery(['department.getAll', userData.instituteId as number], { + const { data: departments = [], isLoading } = trpc.department.getAll.useQuery(userData.instituteId as number, { ...opts, enabled: isLoggedIn, onError(e) { @@ -29,7 +29,7 @@ export function useDepartments(opts?: QueryOptions<'department.getAll'>) { } } -export function useDepartment(opts?: QueryOptions<'department.get'>) { +export function useDepartment(opts?: AnyObject) { opts = opts ?? {} const router = useRouter() @@ -46,14 +46,11 @@ export function useDepartment(opts?: QueryOptions<'department.get'>) { skipPath: '/institute/department/create', }) - const { data: department, isLoading } = trpc.useQuery( - [ - 'department.get', - { - instituteId: userDate.instituteId as number, - departmentId: +(router.query.departmentId as string), - }, - ], + const { data: department, isLoading } = trpc.department.get.useQuery( + { + instituteId: userDate.instituteId as number, + departmentId: +(router.query.departmentId as string), + }, { onError(e) { if (e.data?.code === 'NOT_FOUND') { @@ -67,9 +64,9 @@ export function useDepartment(opts?: QueryOptions<'department.get'>) { }, ) - const update = trpc.useMutation(['department.update'], { + const update = trpc.department.update.useMutation({ onSuccess() { - void utils.invalidateQueries(['department.getAll']) + void utils.department.getAll.invalidate() setAlert({ type: 'success', @@ -80,9 +77,9 @@ export function useDepartment(opts?: QueryOptions<'department.get'>) { }, }) - const create = trpc.useMutation(['department.create'], { + const create = trpc.department.create.useMutation({ onSuccess() { - void utils.invalidateQueries(['department.getAll']) + void utils.department.getAll.invalidate() setAlert({ type: 'success', diff --git a/app/src/contexts/useInstitute.ts b/app/src/contexts/useInstitute.ts index 31b8f8b..e8c072f 100644 --- a/app/src/contexts/useInstitute.ts +++ b/app/src/contexts/useInstitute.ts @@ -5,7 +5,7 @@ import { z } from 'zod' import { loaderAtom, useAlert } from '../components/lib' import { manageInstituteSchema } from '../schemas' import { loggedInAtom, useUser } from '../stores' -import { QueryOptions } from '../types' +import { AnyObject } from '../types' import { getUserHome, trpc, useStrictQueryCheck } from '../utils' export function useInstitutes() { @@ -13,7 +13,7 @@ export function useInstitutes() { const isLoggedIn = useAtomValue(loggedInAtom) const router = useRouter() - const { data: institutes = [], isLoading } = trpc.useQuery(['institute.get_all'], { + const { data: institutes = [], isLoading } = trpc.institute.get_all.useQuery(undefined, { enabled: isLoggedIn, onError(e) { if (e.data?.code === 'UNAUTHORIZED') { @@ -28,7 +28,7 @@ export function useInstitutes() { } } -export function useInstitute(opts?: QueryOptions<'institute.get'>) { +export function useInstitute(opts?: AnyObject) { opts = opts ?? {} const router = useRouter() @@ -44,7 +44,7 @@ export function useInstitute(opts?: QueryOptions<'institute.get'>) { skipPath: '/admin/institute/create', }) - const { data: institute, isLoading } = trpc.useQuery(['institute.get', +(router.query.instituteId as string)], { + const { data: institute, isLoading } = trpc.institute.get.useQuery(+(router.query.instituteId as string), { onError(e) { if (e.data?.code === 'NOT_FOUND') { setAlert({ type: 'danger', message: 'Institute not found' }) @@ -56,7 +56,7 @@ export function useInstitute(opts?: QueryOptions<'institute.get'>) { enabled: isLoggedIn && isQuery, }) - const signUp = trpc.useMutation(['auth.sign_up'], { + const signUp = trpc.auth.sign_up.useMutation({ onError: opts?.onError, onSuccess({ instituteId }) { if (instituteId === null) return @@ -70,7 +70,7 @@ export function useInstitute(opts?: QueryOptions<'institute.get'>) { }, }) - const createInstitute = trpc.useMutation(['account.create_institute'], { + const createInstitute = trpc.account.create_institute.useMutation({ onError: opts?.onError, }) @@ -80,9 +80,9 @@ export function useInstitute(opts?: QueryOptions<'institute.get'>) { signUp.mutate({ role: 'INSTITUTE', email, instituteId, name: rest.name }) } - const update = trpc.useMutation(['account.update_institute'], { + const update = trpc.account.update_institute.useMutation({ onSuccess() { - void utils.invalidateQueries(['institute.get_all']) + void utils.institute.get_all.invalidate() setAlert({ type: 'success', diff --git a/app/src/contexts/useStudents.ts b/app/src/contexts/useStudents.ts index b528969..aa08730 100644 --- a/app/src/contexts/useStudents.ts +++ b/app/src/contexts/useStudents.ts @@ -1,9 +1,15 @@ +import { AppRouter } from '@mirai/api' +import { inferProcedureOutput } from '@trpc/server' import { useAtomValue } from 'jotai' import { useRouter } from 'next/router' import { studentFiltersAtom, loggedInAtom, useUser } from '../stores' -import { getUserHome, inferQueryOutput, trpc } from '../utils' +import { getUserHome, trpc } from '../utils' -export type StudentsListingType = inferQueryOutput<'institute.get_all_students'> extends Array<infer U> ? U : never +export type StudentsListingType = inferProcedureOutput<AppRouter['institute']['get_all_students']> extends Array< + infer U +> + ? U + : never export function useStudents() { const router = useRouter() @@ -16,14 +22,17 @@ export function useStudents() { data: students = [], isLoading, refetch, - } = trpc.useQuery(['institute.get_all_students', { instituteId: Number(userData.instituteId), ...studentFilters }], { - enabled: isLoggedIn && userData.instituteId !== null, - onError(e) { - if (e.data?.code === 'UNAUTHORIZED') { - void router.push(getUserHome(userData.role)) - } + } = trpc.institute.get_all_students.useQuery( + { instituteId: Number(userData.instituteId), ...studentFilters }, + { + enabled: isLoggedIn && userData.instituteId !== null, + onError(e) { + if (e.data?.code === 'UNAUTHORIZED') { + void router.push(getUserHome(userData.role)) + } + }, }, - }) + ) return { students, diff --git a/app/src/contexts/useTicket.tsx b/app/src/contexts/useTicket.tsx index 7f84674..c864134 100644 --- a/app/src/contexts/useTicket.tsx +++ b/app/src/contexts/useTicket.tsx @@ -6,7 +6,7 @@ import { useRouter } from 'next/router' import { useEffect, useMemo } from 'react' import { TicketType } from '../schemas' import { loggedInAtom, useUser, ticketFiltersAtom } from '../stores' -import { OverWrite, QueryOptions } from '../types' +import { AnyObject, OverWrite } from '../types' import { getUserHome, useGlobalError, useQuery, trpc } from '../utils' interface Ticketmeta { @@ -16,23 +16,20 @@ interface Ticketmeta { export type TicketWithMeta = OverWrite<Ticket, { meta: Ticketmeta }> -export function useTickets(opts?: QueryOptions<'ticket.get_all'>) { +export function useTickets(opts?: AnyObject) { opts = opts ?? {} const userData = useUser() const isLoggedIn = useAtomValue(loggedInAtom) const router = useRouter() const ticketFilters = useAtomValue(ticketFiltersAtom) - const { data: tickets = [], isLoading } = trpc.useQuery( - [ - 'ticket.get_all', - { - // TODO: fix bug - // when date filter value is not there don't refetch - instituteId: userData.instituteId as number, - ...ticketFilters, - }, - ], + const { data: tickets = [], isLoading } = trpc.ticket.get_all.useQuery( + { + // TODO: fix bug + // when date filter value is not there don't refetch + instituteId: userData.instituteId as number, + ...ticketFilters, + }, { ...opts, // TODO: check for role @@ -48,7 +45,7 @@ export function useTickets(opts?: QueryOptions<'ticket.get_all'>) { return { tickets, isLoading } } -export function useTicket(opts?: QueryOptions<'ticket.get'>) { +export function useTicket(opts?: AnyObject) { opts = opts ?? {} const setAlert = useAlert() const setLoader = useSetAtom(loaderAtom) @@ -56,7 +53,7 @@ export function useTicket(opts?: QueryOptions<'ticket.get'>) { const { isQuery, queryVal } = useQuery('ticketId') const setError = useGlobalError() - const { data: ticket, isLoading } = trpc.useQuery(['ticket.get', Number(queryVal)], { + const { data: ticket, isLoading } = trpc.ticket.get.useQuery(Number(queryVal), { onError(e) { setError(e) }, @@ -64,9 +61,9 @@ export function useTicket(opts?: QueryOptions<'ticket.get'>) { enabled: isQuery, }) - const create = trpc.useMutation(['ticket.create'], { + const create = trpc.ticket.create.useMutation({ async onSuccess(data): Promise<Ticket> { - void utils.invalidateQueries(['ticket.get_all']) + void utils.ticket.get_all.invalidate() setAlert({ type: 'success', @@ -80,12 +77,12 @@ export function useTicket(opts?: QueryOptions<'ticket.get'>) { }, }) - const action = trpc.useMutation(['ticket.action'], { + const action = trpc.ticket.action.useMutation({ async onSuccess(data): Promise<{ success: boolean data: TicketResolveResponse[] }> { - void utils.invalidateQueries(['ticket.get_all']) + void utils.ticket.get_all.invalidate() setAlert({ type: 'success', diff --git a/app/src/pages/_app.tsx b/app/src/pages/_app.tsx index e89f263..498e89f 100644 --- a/app/src/pages/_app.tsx +++ b/app/src/pages/_app.tsx @@ -1,19 +1,15 @@ import '../styles/globals.scss' -import { httpBatchLink } from '@trpc/client/links/httpBatchLink' -import { loggerLink } from '@trpc/client/links/loggerLink' -import { withTRPC } from '@trpc/next' import { NextPage } from 'next' import { AppProps } from 'next/app' import { AppType } from 'next/dist/shared/lib/utils' import { ReactElement, ReactNode } from 'react' -import type { AppRouter } from '@mirai/api' -import superjson from 'superjson' import { DefaultLayout } from '../components/globals/DefaultLayout' import { AppProviders } from '../contexts' import { MAlertGroup, MLoader } from '../components/lib' -import { ErrorBoundary, getBaseUrl } from '../utils/helpers' +import { ErrorBoundary } from '../utils/helpers' import { useRouter } from 'next/router' +import { trpc } from '../utils' export type NextPageWithLayout<P = Record<string, never>, IP = P> = NextPage<P, IP> & { getLayout?: (page: ReactElement) => ReactNode @@ -54,49 +50,4 @@ const MyApp = (({ Component, pageProps }: AppPropsWithLayout) => { ) }) as AppType -export default withTRPC<AppRouter>({ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - config() { - return { - links: [ - // adds pretty logs to your console in development and logs errors in production - loggerLink({ - enabled: (opts) => - process.env.NODE_ENV === 'development' || (opts.direction === 'down' && opts.result instanceof Error), - }), - httpBatchLink({ - url: `${getBaseUrl()}/api/trpc`, - }), - ], - transformer: superjson, - queryClientConfig: { - defaultOptions: { - queries: { - refetchOnWindowFocus: false, - retry: false, - }, - }, - }, - // url: `${getBaseUrl() as string}/api/trpc`, - } - }, - /** - * @link https://trpc.io/docs/ssr - */ - ssr: true, - /** - * Set headers or status code when doing SSR - */ - responseMeta({ clientErrors }) { - if (clientErrors.length > 0) { - // propagate http first error from API calls - return { - status: clientErrors[0].data?.httpStatus ?? 500, - } - } - - // for app caching with SSR see https://trpc.io/docs/caching - - return {} - }, -})(MyApp) +export default trpc.withTRPC(MyApp) diff --git a/app/src/pages/login.tsx b/app/src/pages/login.tsx index c35b0cd..6cdfe7b 100644 --- a/app/src/pages/login.tsx +++ b/app/src/pages/login.tsx @@ -64,7 +64,7 @@ const Login: NextPageWithLayout = () => { return setError(status.error) } - void utils.invalidateQueries(['auth.account']) + void utils.auth.account.invalidate() loader.hide() diff --git a/app/src/pages/reset-password.tsx b/app/src/pages/reset-password.tsx index 42ef536..d2f8925 100644 --- a/app/src/pages/reset-password.tsx +++ b/app/src/pages/reset-password.tsx @@ -50,7 +50,7 @@ const ResetPassword: NextPageWithLayout<{ disabled: boolean }> = ({ disabled }) const { query, push } = useRouter() const setAlert = useAlert() - const { mutateAsync: resetPassword } = trpc.useMutation(['auth.reset_password'], { + const { mutateAsync: resetPassword } = trpc.auth.reset_password.useMutation({ onError(e) { setAlert({ type: 'danger', diff --git a/app/src/types/index.ts b/app/src/types/index.ts index 49dea03..e9b05e9 100644 --- a/app/src/types/index.ts +++ b/app/src/types/index.ts @@ -1,33 +1,20 @@ import { createTicketSchema, studentOnboardingSchema } from './../schemas/index' -import { TRPCClientErrorLike } from '@trpc/client' -import { UseTRPCQueryOptions } from '@trpc/react' -import { inferProcedureInput, inferProcedureOutput, ProcedureRecord } from '@trpc/server' -import type { AppRouter } from '@mirai/api' import { MLinkProps } from '../components/lib' import React from 'react' import type { z } from 'zod' +import { TRPCClientError } from '@trpc/client' +import { AppRouter } from '@mirai/api' -export type TRPCErrorType = TRPCClientErrorLike<AppRouter> +export type QueryOptions<Fn extends (...args: any[]) => any, RT = ReturnType<Fn>> = Fn extends ( + a: infer _A, + b: infer B, +) => RT + ? B + : Fn extends (a: infer A) => RT + ? A + : never -type inferProcedures<TObj extends ProcedureRecord<any, any, any, any>> = { - [TPath in keyof TObj]: { - input: inferProcedureInput<TObj[TPath]> - output: inferProcedureOutput<TObj[TPath]> - } -} - -type TQueryValues = inferProcedures<AppRouter['_def']['queries']> - -export type QueryOptions< - TPath extends keyof TQueryValues & string, - TError = TRPCClientErrorLike<AppRouter>, -> = UseTRPCQueryOptions< - TPath, - TQueryValues[TPath]['input'], - TQueryValues[TPath]['output'], - TQueryValues[TPath]['output'], - TError -> +export type TRPCErrorType = TRPCClientError<AppRouter> export type OverWrite<T, K> = Omit<T, keyof K> & K export type NullToUndefined<T> = T extends null ? undefined : T @@ -52,3 +39,5 @@ export type StudentTicketShape = OverWrite< > export type StudentProfileIgnore = 'verified' | 'verifiedBy' | 'verifiedOn' | 'createdAt' | 'updatedAt' + +export type AnyObject = Record<string, any> diff --git a/app/src/utils/hooks/index.ts b/app/src/utils/hooks/index.ts index c938b88..25c9403 100644 --- a/app/src/utils/hooks/index.ts +++ b/app/src/utils/hooks/index.ts @@ -5,7 +5,6 @@ import { useRouter } from 'next/router' import { RefCallback, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useEffectOnce } from 'react-use' import { themeAtom } from '../../stores' -import { TRPCErrorType } from '../../types' export * from './student' @@ -66,7 +65,7 @@ export function useStrictQueryCheck({ key, redirect, message, skipPath: skip }: export function useGlobalError() { const setAlert = useAlert() - const [globalError, setError] = useState<TRPCErrorType | null>(null) + const [globalError, setError] = useState<any | null>(null) useEffect(() => { if (globalError === null) return diff --git a/app/src/utils/trpc.ts b/app/src/utils/trpc.ts index e7fa8e4..dde3407 100644 --- a/app/src/utils/trpc.ts +++ b/app/src/utils/trpc.ts @@ -1,23 +1,67 @@ -import { createReactQueryHooks } from '@trpc/react' -import type { inferProcedureOutput } from '@trpc/server' import type { AppRouter } from '@mirai/api' import { loggerLink } from '@trpc/client/links/loggerLink' import { httpBatchLink } from '@trpc/client/links/httpBatchLink' import superjson from 'superjson' import { getBaseUrl } from './helpers' -import { createTRPCClient } from '@trpc/client' +import { createTRPCProxyClient } from '@trpc/client' +import { createTRPCNext } from '@trpc/next' /** * A set of strongly-typed React hooks from your `AppRouter` type signature with `createReactQueryHooks`. * @link https://trpc.io/docs/react#3-create-trpc-hooks */ -export const trpc = createReactQueryHooks<AppRouter>() +export const trpc = createTRPCNext<AppRouter>({ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + config() { + return { + links: [ + // adds pretty logs to your console in development and logs errors in production + loggerLink({ + enabled: (opts) => + process.env.NODE_ENV === 'development' || (opts.direction === 'down' && opts.result instanceof Error), + }), + httpBatchLink({ + url: `${getBaseUrl()}/api/trpc`, + }), + ], + transformer: superjson, + queryClientConfig: { + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: false, + }, + }, + }, + // url: `${getBaseUrl() as string}/api/trpc`, + } + }, + /** + * @link https://trpc.io/docs/ssr + */ + ssr: true, + /** + * Set headers or status code when doing SSR + */ + responseMeta({ clientErrors }) { + if (clientErrors.length > 0) { + // propagate http first error from API calls + return { + status: clientErrors[0].data?.httpStatus ?? 500, + } + } + + // for app caching with SSR see https://trpc.io/docs/caching + + return {} + }, +}) /** * This should be used where we don't need reactQuery state * intented to be used with delete mutations */ -export const trpcClient = createTRPCClient<AppRouter>({ +export const trpcClient = createTRPCProxyClient<AppRouter>({ links: [ // adds pretty logs to your console in development and logs errors in production loggerLink({ @@ -30,12 +74,3 @@ export const trpcClient = createTRPCClient<AppRouter>({ ], transformer: superjson, }) - -// export const transformer = superjson; -/** - * This is a helper method to infer the output of a query resolver - * @example type HelloOutput = inferQueryOutput<'hello'> - */ -export type inferQueryOutput<TRouteKey extends keyof AppRouter['_def']['queries']> = inferProcedureOutput< - AppRouter['_def']['queries'][TRouteKey] -> diff --git a/app/tsconfig.json b/app/tsconfig.json index 5c393ef..6eaead0 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -7,7 +7,8 @@ "types": ["unplugin-icons/types/react"], "incremental": true, "noEmit": true, - "strict": true + "strict": true, + "noImplicitAny": false }, "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.tsx", "auto-imports.d.ts", "../next-auth.d.ts"], "exclude": ["node_modules", ".next", "dist"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index caccce9..e124eba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,7 +96,7 @@ importers: '@mirai/app': workspace:* '@swc/core': '*' '@swc/helpers': ^0.3.17 - '@trpc/server': ^9.27.1 + '@trpc/server': 10.0.0-proxy-alpha.73 '@typegoose/typegoose': ^9.11.0 '@types/bull': ^3.15.9 '@types/ws': ^8.5.3 @@ -122,7 +122,7 @@ importers: '@fastify/cors': 7.0.0 '@fastify/websocket': 5.0.0 '@swc/helpers': 0.3.17 - '@trpc/server': 9.27.1 + '@trpc/server': 10.0.0-proxy-alpha.73 '@typegoose/typegoose': 9.11.0_mongoose@6.5.2 bull: 4.8.5 bullmq: 1.89.1 @@ -153,10 +153,12 @@ importers: '@prisma/client': '*' '@svgr/core': ^6.3.1 '@tailwindcss/typography': ^0.5.4 - '@trpc/client': ^9.27.1 - '@trpc/next': ^9.27.1 - '@trpc/react': ^9.27.1 - '@trpc/server': '*' + '@tanstack/react-query': ^4.2.3 + '@tanstack/react-query-devtools': ^4.2.3 + '@trpc/client': 10.0.0-proxy-alpha.73 + '@trpc/next': 10.0.0-proxy-alpha.73 + '@trpc/react': 10.0.0-proxy-alpha.73 + '@trpc/server': 10.0.0-proxy-alpha.73 '@types/react': ^17.0.47 '@types/react-dom': ^17.0.16 '@types/ws': ^8.5.3 @@ -176,7 +178,6 @@ importers: react: ^17.0.2 react-dom: ^17.0.2 react-hook-form: ^7.34.2 - react-query: ^3.39.2 react-shepherd: ^4.1.0 react-use: ^17.4.0 reconnecting-websocket: ^4.4.0 @@ -191,10 +192,12 @@ importers: '@hookform/resolvers': 2.9.7_react-hook-form@7.34.2 '@popperjs/core': 2.11.6 '@prisma/client': 4.2.1 - '@trpc/client': 9.27.1_@trpc+server@9.27.1 - '@trpc/next': 9.27.1_vdfoow2xw4mspn6i2346g6q57u - '@trpc/react': 9.27.1_f4divxdbitgtyxgulygbrgbomm - '@trpc/server': 9.27.1 + '@tanstack/react-query': 4.2.3_sfoxds7t5ydpegc3knd667wn6m + '@tanstack/react-query-devtools': 4.2.3_utcvct563i5sna5ghw7jrisjk4 + '@trpc/client': 10.0.0-proxy-alpha.73_7jrqolvqjpfojvwms5forbwovm + '@trpc/next': 10.0.0-proxy-alpha.73_l7hg6dxdre5oyuqnxi2inzo5vy + '@trpc/react': 10.0.0-proxy-alpha.73_p2lvow2qqjgwcbz4cqgw7pk5zy + '@trpc/server': 10.0.0-proxy-alpha.73 axios: 0.27.2 clsx: 1.2.1 daisyui: 2.24.0_25hquoklqeoqwmt7fwvvcyxm5e @@ -207,7 +210,6 @@ importers: react: 17.0.2 react-dom: 17.0.2_react@17.0.2 react-hook-form: 7.34.2_react@17.0.2 - react-query: 3.39.2_sfoxds7t5ydpegc3knd667wn6m react-shepherd: 4.1.0_sfoxds7t5ydpegc3knd667wn6m react-use: 17.4.0_sfoxds7t5ydpegc3knd667wn6m reconnecting-websocket: 4.4.0 @@ -457,6 +459,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.13.9 + dev: true /@babel/runtime/7.18.3: resolution: {integrity: sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==} @@ -1223,56 +1226,98 @@ packages: tailwindcss: 3.1.8_postcss@8.4.16 dev: true - /@trpc/client/9.27.1_@trpc+server@9.27.1: - resolution: {integrity: sha512-srh3s1ekhV5fssqxdE5DagcW+bnWUXp4LWkuHTunPDUL5BaIs6l0FXE4wKMv9AFAcCw7xJOFx8VSwFl/raXqKQ==} + /@tanstack/match-sorter-utils/8.1.1: + resolution: {integrity: sha512-IdmEekEYxQsoLOR0XQyw3jD1GujBpRRYaGJYQUw1eOT1eUugWxdc7jomh1VQ1EKHcdwDLpLaCz/8y4KraU4T9A==} + engines: {node: '>=12'} + dependencies: + remove-accents: 0.4.2 + dev: false + + /@tanstack/query-core/4.2.3: + resolution: {integrity: sha512-zdt5lYWs1dZaA3IxJbCgtAfHZJScRZONpiLL7YkeOkrme5MfjQqTpjq7LYbzpyuwPOh2Jx68le0PLl57JFv5hQ==} + dev: false + + /@tanstack/react-query-devtools/4.2.3_utcvct563i5sna5ghw7jrisjk4: + resolution: {integrity: sha512-0PH8n824BnFyMrtv7q5uLS0b7jYg2tDH8vU4etkSYzV1uL4RJjiqMh7Gyi8qhYCwM+khlrkRYlNZvE0cxlp3SQ==} peerDependencies: - '@trpc/server': 9.27.1 + '@tanstack/react-query': 4.2.3 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@babel/runtime': 7.18.9 - '@trpc/server': 9.27.1 + '@tanstack/match-sorter-utils': 8.1.1 + '@tanstack/react-query': 4.2.3_sfoxds7t5ydpegc3knd667wn6m + '@types/use-sync-external-store': 0.0.3 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + use-sync-external-store: 1.2.0_react@17.0.2 dev: false - /@trpc/next/9.27.1_vdfoow2xw4mspn6i2346g6q57u: - resolution: {integrity: sha512-eEo5ZJygODPIxzuH0iQzL2xsrggNq/bLF6F9OdLQ/wG8+VI4jUzCxDUNRt42rARAiZG4Y40jdPullMd/Kud4PA==} + /@tanstack/react-query/4.2.3_sfoxds7t5ydpegc3knd667wn6m: + resolution: {integrity: sha512-JLaMOxoJTkiAu7QpevRCt2uI/0vd3E8K/rSlCuRgWlcW5DeJDFpDS5kfzmLO5MOcD97fgsJRrDbxDORxR1FdJA==} peerDependencies: - '@trpc/client': 9.27.1 - '@trpc/react': 9.27.1 - '@trpc/server': 9.27.1 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@tanstack/query-core': 4.2.3 + '@types/use-sync-external-store': 0.0.3 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + use-sync-external-store: 1.2.0_react@17.0.2 + dev: false + + /@trpc/client/10.0.0-proxy-alpha.73_7jrqolvqjpfojvwms5forbwovm: + resolution: {integrity: sha512-k46rFBvqsdSzaQLYMdPckEE8uGMhWUx7SoQ4NpGBhCeI2bm+bb+V02wub1R3Gy63qEnCn6BY7+AetzTOn8H57Q==} + peerDependencies: + '@trpc/server': 10.0.0-proxy-alpha.73 + dependencies: + '@trpc/server': 10.0.0-proxy-alpha.73 + dev: false + + /@trpc/next/10.0.0-proxy-alpha.73_l7hg6dxdre5oyuqnxi2inzo5vy: + resolution: {integrity: sha512-nL24qCGcDjSfkke+4sv0pQ3C/bXp6EVw8pWVt1U3ZA3FfXnEa1QAb3/m9LM9F1dgxEGPY3yVizKqyvhyo975+g==} + peerDependencies: + '@tanstack/react-query': ^4.0.10 + '@trpc/client': 10.0.0-proxy-alpha.73 + '@trpc/react': 10.0.0-proxy-alpha.73 + '@trpc/server': 10.0.0-proxy-alpha.73 next: '*' react: '>=16.8.0' react-dom: '>=16.8.0' - react-query: ^3.37.0 dependencies: - '@babel/runtime': 7.18.9 - '@trpc/client': 9.27.1_@trpc+server@9.27.1 - '@trpc/react': 9.27.1_f4divxdbitgtyxgulygbrgbomm - '@trpc/server': 9.27.1 + '@tanstack/react-query': 4.2.3_sfoxds7t5ydpegc3knd667wn6m + '@trpc/client': 10.0.0-proxy-alpha.73_7jrqolvqjpfojvwms5forbwovm + '@trpc/react': 10.0.0-proxy-alpha.73_p2lvow2qqjgwcbz4cqgw7pk5zy + '@trpc/server': 10.0.0-proxy-alpha.73 next: 12.2.5_cmpys7xj2wztdwvidtjx65lbde react: 17.0.2 react-dom: 17.0.2_react@17.0.2 - react-query: 3.39.2_sfoxds7t5ydpegc3knd667wn6m react-ssr-prepass: 1.5.0_react@17.0.2 dev: false - /@trpc/react/9.27.1_f4divxdbitgtyxgulygbrgbomm: - resolution: {integrity: sha512-EX7GaZWvzF9WoyBDfO+54wV52x1nr31hMhnZiEKsZFgchnzjpIkYg0Q4XfZ/Nz0Puyn0Wf6ksMChbTXd0cTqSg==} + /@trpc/react/10.0.0-proxy-alpha.73_p2lvow2qqjgwcbz4cqgw7pk5zy: + resolution: {integrity: sha512-BwKjETA/RXFlzW6RD+0dEUt6kh5/+ozt+7q3g8hGCja0qN5h8tr1aYkg8E/B/7Li5g/Lsdz55DBHIBe1TYS1ew==} peerDependencies: - '@trpc/client': 9.27.1 - '@trpc/server': 9.27.1 + '@tanstack/react-query': ^4.0.10 + '@trpc/client': 10.0.0-proxy-alpha.73 + '@trpc/server': 10.0.0-proxy-alpha.73 react: '>=16.8.0' react-dom: '>=16.8.0' - react-query: ^3.37.0 dependencies: - '@babel/runtime': 7.18.9 - '@trpc/client': 9.27.1_@trpc+server@9.27.1 - '@trpc/server': 9.27.1 + '@tanstack/react-query': 4.2.3_sfoxds7t5ydpegc3knd667wn6m + '@trpc/client': 10.0.0-proxy-alpha.73_7jrqolvqjpfojvwms5forbwovm + '@trpc/server': 10.0.0-proxy-alpha.73 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 - react-query: 3.39.2_sfoxds7t5ydpegc3knd667wn6m dev: false - /@trpc/server/9.27.1: - resolution: {integrity: sha512-r/Fg3DuuYhIvXx5ZSmQ+VVyeV8kZ+N5aa6B7KkDg5wY+o7c1kDygCl+ZJ8XgwNg+yM2JPXPZxetWmyUw/YvWNQ==} + /@trpc/server/10.0.0-proxy-alpha.73: + resolution: {integrity: sha512-gKCciEvgfBfaAfMoexZnIlrectauOOZIiNJq50ZSFC9PjwsL+JoEKyviqsRUAE9ny+lOkQ1h60ccAk20ssMgMA==} dev: false /@tsconfig/node10/1.0.9: @@ -1393,6 +1438,10 @@ packages: resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} dev: false + /@types/use-sync-external-store/0.0.3: + resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==} + dev: false + /@types/webidl-conversions/6.1.1: resolution: {integrity: sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==} dev: false @@ -1830,11 +1879,6 @@ packages: resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==} dev: false - /big-integer/1.6.51: - resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} - engines: {node: '>=0.6'} - dev: false - /big.js/5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} dev: true @@ -1861,19 +1905,6 @@ packages: dependencies: fill-range: 7.0.1 - /broadcast-channel/3.7.0: - resolution: {integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==} - dependencies: - '@babel/runtime': 7.17.9 - detect-node: 2.1.0 - js-sha3: 0.8.0 - microseconds: 0.2.0 - nano-time: 1.0.0 - oblivious-set: 1.0.0 - rimraf: 3.0.2 - unload: 2.2.0 - dev: false - /browserslist/4.21.3: resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -2375,10 +2406,6 @@ packages: resolution: {integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=} dev: false - /detect-node/2.1.0: - resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - dev: false - /detective/5.2.1: resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} engines: {node: '>=0.8.0'} @@ -3856,10 +3883,6 @@ packages: resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} dev: false - /js-sha3/0.8.0: - resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} - dev: false - /js-tokens/4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4203,13 +4226,6 @@ packages: /make-error/1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - /match-sorter/6.3.1: - resolution: {integrity: sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==} - dependencies: - '@babel/runtime': 7.17.9 - remove-accents: 0.4.2 - dev: false - /mdn-data/2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} dev: false @@ -4239,10 +4255,6 @@ packages: braces: 3.0.2 picomatch: 2.3.1 - /microseconds/0.2.0: - resolution: {integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==} - dev: false - /mime-db/1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -4476,12 +4488,6 @@ packages: stylis: 4.1.1 dev: false - /nano-time/1.0.0: - resolution: {integrity: sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=} - dependencies: - big-integer: 1.6.51 - dev: false - /nanoid/3.3.4: resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -4732,10 +4738,6 @@ packages: es-abstract: 1.20.1 dev: true - /oblivious-set/1.0.0: - resolution: {integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==} - dev: false - /oidc-token-hash/5.0.1: resolution: {integrity: sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==} engines: {node: ^10.13.0 || >=12.0.0} @@ -5263,25 +5265,6 @@ packages: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true - /react-query/3.39.2_sfoxds7t5ydpegc3knd667wn6m: - resolution: {integrity: sha512-F6hYDKyNgDQfQOuR1Rsp3VRzJnWHx6aRnnIZHMNGGgbL3SBgpZTDg8MQwmxOgpCAoqZJA+JSNCydF1xGJqKOCA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - dependencies: - '@babel/runtime': 7.17.9 - broadcast-channel: 3.7.0 - match-sorter: 6.3.1 - react: 17.0.2 - react-dom: 17.0.2_react@17.0.2 - dev: false - /react-shepherd/4.1.0_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-g+SCP5DDLFEqM/Q+ted6/yMNf2fpHvoyq6GDGWjDk1IlvfAcIshW/BulKjqkq5tAibd0bUpDFSGCYn3o7GWE+g==} engines: {node: '>=14', npm: '>=7'} @@ -6309,13 +6292,6 @@ packages: - webpack dev: true - /unload/2.2.0: - resolution: {integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==} - dependencies: - '@babel/runtime': 7.17.9 - detect-node: 2.1.0 - dev: false - /unplugin-auto-import/0.11.2: resolution: {integrity: sha512-1+VwBfn9dtiYv9SQLKP1AvZolUbK9xTVeAT+iOcEk4EHSFUlmIqBVLEKI76cifSQTLOJ3rZyPrEgptf3SZNLlQ==} engines: {node: '>=14'}