refactor: introduce ContactsPageLayout component for improved structure

- Replace PageContentWrapper and PageHeader with a new ContactsPageLayout component in Contacts, Attributes, and Segments pages.
- Simplify the layout structure and enhance code readability.
- Remove unused imports and comments in safe-identifier.ts.
This commit is contained in:
Dhruwang
2025-12-25 16:36:16 +05:30
parent c0dc00341a
commit 32f96392a3
5 changed files with 122 additions and 144 deletions

View File

@@ -2,8 +2,6 @@
* Validates that a string is a safe identifier.
* Safe identifiers can only contain lowercase letters, numbers, and underscores.
* They cannot start with a number.
*
* This matches the validation used for survey variable names (see formbricks#5342).
*/
export const isSafeIdentifier = (value: string): boolean => {
// Must start with a lowercase letter

View File

@@ -1,13 +1,8 @@
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
import { getLocale } from "@/lingodotdev/language";
import { getTranslate } from "@/lingodotdev/server";
import { ContactsSecondaryNavigation } from "@/modules/ee/contacts/components/contacts-secondary-navigation";
import { ContactsPageLayout } from "@/modules/ee/contacts/components/contacts-page-layout";
import { getContactAttributeKeys } from "@/modules/ee/contacts/lib/contact-attribute-keys";
import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
import { AttributesTable } from "./components/attributes-table";
import { CreateAttributeModal } from "./components/create-attribute-modal";
@@ -17,7 +12,6 @@ export const AttributesPage = async ({
params: Promise<{ environmentId: string }>;
}) => {
const params = await paramsProps;
const t = await getTranslate();
const locale = await getLocale();
const [{ isReadOnly }, contactAttributeKeys] = await Promise.all([
@@ -28,46 +22,19 @@ export const AttributesPage = async ({
const isContactsEnabled = await getIsContactsEnabled();
return (
<PageContentWrapper>
<PageHeader
pageTitle="Contacts"
cta={
isContactsEnabled && !isReadOnly ? (
<CreateAttributeModal environmentId={params.environmentId} />
) : undefined
}>
<ContactsSecondaryNavigation activeId="attributes" environmentId={params.environmentId} />
</PageHeader>
{isContactsEnabled ? (
<AttributesTable
contactAttributeKeys={contactAttributeKeys}
isReadOnly={isReadOnly}
environmentId={params.environmentId}
locale={locale}
/>
) : (
<div className="flex items-center justify-center">
<UpgradePrompt
title={t("environments.contacts.unlock_contacts_title")}
description={t("environments.contacts.unlock_contacts_description")}
buttons={[
{
text: IS_FORMBRICKS_CLOUD ? t("common.start_free_trial") : t("common.request_trial_license"),
href: IS_FORMBRICKS_CLOUD
? `/environments/${params.environmentId}/settings/billing`
: "https://formbricks.com/upgrade-self-hosting-license",
},
{
text: t("common.learn_more"),
href: IS_FORMBRICKS_CLOUD
? `/environments/${params.environmentId}/settings/billing`
: "https://formbricks.com/learn-more-self-hosting-license",
},
]}
/>
</div>
)}
</PageContentWrapper>
<ContactsPageLayout
pageTitle="Contacts"
activeId="attributes"
environmentId={params.environmentId}
isContactsEnabled={isContactsEnabled}
isReadOnly={isReadOnly}
cta={<CreateAttributeModal environmentId={params.environmentId} />}>
<AttributesTable
contactAttributeKeys={contactAttributeKeys}
isReadOnly={isReadOnly}
environmentId={params.environmentId}
locale={locale}
/>
</ContactsPageLayout>
);
};

View File

@@ -0,0 +1,66 @@
import { ReactNode } from "react";
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
import { getTranslate } from "@/lingodotdev/server";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
import { ContactsSecondaryNavigation } from "./contacts-secondary-navigation";
interface ContactsPageLayoutProps {
pageTitle: string;
activeId: string;
environmentId: string;
isContactsEnabled: boolean;
isReadOnly: boolean;
cta?: ReactNode;
children: ReactNode;
upgradePromptTitle?: string;
upgradePromptDescription?: string;
}
export const ContactsPageLayout = async ({
pageTitle,
activeId,
environmentId,
isContactsEnabled,
isReadOnly,
cta,
children,
upgradePromptTitle,
upgradePromptDescription,
}: ContactsPageLayoutProps) => {
const t = await getTranslate();
return (
<PageContentWrapper>
<PageHeader pageTitle={pageTitle} cta={isContactsEnabled && !isReadOnly ? cta : undefined}>
<ContactsSecondaryNavigation activeId={activeId} environmentId={environmentId} />
</PageHeader>
{isContactsEnabled ? (
children
) : (
<div className="flex items-center justify-center">
<UpgradePrompt
title={upgradePromptTitle ?? t("environments.contacts.unlock_contacts_title")}
description={upgradePromptDescription ?? t("environments.contacts.unlock_contacts_description")}
buttons={[
{
text: IS_FORMBRICKS_CLOUD ? t("common.start_free_trial") : t("common.request_trial_license"),
href: IS_FORMBRICKS_CLOUD
? `/environments/${environmentId}/settings/billing`
: "https://formbricks.com/upgrade-self-hosting-license",
},
{
text: t("common.learn_more"),
href: IS_FORMBRICKS_CLOUD
? `/environments/${environmentId}/settings/billing`
: "https://formbricks.com/learn-more-self-hosting-license",
},
]}
/>
</div>
)}
</PageContentWrapper>
);
};

View File

@@ -1,15 +1,12 @@
import { IS_FORMBRICKS_CLOUD, ITEMS_PER_PAGE } from "@/lib/constants";
import { ITEMS_PER_PAGE } from "@/lib/constants";
import { getTranslate } from "@/lingodotdev/server";
import { ContactsPageLayout } from "@/modules/ee/contacts/components/contacts-page-layout";
import { UploadContactsCSVButton } from "@/modules/ee/contacts/components/upload-contacts-button";
import { getContactAttributeKeys } from "@/modules/ee/contacts/lib/contact-attribute-keys";
import { getContacts } from "@/modules/ee/contacts/lib/contacts";
import { getIsContactsEnabled, getIsQuotasEnabled } from "@/modules/ee/license-check/lib/utils";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
import { ContactDataView } from "./components/contact-data-view";
import { ContactsSecondaryNavigation } from "./components/contacts-secondary-navigation";
export const ContactsPage = async ({
params: paramsProps,
@@ -34,46 +31,23 @@ export const ContactsPage = async ({
);
return (
<PageContentWrapper>
<PageHeader
pageTitle={t("common.contacts")}
cta={isContactsEnabled && !isReadOnly ? AddContactsButton : undefined}>
<ContactsSecondaryNavigation activeId="contacts" environmentId={params.environmentId} />
</PageHeader>
{isContactsEnabled ? (
<ContactDataView
key={initialContacts.length + contactAttributeKeys.length}
environment={environment}
itemsPerPage={ITEMS_PER_PAGE}
contactAttributeKeys={contactAttributeKeys}
isReadOnly={isReadOnly}
initialContacts={initialContacts}
hasMore={initialContacts.length >= ITEMS_PER_PAGE}
isQuotasAllowed={isQuotasAllowed}
/>
) : (
<div className="flex items-center justify-center">
<UpgradePrompt
title={t("environments.contacts.unlock_contacts_title")}
description={t("environments.contacts.unlock_contacts_description")}
buttons={[
{
text: IS_FORMBRICKS_CLOUD ? t("common.start_free_trial") : t("common.request_trial_license"),
href: IS_FORMBRICKS_CLOUD
? `/environments/${params.environmentId}/settings/billing`
: "https://formbricks.com/upgrade-self-hosting-license",
},
{
text: t("common.learn_more"),
href: IS_FORMBRICKS_CLOUD
? `/environments/${params.environmentId}/settings/billing`
: "https://formbricks.com/learn-more-self-hosting-license",
},
]}
/>
</div>
)}
</PageContentWrapper>
<ContactsPageLayout
pageTitle={t("common.contacts")}
activeId="contacts"
environmentId={params.environmentId}
isContactsEnabled={isContactsEnabled}
isReadOnly={isReadOnly}
cta={AddContactsButton}>
<ContactDataView
key={initialContacts.length + contactAttributeKeys.length}
environment={environment}
itemsPerPage={ITEMS_PER_PAGE}
contactAttributeKeys={contactAttributeKeys}
isReadOnly={isReadOnly}
initialContacts={initialContacts}
hasMore={initialContacts.length >= ITEMS_PER_PAGE}
isQuotasAllowed={isQuotasAllowed}
/>
</ContactsPageLayout>
);
};

View File

@@ -1,14 +1,10 @@
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
import { getTranslate } from "@/lingodotdev/server";
import { ContactsSecondaryNavigation } from "@/modules/ee/contacts/components/contacts-secondary-navigation";
import { ContactsPageLayout } from "@/modules/ee/contacts/components/contacts-page-layout";
import { getContactAttributeKeys } from "@/modules/ee/contacts/lib/contact-attribute-keys";
import { SegmentTable } from "@/modules/ee/contacts/segments/components/segment-table";
import { getSegments } from "@/modules/ee/contacts/segments/lib/segments";
import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
import { CreateSegmentModal } from "./components/create-segment-modal";
export const SegmentsPage = async ({
@@ -35,50 +31,27 @@ export const SegmentsPage = async ({
const filteredSegments = segments.filter((segment) => !segment.isPrivate);
return (
<PageContentWrapper>
<PageHeader
pageTitle="Contacts"
cta={
isContactsEnabled && !isReadOnly ? (
<CreateSegmentModal
environmentId={params.environmentId}
contactAttributeKeys={contactAttributeKeys}
segments={filteredSegments}
/>
) : undefined
}>
<ContactsSecondaryNavigation activeId="segments" environmentId={params.environmentId} />
</PageHeader>
{isContactsEnabled ? (
<SegmentTable
segments={filteredSegments}
<ContactsPageLayout
pageTitle="Contacts"
activeId="segments"
environmentId={params.environmentId}
isContactsEnabled={isContactsEnabled}
isReadOnly={isReadOnly}
cta={
<CreateSegmentModal
environmentId={params.environmentId}
contactAttributeKeys={contactAttributeKeys}
isContactsEnabled={isContactsEnabled}
isReadOnly={isReadOnly}
segments={filteredSegments}
/>
) : (
<div className="flex items-center justify-center">
<UpgradePrompt
title={t("environments.segments.unlock_segments_title")}
description={t("environments.segments.unlock_segments_description")}
buttons={[
{
text: IS_FORMBRICKS_CLOUD ? t("common.start_free_trial") : t("common.request_trial_license"),
href: IS_FORMBRICKS_CLOUD
? `/environments/${params.environmentId}/settings/billing`
: "https://formbricks.com/upgrade-self-hosting-license",
},
{
text: t("common.learn_more"),
href: IS_FORMBRICKS_CLOUD
? `/environments/${params.environmentId}/settings/billing`
: "https://formbricks.com/learn-more-self-hosting-license",
},
]}
/>
</div>
)}
</PageContentWrapper>
}
upgradePromptTitle={t("environments.segments.unlock_segments_title")}
upgradePromptDescription={t("environments.segments.unlock_segments_description")}>
<SegmentTable
segments={filteredSegments}
contactAttributeKeys={contactAttributeKeys}
isContactsEnabled={isContactsEnabled}
isReadOnly={isReadOnly}
/>
</ContactsPageLayout>
);
};