Skip to content

Commit

Permalink
feat: Data table for persons (formbricks#3154)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dhruwang authored Sep 19, 2024
1 parent fe9746b commit 6b64367
Show file tree
Hide file tree
Showing 38 changed files with 758 additions and 344 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
"use client";

import { getQuestionDefaults, questionTypes, universalQuestionPresets } from "@/app/lib/questions";
import { createId } from "@paralleldrive/cuid2";
import * as Collapsible from "@radix-ui/react-collapsible";
import { PlusIcon } from "lucide-react";
import { useState } from "react";
import { cn } from "@formbricks/lib/cn";
import {
getQuestionDefaults,
questionTypes,
universalQuestionPresets,
} from "@formbricks/lib/utils/questions";
import { TProduct } from "@formbricks/types/product";

interface AddQuestionButtonProps {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"use client";

import { QUESTIONS_ICON_MAP, QUESTIONS_NAME_MAP, getQuestionDefaults } from "@/app/lib/questions";
import { createId } from "@paralleldrive/cuid2";
import { ArrowDownIcon, ArrowUpIcon, CopyIcon, EllipsisIcon, TrashIcon } from "lucide-react";
import { useState } from "react";
import { cn } from "@formbricks/lib/cn";
import { QUESTIONS_ICON_MAP, QUESTIONS_NAME_MAP, getQuestionDefaults } from "@formbricks/lib/utils/questions";
import { TProduct } from "@formbricks/types/product";
import {
TSurvey,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import { RankingQuestionForm } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/RankingQuestionForm";
import { formatTextWithSlashes } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/lib/util";
import { QUESTIONS_ICON_MAP, getTSurveyQuestionTypeEnumName } from "@/app/lib/questions";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import * as Collapsible from "@radix-ui/react-collapsible";
import { ChevronDownIcon, ChevronRightIcon, GripIcon } from "lucide-react";
import { useState } from "react";
import { cn } from "@formbricks/lib/cn";
import { QUESTIONS_ICON_MAP, getTSurveyQuestionTypeEnumName } from "@formbricks/lib/utils/questions";
import { recallToHeadline } from "@formbricks/lib/utils/recall";
import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TProduct } from "@formbricks/types/product";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ export const SurveyMenuBar = ({
};
}, [localSurvey, survey]);

const clearSurveyLocalStorage = () => {
if (typeof localStorage !== "undefined") {
localStorage.removeItem(`${localSurvey.id}-columnOrder`);
localStorage.removeItem(`${localSurvey.id}-columnVisibility`);
}
};

const containsEmptyTriggers = useMemo(() => {
if (localSurvey.type === "link") return false;

Expand Down Expand Up @@ -233,6 +240,7 @@ export const SurveyMenuBar = ({
}

const segment = await handleSegmentUpdate();
clearSurveyLocalStorage();
const updatedSurveyResponse = await updateSurveyAction({ ...localSurvey, segment });

setIsSurveySaving(false);
Expand Down Expand Up @@ -278,6 +286,7 @@ export const SurveyMenuBar = ({
}
const status = localSurvey.runOnDate ? "scheduled" : "inProgress";
const segment = await handleSegmentUpdate();
clearSurveyLocalStorage();

await updateSurveyAction({
...localSurvey,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PeopleSecondaryNavigation } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PeopleSecondaryNavigation";
import { PersonSecondaryNavigation } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonSecondaryNavigation";
import { TagIcon } from "lucide-react";
import { PageContentWrapper } from "@formbricks/ui/PageContentWrapper";
import { PageHeader } from "@formbricks/ui/PageHeader";
Expand All @@ -8,7 +8,7 @@ const Loading = () => {
<>
<PageContentWrapper>
<PageHeader pageTitle="People">
<PeopleSecondaryNavigation activeId="attributes" loading />
<PersonSecondaryNavigation activeId="attributes" loading />
</PageHeader>
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
<div className="grid h-12 grid-cols-5 content-center border-b text-left text-sm font-semibold text-slate-900">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PeopleSecondaryNavigation } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PeopleSecondaryNavigation";
import { PersonSecondaryNavigation } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonSecondaryNavigation";
import { CircleHelpIcon } from "lucide-react";
import { Metadata } from "next";
import { notFound } from "next/navigation";
Expand Down Expand Up @@ -42,7 +42,7 @@ const Page = async ({ params }) => {
return (
<PageContentWrapper>
<PageHeader pageTitle="People" cta={HowToAddAttributesButton}>
<PeopleSecondaryNavigation activeId="attributes" environmentId={params.environmentId} />
<PersonSecondaryNavigation activeId="attributes" environmentId={params.environmentId} />
</PageHeader>
<AttributeClassesTable attributeClasses={attributeClasses} />
</PageContentWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"use client";

import { deletePersonAction } from "@/app/(app)/environments/[environmentId]/(people)/people/[personId]/actions";
import { TrashIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
import { getFormattedErrorMessage } from "@formbricks/lib/actionClient/helper";
import { deletePersonAction } from "@formbricks/ui/DataTable/actions";
import { DeleteDialog } from "@formbricks/ui/DeleteDialog";

interface DeletePersonButtonProps {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use server";

import { z } from "zod";
import { authenticatedActionClient } from "@formbricks/lib/actionClient";
import { checkAuthorization } from "@formbricks/lib/actionClient/utils";
import { getAttributes } from "@formbricks/lib/attribute/service";
import { getOrganizationIdFromEnvironmentId } from "@formbricks/lib/organization/utils";
import { getPeople } from "@formbricks/lib/person/service";
import { ZId } from "@formbricks/types/common";

const ZGetPersonsAction = z.object({
environmentId: ZId,
page: z.number(),
});

export const getPersonsAction = authenticatedActionClient
.schema(ZGetPersonsAction)
.action(async ({ ctx, parsedInput }) => {
await checkAuthorization({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromEnvironmentId(parsedInput.environmentId),
rules: ["environment", "read"],
});

return getPeople(parsedInput.environmentId, parsedInput.page);
});

const ZGetPersonAttributesAction = z.object({
environmentId: ZId,
personId: ZId,
});

export const getPersonAttributesAction = authenticatedActionClient
.schema(ZGetPersonAttributesAction)
.action(async ({ ctx, parsedInput }) => {
await checkAuthorization({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromEnvironmentId(parsedInput.environmentId),
rules: ["environment", "read"],
});

return getAttributes(parsedInput.personId);
});

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"use client";

import {
getPersonAttributesAction,
getPersonsAction,
} from "@/app/(app)/environments/[environmentId]/(people)/people/actions";
import { PersonTable } from "@/app/(app)/environments/[environmentId]/(people)/people/components/PersonTable";
import { useEffect, useState } from "react";
import React from "react";
import { TEnvironment } from "@formbricks/types/environment";
import { TPerson, TPersonTableData } from "@formbricks/types/people";

interface PersonDataViewProps {
environment: TEnvironment;
personCount: number;
itemsPerPage: number;
}

export const PersonDataView = ({ environment, personCount, itemsPerPage }: PersonDataViewProps) => {
const [persons, setPersons] = useState<TPerson[]>([]);
const [personTableData, setPersonTableData] = useState<TPersonTableData[]>([]);
const [pageNumber, setPageNumber] = useState<number>(1);
const [totalPersons, setTotalPersons] = useState<number>(0);
const [isDataLoaded, setIsDataLoaded] = useState<boolean>(false);
const [hasMore, setHasMore] = useState<boolean>(false);
const [loadingNextPage, setLoadingNextPage] = useState<boolean>(false);

useEffect(() => {
setTotalPersons(personCount);
setHasMore(pageNumber < Math.ceil(personCount / itemsPerPage));

const fetchData = async () => {
try {
const getPersonActionData = await getPersonsAction({
environmentId: environment.id,
page: pageNumber,
});
if (getPersonActionData?.data) {
setPersons(getPersonActionData.data);
}
} catch (error) {
console.error("Error fetching people data:", error);
}
};

fetchData();
}, [pageNumber]);

// Fetch additional person attributes and update table data
useEffect(() => {
const fetchAttributes = async () => {
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);
setIsDataLoaded(true);
};

if (persons.length > 0) {
fetchAttributes();
}
}, [persons]);

const fetchNextPage = async () => {
if (hasMore && !loadingNextPage) {
setLoadingNextPage(true);
const getPersonsActionData = await getPersonsAction({
environmentId: environment.id,
page: pageNumber,
});
if (getPersonsActionData?.data) {
const newData = getPersonsActionData.data;
setPersons((prevPersonsData) => [...prevPersonsData, ...newData]);
}
setPageNumber((prevPage) => prevPage + 1);
setHasMore(pageNumber + 1 < Math.ceil(totalPersons / itemsPerPage));
setLoadingNextPage(false);
}
};

const deletePersons = (personIds: string[]) => {
setPersons(persons.filter((p) => !personIds.includes(p.id)));
};

return (
<PersonTable
data={personTableData}
fetchNextPage={fetchNextPage}
hasMore={hasMore}
isDataLoaded={isDataLoaded}
deletePersons={deletePersons}
environmentId={environment.id}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
import { TProductConfigChannel } from "@formbricks/types/product";
import { SecondaryNavigation } from "@formbricks/ui/SecondaryNavigation";

interface PeopleSegmentsTabsProps {
interface PersonSecondaryNavigationProps {
activeId: string;
environmentId?: string;
loading?: boolean;
}

export const PeopleSecondaryNavigation = async ({
export const PersonSecondaryNavigation = async ({
activeId,
environmentId,
loading,
}: PeopleSegmentsTabsProps) => {
}: PersonSecondaryNavigationProps) => {
let currentProductChannel: TProductConfigChannel = null;

if (!loading && environmentId) {
Expand Down
Loading

0 comments on commit 6b64367

Please sign in to comment.