mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-12 03:09:41 -06:00
154 lines
4.8 KiB
TypeScript
154 lines
4.8 KiB
TypeScript
"use client";
|
|
|
|
import { debounce } from "lodash";
|
|
import dynamic from "next/dynamic";
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
import toast from "react-hot-toast";
|
|
import { TContactAttributeKey } from "@formbricks/types/contact-attribute-key";
|
|
import { TEnvironment } from "@formbricks/types/environment";
|
|
import { LoadingSpinner } from "@/modules/ui/components/loading-spinner";
|
|
import { getContactsAction } from "../actions";
|
|
import { TContactTableData, TContactWithAttributes } from "../types/contact";
|
|
|
|
const ContactsTableDynamic = dynamic(() => import("./contacts-table").then((mod) => mod.ContactsTable), {
|
|
loading: () => <LoadingSpinner />,
|
|
ssr: false,
|
|
});
|
|
|
|
interface ContactDataViewProps {
|
|
environment: TEnvironment;
|
|
contactAttributeKeys: TContactAttributeKey[];
|
|
initialContacts: TContactWithAttributes[];
|
|
itemsPerPage: number;
|
|
isReadOnly: boolean;
|
|
hasMore: boolean;
|
|
isQuotasAllowed: boolean;
|
|
}
|
|
|
|
export const ContactDataView = ({
|
|
environment,
|
|
itemsPerPage,
|
|
contactAttributeKeys,
|
|
isReadOnly,
|
|
hasMore: initialHasMore,
|
|
initialContacts,
|
|
isQuotasAllowed,
|
|
}: ContactDataViewProps) => {
|
|
const [contacts, setContacts] = useState<TContactWithAttributes[]>([...initialContacts]);
|
|
const [hasMore, setHasMore] = useState<boolean>(initialHasMore);
|
|
const [loadingNextPage, setLoadingNextPage] = useState<boolean>(false);
|
|
const [searchValue, setSearchValue] = useState<string>("");
|
|
const [isDataLoaded, setIsDataLoaded] = useState(true);
|
|
|
|
const isFirstRender = useRef(true);
|
|
|
|
const environmentAttributes = useMemo(() => {
|
|
return contactAttributeKeys.filter(
|
|
(attr) => !["userId", "email", "firstName", "lastName"].includes(attr.key)
|
|
);
|
|
}, [contactAttributeKeys]);
|
|
|
|
// Fetch contacts from offset 0 with current search value
|
|
const fetchContactsFromStart = useCallback(async () => {
|
|
setIsDataLoaded(false);
|
|
try {
|
|
setHasMore(true);
|
|
const contactsResponse = await getContactsAction({
|
|
environmentId: environment.id,
|
|
offset: 0,
|
|
searchValue,
|
|
});
|
|
if (contactsResponse?.data) {
|
|
setContacts(contactsResponse.data);
|
|
}
|
|
if (contactsResponse?.data && contactsResponse.data.length < itemsPerPage) {
|
|
setHasMore(false);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching contacts:", error);
|
|
toast.error("Error fetching contacts. Please try again.");
|
|
} finally {
|
|
setIsDataLoaded(true);
|
|
}
|
|
}, [environment.id, itemsPerPage, searchValue]);
|
|
|
|
useEffect(() => {
|
|
if (!isFirstRender.current) {
|
|
const debouncedFetchData = debounce(fetchContactsFromStart, 300);
|
|
debouncedFetchData();
|
|
|
|
return () => {
|
|
debouncedFetchData.cancel();
|
|
};
|
|
}
|
|
}, [fetchContactsFromStart]);
|
|
|
|
useEffect(() => {
|
|
if (isFirstRender.current) {
|
|
isFirstRender.current = false;
|
|
}
|
|
}, []);
|
|
|
|
// Fetch next page of contacts
|
|
const fetchNextPage = async () => {
|
|
if (hasMore && !loadingNextPage) {
|
|
setLoadingNextPage(true);
|
|
try {
|
|
const contactsResponse = await getContactsAction({
|
|
environmentId: environment.id,
|
|
offset: contacts.length,
|
|
searchValue,
|
|
});
|
|
const contactsData = contactsResponse?.data || [];
|
|
|
|
setContacts((prevContacts) => [...prevContacts, ...contactsData]);
|
|
|
|
if (contactsData.length < itemsPerPage) {
|
|
setHasMore(false);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching next page of contacts:", error);
|
|
} finally {
|
|
setLoadingNextPage(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Delete selected contacts
|
|
const updateContactList = (contactIds: string[]) => {
|
|
setContacts((prevContacts) => prevContacts.filter((contact) => !contactIds.includes(contact.id)));
|
|
};
|
|
|
|
// Prepare data for the ContactTable component
|
|
const contactsTableData: TContactTableData[] = useMemo(() => {
|
|
return contacts.map((contact) => ({
|
|
id: contact.id,
|
|
userId: contact.attributes.userId ?? "",
|
|
email: contact.attributes.email ?? "",
|
|
firstName: contact.attributes.firstName ?? "",
|
|
lastName: contact.attributes.lastName ?? "",
|
|
attributes: (environmentAttributes ?? []).map((attr) => ({
|
|
key: attr.key,
|
|
name: attr.name,
|
|
value: contact.attributes[attr.key] ?? "",
|
|
})),
|
|
}));
|
|
}, [contacts, environmentAttributes]);
|
|
|
|
return (
|
|
<ContactsTableDynamic
|
|
data={contactsTableData}
|
|
fetchNextPage={fetchNextPage}
|
|
hasMore={hasMore}
|
|
isDataLoaded={isFirstRender.current ? true : isDataLoaded}
|
|
updateContactList={updateContactList}
|
|
environmentId={environment.id}
|
|
searchValue={searchValue}
|
|
setSearchValue={setSearchValue}
|
|
isReadOnly={isReadOnly}
|
|
isQuotasAllowed={isQuotasAllowed}
|
|
refreshContacts={fetchContactsFromStart}
|
|
/>
|
|
);
|
|
};
|