forked from formbricks/formbricks
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrite Person Detail Page to Server Components (formbricks#609)
* feat: migration /[personId] page to server side * feat: decouple components in person page * fix: ZDisplaysWithSurveyName now extends the ZDisplay type * feat: drop custom service and use existing service for survey and response * run pnpm format * shift data fetching to component level but still server side * rename event to action * move special person services to activity service * remove activityFeedItem type in ActivityFeed * simplify TResponseWithSurvey --------- Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
- Loading branch information
1 parent
e7dc734
commit 81ca60c
Showing
26 changed files
with
796 additions
and
356 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
...p/(app)/environments/[environmentId]/people/[personId]/(activitySection)/ActivityFeed.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import EmptySpaceFiller from "@/components/shared/EmptySpaceFiller"; | ||
import { ActivityItemContent, ActivityItemIcon, ActivityItemPopover } from "./ActivityItemComponents"; | ||
import { TActivityFeedItem } from "@formbricks/types/v1/activity"; | ||
|
||
interface ActivityFeedProps { | ||
activities: TActivityFeedItem[]; | ||
sortByDate: boolean; | ||
environmentId: string; | ||
} | ||
|
||
export default function ActivityFeed({ activities, sortByDate, environmentId }: ActivityFeedProps) { | ||
const sortedActivities: TActivityFeedItem[] = activities.sort((a, b) => | ||
sortByDate | ||
? new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() | ||
: new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime() | ||
); | ||
return ( | ||
<> | ||
{sortedActivities.length === 0 ? ( | ||
<EmptySpaceFiller type={"event"} environmentId={environmentId} /> | ||
) : ( | ||
<div> | ||
{sortedActivities.map((activityItem) => ( | ||
<li key={activityItem.id} className="list-none"> | ||
<div className="relative pb-12"> | ||
<span className="absolute left-6 top-4 -ml-px h-full w-0.5 bg-slate-200" aria-hidden="true" /> | ||
<div className="relative"> | ||
<ActivityItemPopover activityItem={activityItem}> | ||
<div className="flex space-x-3 text-left"> | ||
<ActivityItemIcon activityItem={activityItem} /> | ||
<ActivityItemContent activityItem={activityItem} /> | ||
</div> | ||
</ActivityItemPopover> | ||
</div> | ||
</div> | ||
</li> | ||
))} | ||
</div> | ||
)} | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
...app)/environments/[environmentId]/people/[personId]/(activitySection)/ActivitySection.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import ActivityTimeline from "@/app/(app)/environments/[environmentId]/people/[personId]/(activitySection)/ActivityTimeline"; | ||
import { getActivityTimeline } from "@formbricks/lib/services/activity"; | ||
|
||
export default async function ActivitySection({ | ||
environmentId, | ||
personId, | ||
}: { | ||
environmentId: string; | ||
personId: string; | ||
}) { | ||
const activities = await getActivityTimeline(personId); | ||
|
||
return ( | ||
<div className="md:col-span-1"> | ||
<ActivityTimeline environmentId={environmentId} activities={activities} /> | ||
</div> | ||
); | ||
} |
36 changes: 36 additions & 0 deletions
36
...pp)/environments/[environmentId]/people/[personId]/(activitySection)/ActivityTimeline.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
"use client"; | ||
|
||
import ActivityFeed from "@/app/(app)/environments/[environmentId]/people/[personId]/(activitySection)/ActivityFeed"; | ||
import { TActivityFeedItem } from "@formbricks/types/v1/activity"; | ||
import { ArrowsUpDownIcon } from "@heroicons/react/24/outline"; | ||
import { useState } from "react"; | ||
|
||
export default function ActivityTimeline({ | ||
environmentId, | ||
activities, | ||
}: { | ||
environmentId: string; | ||
activities: TActivityFeedItem[]; | ||
}) { | ||
const [activityAscending, setActivityAscending] = useState(true); | ||
const toggleSortActivity = () => { | ||
setActivityAscending(!activityAscending); | ||
}; | ||
|
||
return ( | ||
<> | ||
<div className="flex items-center justify-between pb-6"> | ||
<h2 className="text-lg font-bold text-slate-700">Activity Timeline</h2> | ||
<div className="text-right"> | ||
<button | ||
onClick={toggleSortActivity} | ||
className="hover:text-brand-dark flex items-center px-1 text-slate-800"> | ||
<ArrowsUpDownIcon className="inline h-4 w-4" /> | ||
</button> | ||
</div> | ||
</div> | ||
|
||
<ActivityFeed activities={activities} sortByDate={activityAscending} environmentId={environmentId} /> | ||
</> | ||
); | ||
} |
68 changes: 68 additions & 0 deletions
68
...)/environments/[environmentId]/people/[personId]/(attributeSection)/AttributesSection.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
export const revalidate = REVALIDATION_INTERVAL; | ||
|
||
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants"; | ||
|
||
import { capitalizeFirstLetter } from "@/lib/utils"; | ||
import { getPerson } from "@formbricks/lib/services/person"; | ||
import { getResponsesByPersonId } from "@formbricks/lib/services/response"; | ||
import { getSessionCount } from "@formbricks/lib/services/session"; | ||
|
||
export default async function AttributesSection({ personId }: { personId: string }) { | ||
const person = await getPerson(personId); | ||
if (!person) { | ||
throw new Error("No such person found"); | ||
} | ||
const numberOfSessions = await getSessionCount(personId); | ||
const responses = await getResponsesByPersonId(personId); | ||
|
||
const numberOfResponses = responses?.length || 0; | ||
|
||
return ( | ||
<div className="space-y-6"> | ||
<h2 className="text-lg font-bold text-slate-700">Attributes</h2> | ||
<div> | ||
<dt className="text-sm font-medium text-slate-500">Email</dt> | ||
<dd className="ph-no-capture mt-1 text-sm text-slate-900"> | ||
{person.attributes.email ? ( | ||
<span>{person.attributes.email}</span> | ||
) : ( | ||
<span className="text-slate-300">Not provided</span> | ||
)} | ||
</dd> | ||
</div> | ||
<div> | ||
<dt className="text-sm font-medium text-slate-500">User Id</dt> | ||
<dd className="ph-no-capture mt-1 text-sm text-slate-900"> | ||
{person.attributes.userId ? ( | ||
<span>{person.attributes.userId}</span> | ||
) : ( | ||
<span className="text-slate-300">Not provided</span> | ||
)} | ||
</dd> | ||
</div> | ||
<div> | ||
<dt className="text-sm font-medium text-slate-500">Formbricks Id (internal)</dt> | ||
<dd className="ph-no-capture mt-1 text-sm text-slate-900">{person.id}</dd> | ||
</div> | ||
|
||
{Object.entries(person.attributes) | ||
.filter(([key, _]) => key !== "email" && key !== "userId") | ||
.map(([key, value]) => ( | ||
<div key={key}> | ||
<dt className="text-sm font-medium text-slate-500">{capitalizeFirstLetter(key.toString())}</dt> | ||
<dd className="mt-1 text-sm text-slate-900">{value}</dd> | ||
</div> | ||
))} | ||
<hr /> | ||
|
||
<div> | ||
<dt className="text-sm font-medium text-slate-500">Sessions</dt> | ||
<dd className="mt-1 text-sm text-slate-900">{numberOfSessions}</dd> | ||
</div> | ||
<div> | ||
<dt className="text-sm font-medium text-slate-500">Responses</dt> | ||
<dd className="mt-1 text-sm text-slate-900">{numberOfResponses}</dd> | ||
</div> | ||
</div> | ||
); | ||
} |
30 changes: 30 additions & 0 deletions
30
...app)/environments/[environmentId]/people/[personId]/(responseSection)/ResponseSection.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import ResponseTimeline from "@/app/(app)/environments/[environmentId]/people/[personId]/(responseSection)/ResponseTimeline"; | ||
import { getResponsesByPersonId } from "@formbricks/lib/services/response"; | ||
import { getSurveys } from "@formbricks/lib/services/survey"; | ||
import { TResponseWithSurvey } from "@formbricks/types/v1/responses"; | ||
import { TSurvey } from "@formbricks/types/v1/surveys"; | ||
|
||
export default async function ResponseSection({ | ||
environmentId, | ||
personId, | ||
}: { | ||
environmentId: string; | ||
personId: string; | ||
}) { | ||
const responses = await getResponsesByPersonId(personId); | ||
const surveyIds = responses?.map((response) => response.surveyId) || []; | ||
const surveys: TSurvey[] = surveyIds.length === 0 ? [] : (await getSurveys(environmentId)) ?? []; | ||
const responsesWithSurvey: TResponseWithSurvey[] = | ||
responses?.reduce((acc: TResponseWithSurvey[], response) => { | ||
const thisSurvey = surveys.find((survey) => survey?.id === response.surveyId); | ||
if (thisSurvey) { | ||
acc.push({ | ||
...response, | ||
survey: thisSurvey, | ||
}); | ||
} | ||
return acc; | ||
}, []) || []; | ||
|
||
return <ResponseTimeline environmentId={environmentId} responses={responsesWithSurvey} />; | ||
} |
35 changes: 35 additions & 0 deletions
35
...pp)/environments/[environmentId]/people/[personId]/(responseSection)/ResponseTimeline.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
"use client"; | ||
|
||
import ResponseFeed from "@/app/(app)/environments/[environmentId]/people/[personId]/(responseSection)/ResponsesFeed"; | ||
import { TResponseWithSurvey } from "@formbricks/types/v1/responses"; | ||
import { ArrowsUpDownIcon } from "@heroicons/react/24/outline"; | ||
import { useState } from "react"; | ||
|
||
export default function ResponseTimeline({ | ||
environmentId, | ||
responses, | ||
}: { | ||
environmentId: string; | ||
responses: TResponseWithSurvey[]; | ||
}) { | ||
const [responsesAscending, setResponsesAscending] = useState(true); | ||
const toggleSortResponses = () => { | ||
setResponsesAscending(!responsesAscending); | ||
}; | ||
|
||
return ( | ||
<div className="md:col-span-2"> | ||
<div className="flex items-center justify-between pb-6"> | ||
<h2 className="text-lg font-bold text-slate-700">Responses</h2> | ||
<div className="text-right"> | ||
<button | ||
onClick={toggleSortResponses} | ||
className="hover:text-brand-dark flex items-center px-1 text-slate-800"> | ||
<ArrowsUpDownIcon className="inline h-4 w-4" /> | ||
</button> | ||
</div> | ||
</div> | ||
<ResponseFeed responses={responses} sortByDate={responsesAscending} environmentId={environmentId} /> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.