fix: improved person fetching (#3161)

This commit is contained in:
Dhruwang Jariwala
2024-09-20 10:39:22 +05:30
committed by GitHub
parent 0b553447e0
commit e4fceb2e5e
8 changed files with 46 additions and 48 deletions
@@ -1,14 +1,11 @@
"use client"; "use client";
import { import { getPersonsAction } from "@/app/(app)/environments/[environmentId]/(people)/people/actions";
getPersonAttributesAction,
getPersonsAction,
} from "@/app/(app)/environments/[environmentId]/(people)/people/actions";
import { PersonTable } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonTable"; import { PersonTable } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonTable";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import React from "react"; import React from "react";
import { TEnvironment } from "@formbricks/types/environment"; import { TEnvironment } from "@formbricks/types/environment";
import { TPerson, TPersonTableData } from "@formbricks/types/people"; import { TPersonWithAttributes } from "@formbricks/types/people";
interface PersonDataViewProps { interface PersonDataViewProps {
environment: TEnvironment; environment: TEnvironment;
@@ -17,8 +14,7 @@ interface PersonDataViewProps {
} }
export const PersonDataView = ({ environment, personCount, itemsPerPage }: PersonDataViewProps) => { export const PersonDataView = ({ environment, personCount, itemsPerPage }: PersonDataViewProps) => {
const [persons, setPersons] = useState<TPerson[]>([]); const [persons, setPersons] = useState<TPersonWithAttributes[]>([]);
const [personTableData, setPersonTableData] = useState<TPersonTableData[]>([]);
const [pageNumber, setPageNumber] = useState<number>(1); const [pageNumber, setPageNumber] = useState<number>(1);
const [totalPersons, setTotalPersons] = useState<number>(0); const [totalPersons, setTotalPersons] = useState<number>(0);
const [isDataLoaded, setIsDataLoaded] = useState<boolean>(false); const [isDataLoaded, setIsDataLoaded] = useState<boolean>(false);
@@ -40,41 +36,13 @@ export const PersonDataView = ({ environment, personCount, itemsPerPage }: Perso
} }
} catch (error) { } catch (error) {
console.error("Error fetching people data:", error); console.error("Error fetching people data:", error);
}
};
fetchData();
}, [pageNumber, personCount, itemsPerPage, environment.id]);
// Fetch additional person attributes and update table data
useEffect(() => {
const fetchAttributes = async () => {
try {
const updatedPersonTableData = await Promise.all(
persons.map(async (person) => {
const attributes = await getPersonAttributesAction({
environmentId: environment.id,
personId: person.id,
});
return {
createdAt: person.createdAt,
personId: person.id,
userId: person.userId,
email: attributes?.data?.email ?? "",
attributes: attributes?.data ?? {},
};
})
);
setPersonTableData(updatedPersonTableData);
} catch (error) {
console.error("Error fetching person attributes:", error);
} finally { } finally {
setIsDataLoaded(true); setIsDataLoaded(true);
} }
}; };
fetchAttributes(); fetchData();
}, [persons, environment.id]); }, [pageNumber, personCount, itemsPerPage, environment.id]);
const fetchNextPage = async () => { const fetchNextPage = async () => {
if (hasMore && !loadingNextPage) { if (hasMore && !loadingNextPage) {
@@ -97,6 +65,15 @@ export const PersonDataView = ({ environment, personCount, itemsPerPage }: Perso
setPersons((prevPersons) => prevPersons.filter((p) => !personIds.includes(p.id))); setPersons((prevPersons) => prevPersons.filter((p) => !personIds.includes(p.id)));
}; };
const personTableData = persons.map((person) => ({
id: person.id,
userId: person.userId,
email: person.attributes.email,
createdAt: person.createdAt,
attributes: person.attributes,
personId: person.id,
}));
return ( return (
<PersonTable <PersonTable
data={personTableData} data={personTableData}
@@ -18,6 +18,7 @@ import { cn } from "@formbricks/lib/cn";
import { TPersonTableData } from "@formbricks/types/people"; import { TPersonTableData } from "@formbricks/types/people";
import { Button } from "@formbricks/ui/Button"; import { Button } from "@formbricks/ui/Button";
import { DataTableHeader, DataTableSettingsModal, DataTableToolbar } from "@formbricks/ui/DataTable"; import { DataTableHeader, DataTableSettingsModal, DataTableToolbar } from "@formbricks/ui/DataTable";
import { getCommonPinningStyles } from "@formbricks/ui/DataTable/lib/utils";
import { Skeleton } from "@formbricks/ui/Skeleton"; import { Skeleton } from "@formbricks/ui/Skeleton";
import { Table, TableBody, TableCell, TableHeader, TableRow } from "@formbricks/ui/Table"; import { Table, TableBody, TableCell, TableHeader, TableRow } from "@formbricks/ui/Table";
@@ -157,7 +158,7 @@ export const PersonTable = ({
deleteRows={deletePersons} deleteRows={deletePersons}
type="person" type="person"
/> />
<div className="w-fit max-w-full overflow-hidden overflow-x-auto rounded-xl border border-slate-300"> <div className="w-fit max-w-full overflow-hidden overflow-x-auto rounded-xl border border-slate-200">
<div className="w-full overflow-x-auto"> <div className="w-full overflow-x-auto">
<Table style={{ width: table.getCenterTotalSize(), tableLayout: "fixed" }}> <Table style={{ width: table.getCenterTotalSize(), tableLayout: "fixed" }}>
<TableHeader> <TableHeader>
@@ -189,8 +190,9 @@ export const PersonTable = ({
if (cell.column.id === "select") return; if (cell.column.id === "select") return;
router.push(`/environments/${environmentId}/people/${row.id}`); router.push(`/environments/${environmentId}/people/${row.id}`);
}} }}
style={cell.column.id === "select" ? getCommonPinningStyles(cell.column) : {}}
className={cn( className={cn(
"border-slate-300 bg-white shadow-none group-hover:bg-slate-100", "border-slate-200 bg-white shadow-none group-hover:bg-slate-100",
row.getIsSelected() && "bg-slate-100", row.getIsSelected() && "bg-slate-100",
{ {
"border-r": !cell.column.getIsLastColumn(), "border-r": !cell.column.getIsLastColumn(),
@@ -1,6 +1,7 @@
import { PersonDataView } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonDataView"; import { PersonDataView } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonDataView";
import { PersonSecondaryNavigation } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonSecondaryNavigation"; import { PersonSecondaryNavigation } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonSecondaryNavigation";
import { CircleHelpIcon } from "lucide-react"; import { CircleHelpIcon } from "lucide-react";
import { ITEMS_PER_PAGE } from "@formbricks/lib/constants";
import { getEnvironment } from "@formbricks/lib/environment/service"; import { getEnvironment } from "@formbricks/lib/environment/service";
import { getPersonCount } from "@formbricks/lib/person/service"; import { getPersonCount } from "@formbricks/lib/person/service";
import { Button } from "@formbricks/ui/Button"; import { Button } from "@formbricks/ui/Button";
@@ -31,7 +32,7 @@ const Page = async ({ params }: { params: { environmentId: string } }) => {
<PageHeader pageTitle="People" cta={HowToAddPeopleButton}> <PageHeader pageTitle="People" cta={HowToAddPeopleButton}>
<PersonSecondaryNavigation activeId="people" environmentId={params.environmentId} /> <PersonSecondaryNavigation activeId="people" environmentId={params.environmentId} />
</PageHeader> </PageHeader>
<PersonDataView environment={environment} personCount={personCount} itemsPerPage={25} /> <PersonDataView environment={environment} personCount={personCount} itemsPerPage={ITEMS_PER_PAGE} />
</PageContentWrapper> </PageContentWrapper>
); );
}; };
@@ -176,7 +176,7 @@ export const ResponseTable = ({
deleteRows={deleteResponses} deleteRows={deleteResponses}
type="response" type="response"
/> />
<div className="w-fit max-w-full overflow-hidden overflow-x-auto rounded-xl border border-slate-300"> <div className="w-fit max-w-full overflow-hidden overflow-x-auto rounded-xl border border-slate-200">
<div className="w-full overflow-x-auto"> <div className="w-full overflow-x-auto">
<Table <Table
style={{ style={{
@@ -46,7 +46,7 @@ export const ResponseTableCell = ({
<TableCell <TableCell
key={cell.id} key={cell.id}
className={cn( className={cn(
"border-slate-300 bg-white shadow-none group-hover:bg-slate-100", "border-slate-200 bg-white shadow-none group-hover:bg-slate-100",
row.getIsSelected() && "bg-slate-100", row.getIsSelected() && "bg-slate-100",
{ {
"border-r": !cell.column.getIsLastColumn(), "border-r": !cell.column.getIsLastColumn(),
+17 -5
View File
@@ -5,7 +5,7 @@ import { prisma } from "@formbricks/database";
import { ZOptionalNumber, ZString } from "@formbricks/types/common"; import { ZOptionalNumber, ZString } from "@formbricks/types/common";
import { ZId } from "@formbricks/types/common"; import { ZId } from "@formbricks/types/common";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors"; import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { TPerson } from "@formbricks/types/people"; import { TPerson, TPersonWithAttributes } from "@formbricks/types/people";
import { cache } from "../cache"; import { cache } from "../cache";
import { ITEMS_PER_PAGE } from "../constants"; import { ITEMS_PER_PAGE } from "../constants";
import { validateInputs } from "../utils/validate"; import { validateInputs } from "../utils/validate";
@@ -17,6 +17,16 @@ export const selectPerson = {
createdAt: true, createdAt: true,
updatedAt: true, updatedAt: true,
environmentId: true, environmentId: true,
attributes: {
select: {
value: true,
attributeClass: {
select: {
name: true,
},
},
},
},
}; };
type TransformPersonInput = { type TransformPersonInput = {
@@ -33,7 +43,7 @@ type TransformPersonInput = {
updatedAt: Date; updatedAt: Date;
}; };
export const transformPrismaPerson = (person: TransformPersonInput): TPerson => { export const transformPrismaPerson = (person: TransformPersonInput): TPersonWithAttributes => {
const attributes = person.attributes.reduce( const attributes = person.attributes.reduce(
(acc, attr) => { (acc, attr) => {
acc[attr.attributeClass.name] = attr.value; acc[attr.attributeClass.name] = attr.value;
@@ -49,7 +59,7 @@ export const transformPrismaPerson = (person: TransformPersonInput): TPerson =>
environmentId: person.environmentId, environmentId: person.environmentId,
createdAt: new Date(person.createdAt), createdAt: new Date(person.createdAt),
updatedAt: new Date(person.updatedAt), updatedAt: new Date(person.updatedAt),
} as TPerson; } as TPersonWithAttributes;
}; };
export const getPerson = reactCache( export const getPerson = reactCache(
@@ -81,13 +91,13 @@ export const getPerson = reactCache(
); );
export const getPeople = reactCache( export const getPeople = reactCache(
(environmentId: string, page?: number): Promise<TPerson[]> => (environmentId: string, page?: number): Promise<TPersonWithAttributes[]> =>
cache( cache(
async () => { async () => {
validateInputs([environmentId, ZId], [page, ZOptionalNumber]); validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
try { try {
return await prisma.person.findMany({ const persons = await prisma.person.findMany({
where: { where: {
environmentId: environmentId, environmentId: environmentId,
}, },
@@ -95,6 +105,8 @@ export const getPeople = reactCache(
take: page ? ITEMS_PER_PAGE : undefined, take: page ? ITEMS_PER_PAGE : undefined,
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined, skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
}); });
return persons.map((person) => transformPrismaPerson(person));
} catch (error) { } catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) { if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message); throw new DatabaseError(error.message);
+6
View File
@@ -16,6 +16,12 @@ export const ZPersonTableData = z.object({
attributes: ZAttributes, attributes: ZAttributes,
}); });
export const ZPersonWithAttributes = ZPerson.extend({
attributes: ZAttributes,
});
export type TPersonWithAttributes = z.infer<typeof ZPersonWithAttributes>;
export type TPersonTableData = z.infer<typeof ZPersonTableData>; export type TPersonTableData = z.infer<typeof ZPersonTableData>;
export type TPerson = z.infer<typeof ZPerson>; export type TPerson = z.infer<typeof ZPerson>;
@@ -36,7 +36,7 @@ export const DataTableHeader = <T,>({ header, setIsTableSettingsModalOpen }: Dat
ref={setNodeRef} ref={setNodeRef}
style={style} style={style}
key={header.id} key={header.id}
className={cn("group relative h-10 border-b border-slate-300 bg-white px-4 text-center", { className={cn("group relative h-10 border-b border-slate-200 bg-white px-4 text-center", {
"border-r": !header.column.getIsLastColumn(), "border-r": !header.column.getIsLastColumn(),
"border-l": !header.column.getIsFirstColumn(), "border-l": !header.column.getIsFirstColumn(),
})}> })}>