Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add server side pagination and filteration #503

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6175a7f
feat: add data table hook
G3root Aug 28, 2024
9267b5a
feat: add new data table hook
G3root Aug 28, 2024
c8ae1ac
chore: rename hook
G3root Aug 28, 2024
a0106f4
feat: add paginated hook
G3root Aug 28, 2024
99ff880
feat: add offset schema
G3root Aug 28, 2024
07f2d55
feat: use offset schema
G3root Aug 28, 2024
5c8a3d3
fix: session
G3root Aug 28, 2024
1238f71
feat: add const
G3root Aug 28, 2024
612238a
chore: fix error handling
G3root Aug 28, 2024
733b70d
chore: make header optional
G3root Aug 28, 2024
36c9a72
feat: add api client
G3root Aug 28, 2024
bcede0d
chore: add nuqs
G3root Aug 28, 2024
399b222
feat: use pagination
G3root Aug 28, 2024
b207ff0
refactor: hook
G3root Aug 29, 2024
fd34967
feat: add sort
G3root Aug 29, 2024
e7e7449
feat: add sort
G3root Aug 29, 2024
85116f3
chore: hide select
G3root Aug 29, 2024
4ddcf2a
feat: add search filter
G3root Aug 29, 2024
9851bf3
feat: refactor data
G3root Aug 29, 2024
69a423e
feat: add debounce component
G3root Aug 29, 2024
2409c0d
feat: add debounce input
G3root Aug 29, 2024
3523911
feat: add search
G3root Aug 30, 2024
8810540
fix: query
G3root Aug 30, 2024
5acdd81
feat: update tables
G3root Aug 30, 2024
6c5d5f1
feat: add seed
G3root Aug 30, 2024
2fe228f
Merge branch 'main' into pagination-table
G3root Aug 30, 2024
92d651d
chore: add tax id
G3root Sep 2, 2024
e733531
chore: upgrade trpc
G3root Sep 2, 2024
fcf2db6
feat: use new trpc
G3root Sep 2, 2024
9ca66a6
feat: add error boundary
G3root Sep 2, 2024
233b004
chore: rename loading
G3root Sep 2, 2024
33ba09b
feat: use useSuspense
G3root Sep 2, 2024
b91f92d
fix: data
G3root Sep 2, 2024
c477f15
feat: add search param hook
G3root Sep 17, 2024
160cf3d
feat: add search
G3root Sep 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@
"@sindresorhus/slugify": "^2.2.1",
"@stripe/stripe-js": "^4.1.0",
"@t3-oss/env-nextjs": "^0.10.1",
"@tanstack/react-query": "^4.36.1",
"@tanstack/react-query": "^5.53.3",
"@tanstack/react-table": "^8.20.1",
"@tremor/react": "^3.17.4",
"@trpc/client": "^10.43.6",
"@trpc/next": "^10.45.2",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upgraded to trpc 11 to useSuspenseQuery from "@tanstack/react-query". useSuspenseQuery is introduced in "@tanstack/react-query v5 upgrading only this package breaks trpc

"@trpc/react-query": "^10.43.6",
"@trpc/server": "^10.43.6",
"@trpc/client": "11.0.0-rc.498",
"@trpc/next": "11.0.0-rc.498",
"@trpc/react-query": "11.0.0-rc.498",
"@trpc/server": "11.0.0-rc.498",
"@types/bcryptjs": "^2.4.6",
"@types/papaparse": "^5.3.14",
"@wojtekmaj/react-hooks": "^1.20.0",
Expand All @@ -94,6 +94,7 @@
"next-auth": "^4.24.7",
"next-nprogress-bar": "^2.3.13",
"nodemailer": "^6.9.14",
"nuqs": "^1.17.8",
"papaparse": "^5.4.1",
"pdf-lib": "^1.17.1",
"pg-boss": "^9.0.3",
Expand All @@ -106,6 +107,7 @@
"react-dom": "18.2.0",
"react-dropzone": "^14.2.3",
"react-email": "2.1.6",
"react-error-boundary": "^4.0.13",
"react-hook-form": "^7.52.1",
"react-number-format": "^5.3.4",
"react-pdf": "^8.0.2",
Expand Down Expand Up @@ -146,7 +148,7 @@
"prisma": "^5.13.0",
"tailwindcss": "^3.4.3",
"tsx": "^4.7.0",
"typescript": "^5.4.5",
"typescript": "^5.5.3",
"vitest": "^1.6.0"
},
"ct3aMetadata": {
Expand Down
379 changes: 196 additions & 183 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions prisma/seeds/companies.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { generatePublicId } from "@/common/id";
import { db } from "@/server/db";
import type { TPrismaOrTransaction } from "@/server/db";
import { faker } from "@faker-js/faker";
import colors from "colors";
import { sample } from "lodash-es";
Expand All @@ -19,7 +19,7 @@ type CompanyType = {
zipcode: string;
country: string;
};
const seedCompanies = async (count = 4) => {
const seedCompanies = async (tx: TPrismaOrTransaction, count = 4) => {
const companies: CompanyType[] = [];

for (let i = 0; i < count; i++) {
Expand All @@ -41,11 +41,15 @@ const seedCompanies = async (count = 4) => {

console.log(`Seeding ${companies.length} companies`.blue);

const records = await db.company.createMany({
const records = await tx.company.createManyAndReturn({
data: companies,
select: {
id: true,
name: true,
},
});

console.log(`🎉 Seeded ${records.count} companies`.green);
console.log(`🎉 Seeded ${records.length} companies`.green);
return records;
};

Expand Down
99 changes: 70 additions & 29 deletions prisma/seeds/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import colors from "colors";
import inquirer from "inquirer";
colors.enable();

import { Prisma } from "@prisma/client";
import type { QuestionCollection } from "inquirer";
import seedCompanies from "./companies";
import { seedStakeholders } from "./stakeholder";
import seedTeam from "./team";

if (process.env.NODE_ENV === "production") {
Expand All @@ -23,47 +25,86 @@ const seed = async () => {
const answer = inquiry.answer as boolean;

if (answer) {
await nuke();
await cleanupDb();

console.log("Seeding database".underline.cyan);
return db.$transaction(async () => {
await seedCompanies();
await seedTeam();
return db.$transaction(async (tx) => {
const companies = await seedCompanies(tx);
await seedTeam(tx);

for (const company of companies) {
await seedStakeholders(tx, company.id, company.name, 500);
}
});
} else {
throw new Error("Seeding aborted");
}
};

const nuke = async () => {
console.log("🚀 Nuking database records".yellow);
return db.$transaction(async (db) => {
await db.user.deleteMany();
await db.member.deleteMany();
await db.company.deleteMany();
await db.shareClass.deleteMany();
await db.equityPlan.deleteMany();
await db.document.deleteMany();
await db.bucket.deleteMany();
await db.audit.deleteMany();
await db.session.deleteMany();
});
throw new Error("Seeding aborted");
};

await seed()
.then(async () => {
.then(() => {
console.log("✅ Database seeding completed".green);
console.log(
`💌 We have created four admin accounts for you. Please login with one of these emails:\n`
"💌 We have created four admin accounts for you. Please login with one of these emails:\n"
.cyan,
`[email protected]\n`.underline.yellow,
`[email protected]\n`.underline.yellow,
`[email protected]\n`.underline.yellow,
`[email protected]\n`.underline.yellow,
"[email protected]\n".underline.yellow,
"[email protected]\n".underline.yellow,
"[email protected]\n".underline.yellow,
"[email protected]\n".underline.yellow,
);
await db.$disconnect();
})
.catch(async (error: Error) => {
.catch((error: Error) => {
console.log(`❌ ${error.message}`.red);
})
.finally(async () => {
await db.$disconnect();
});

export async function cleanupDb() {
console.log("🚀 Nuking database records".yellow);

try {
const tables = await db.$queryRaw<{ tablename: string }[]>`
SELECT tablename FROM pg_tables
WHERE schemaname = 'public' AND tablename != '_prisma_migrations'
`;

// Disable foreign key checks
await db.$executeRaw`SET CONSTRAINTS ALL DEFERRED`;

for (const { tablename } of tables) {
try {
// Check if the table exists before attempting to truncate
const tableExists = await db.$queryRaw<[{ exists: boolean }]>`
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public' AND table_name = ${tablename}
)
`;

if (tableExists[0].exists) {
await db.$executeRaw(
Prisma.sql`TRUNCATE TABLE "${Prisma.raw(tablename)}" CASCADE`,
);
console.log(`Table ${tablename} truncated successfully`.green);
} else {
console.log(`Table ${tablename} doesn't exist, skipping`.yellow);
}
} catch (err) {
if (err instanceof Prisma.PrismaClientKnownRequestError) {
console.error(`Error truncating table ${tablename}:`, err.message);
} else {
console.error(`Unexpected error truncating table ${tablename}:`, err);
}
}
}

// Re-enable foreign key checks
await db.$executeRaw`SET CONSTRAINTS ALL IMMEDIATE`;

console.log(
"All tables reset successfully, except _prisma_migrations".yellow,
);
} catch (error) {
console.error("Error resetting tables:", error);
}
}
45 changes: 45 additions & 0 deletions prisma/seeds/stakeholder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { TPrismaOrTransaction } from "@/server/db";
import { faker } from "@faker-js/faker";
import colors from "colors";
import { StakeholderRelationshipEnum, StakeholderTypeEnum } from "../enums";
colors.enable();

export async function seedStakeholders(
tx: TPrismaOrTransaction,
companyId: string,
companyName: string,
count = 100,
) {
const stakeholders = [];

for (let index = 0; index < count; index++) {
stakeholders.push({
name: faker.person.fullName(),
email: faker.internet.email(),
institutionName: faker.company.name(),
stakeholderType: faker.helpers.arrayElement(
Object.values(StakeholderTypeEnum),
),
currentRelationship: faker.helpers.arrayElement(
Object.values(StakeholderRelationshipEnum),
),
taxId: faker.finance.accountNumber(),

streetAddress: faker.location.streetAddress(),
city: faker.location.city(),
state: faker.location.state(),
zipcode: faker.location.zipCode(),
country: faker.location.country(),
companyId,
});
}
console.log(
`Seeding ${stakeholders.length} stakeholders for ${companyName}`.blue,
);

const record = await tx.stakeholder.createMany({ data: stakeholders });

console.log(
`🎉 Seeded ${record.count} stakeholders for ${companyName}`.green,
);
}
13 changes: 8 additions & 5 deletions prisma/seeds/team.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { MemberStatusEnum } from "@/prisma/enums";
import { db } from "@/server/db";
import type { TPrismaOrTransaction } from "@/server/db";

import { faker } from "@faker-js/faker";
import bcrypt from "bcryptjs";
import colors from "colors";
Expand All @@ -14,7 +15,7 @@ type UserType = {
status?: MemberStatusEnum;
};

const seedTeam = async () => {
const seedTeam = async (tx: TPrismaOrTransaction) => {
const team = [
{
name: faker.person.fullName(),
Expand Down Expand Up @@ -60,14 +61,15 @@ const seedTeam = async () => {
];

console.log(`Seeding ${team.length} team members`.blue);
const companies = await db.company.findMany();
const companies = await tx.company.findMany();

// biome-ignore lint/complexity/noForEach: <explanation>
team.forEach(async (t) => {
// const { name, email, image, title, status, isOnboarded } = t
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash("P@ssw0rd!", salt);
const { name, email, title, status, isOnboarded } = t;
const user = await db.user.create({
const user = await tx.user.create({
data: {
name,
email,
Expand All @@ -77,8 +79,9 @@ const seedTeam = async () => {
},
});

// biome-ignore lint/complexity/noForEach: <explanation>
companies.forEach(async (company) => {
await db.member.create({
await tx.member.create({
data: {
title,
isOnboarded,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const AuditsPage = async () => {
return <UnAuthorizedState />;
}

const audits = await api.audit.getAudits.query({});
const audits = await api.audit.getAudits({});
return (
<div className="flex flex-col gap-y-3">
<div className="flex items-center justify-between gap-y-3 ">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ const DataRoomSettinsPage = async ({
}: {
params: { publicId: string; dataRoomPublicId: string };
}) => {
const { dataRoom, documents } = await api.dataRoom.getDataRoom.query({
const { dataRoom, documents } = await api.dataRoom.getDataRoom({
dataRoomPublicId,
include: {
company: false,
recipients: true,
documents: true,
},
});
const contacts = await api.common.getContacts.query();
const contacts = await api.common.getContacts();

if (!dataRoom) {
return notFound();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ const EsignTemplateDetailPage = async ({
}) => {
const session = await withServerComponentSession();

const { name, status, url, fields, recipients } =
await api.template.get.query({
publicId: templatePublicId,
isDraftOnly: true,
});
const { name, status, url, fields, recipients } = await api.template.get({
publicId: templatePublicId,
isDraftOnly: true,
});

return (
<TemplateFieldProvider recipients={recipients} fields={fields}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const metadata: Metadata = {

const EsignDocumentPage = async () => {
const session = await withServerComponentSession();
const { documents } = await api.template.all.query();
const { documents } = await api.template.all();

if (documents.length === 0) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ export default async function TemplateDetailViewPage({
const { allow } = await serverAccessControl();

const [{ name, status, url, fields }, auditsData] = await Promise.all([
api.template.get.query({
api.template.get({
publicId: templatePublicId,
isDraftOnly: false,
}),

allow(
api.audit.allEsignAudits.query({
api.audit.allEsignAudits({
templatePublicId: templatePublicId,
}),
["audits", "read"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ const DocumentsPage = async () => {
const { allow } = await serverAccessControl();
const session = await withServerComponentSession();

const documents = await allow(api.document.getAll.query(), [
"documents",
"read",
]);
const documents = await allow(api.document.getAll(), ["documents", "read"]);

const canUpload = allow(true, ["documents", "read"]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const metadata: Metadata = {
};

const DocumentsPage = async () => {
const documents = await api.document.getAll.query();
const documents = await api.document.getAll();
const session = await withServerComponentSession();

if (documents.length === 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const metadata: Metadata = {
};

const SafePage = async () => {
const safes = await api.safe.getSafes.query();
const safes = await api.safe.getSafes();

if (!safes?.data?.length) {
return (
Expand Down
Loading