Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,8 @@ LOOPS_API_SECRET=
# Sanity config for blog. (Not needed. Only for blog):
# NEXT_PUBLIC_SANITY_PROJECT_ID=
# NEXT_PUBLIC_SANITY_DATASET="production"

# Feature flags
# NEXT_PUBLIC_DIGEST_ENABLED=true
# NEXT_PUBLIC_MEETING_BRIEFS_ENABLED=true
# NEXT_PUBLIC_INTEGRATIONS_ENABLED=true
33 changes: 18 additions & 15 deletions apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import { getRuleConfig } from "@/utils/rule/consts";
import { RuleSectionCard } from "@/app/(app)/[emailAccountId]/assistant/RuleSectionCard";
import { ConditionSteps } from "@/app/(app)/[emailAccountId]/assistant/ConditionSteps";
import { ActionSteps } from "@/app/(app)/[emailAccountId]/assistant/ActionSteps";
import { env } from "@/env";

export function Rule({
ruleId,
Expand Down Expand Up @@ -493,22 +494,24 @@ export function RuleForm({
<ThreadsExplanation size="md" />
</div>

<div className="flex items-center space-x-2">
<Toggle
name="digest"
labelRight="Include in daily digest"
enabled={watch("digest") || false}
onChange={(enabled) => {
setValue("digest", enabled);
}}
/>
{env.NEXT_PUBLIC_DIGEST_ENABLED && (
<div className="flex items-center space-x-2">
<Toggle
name="digest"
labelRight="Include in daily digest"
enabled={watch("digest") || false}
onChange={(enabled) => {
setValue("digest", enabled);
}}
/>

<TooltipExplanation
size="md"
side="right"
text="When enabled you will receive a summary of the emails that match this rule in your digest email."
/>
</div>
<TooltipExplanation
size="md"
side="right"
text="When enabled you will receive a summary of the emails that match this rule in your digest email."
/>
</div>
)}

{!!rule.id && (
<div className="flex">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { LearnedPatternsSetting } from "@/app/(app)/[emailAccountId]/assistant/s
import { PersonalSignatureSetting } from "@/app/(app)/[emailAccountId]/assistant/settings/PersonalSignatureSetting";
import { MultiRuleSetting } from "@/app/(app)/[emailAccountId]/assistant/settings/MultiRuleSetting";
import { WritingStyleSetting } from "@/app/(app)/[emailAccountId]/assistant/settings/WritingStyleSetting";
import { env } from "@/env";

export function SettingsTab() {
return (
Expand All @@ -16,7 +17,7 @@ export function SettingsTab() {
<DraftKnowledgeSetting />
<MultiRuleSetting />
<AboutSetting />
<DigestSetting />
{env.NEXT_PUBLIC_DIGEST_ENABLED && <DigestSetting />}
<PersonalSignatureSetting />
<ReferralSignatureSetting />
<LearnedPatternsSetting />
Expand Down
23 changes: 16 additions & 7 deletions apps/web/components/SideNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
ChevronDownIcon,
ChevronRightIcon,
FileIcon,
HomeIcon,
FileTextIcon,
InboxIcon,
type LucideIcon,
Expand All @@ -29,6 +28,7 @@ import {
SparklesIcon,
TagIcon,
Users2Icon,
ZapIcon,
} from "lucide-react";
import { Logo } from "@/components/Logo";
import { useComposeModal } from "@/providers/ComposeModalProvider";
Expand All @@ -53,6 +53,7 @@ import { useSplitLabels } from "@/hooks/useLabels";
import { LoadingContent } from "@/components/LoadingContent";
import {
useCleanerEnabled,
useIntegrationsEnabled,
useMeetingBriefsEnabled,
} from "@/hooks/useFeatureFlags";
import { ClientOnly } from "@/components/ClientOnly";
Expand All @@ -72,22 +73,19 @@ type NavItem = {
target?: "_blank";
count?: number;
hideInMail?: boolean;
beta?: boolean;
};

export const useNavigation = () => {
const showCleaner = useCleanerEnabled();
const showMeetingBriefs = useMeetingBriefsEnabled();
const showIntegrations = useIntegrationsEnabled();

const { emailAccountId, emailAccount, provider } = useAccount();
const currentEmailAccountId = emailAccount?.id || emailAccountId;

const navItems: NavItem[] = useMemo(
() => [
// {
// name: "Dashboard",
// href: prefixPath(currentEmailAccountId, "/setup"),
// icon: HomeIcon,
// },
{
name: "Assistant",
href: prefixPath(currentEmailAccountId, "/automation"),
Expand Down Expand Up @@ -117,17 +115,28 @@ export const useNavigation = () => {
href: prefixPath(currentEmailAccountId, "/calendars"),
icon: CalendarIcon,
},
...(showIntegrations
? [
{
name: "Integrations",
href: prefixPath(currentEmailAccountId, "/integrations"),
icon: ZapIcon,
beta: true,
},
]
: []),
...(showMeetingBriefs
? [
{
name: "Meeting Briefs",
href: prefixPath(currentEmailAccountId, "/briefs"),
icon: FileTextIcon,
beta: true,
},
]
: []),
],
[currentEmailAccountId, provider, showMeetingBriefs],
[currentEmailAccountId, provider, showMeetingBriefs, showIntegrations],
);

const navItemsFiltered = useMemo(
Expand Down
7 changes: 7 additions & 0 deletions apps/web/components/SideNavMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
SidebarMenuButton,
SidebarMenuItem,
} from "@/components/ui/sidebar";
import { Badge } from "@/components/ui/badge";

type NavItem = {
name: string;
Expand All @@ -16,6 +17,7 @@ type NavItem = {
count?: number;
hideInMail?: boolean;
active?: boolean;
beta?: boolean;
};

export function SideNavMenu({
Expand All @@ -39,6 +41,11 @@ export function SideNavMenu({
<Link href={item.href}>
<item.icon />
<span>{item.name}</span>
{item.beta && (
<Badge variant="secondary" className="ml-auto text-[10px]">
Beta
</Badge>
)}
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
Expand Down
8 changes: 8 additions & 0 deletions apps/web/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ export const env = createEnv({
.default(false),
NEXT_PUBLIC_USE_AEONIK_FONT: z.coerce.boolean().optional().default(false),
NEXT_PUBLIC_BYPASS_PREMIUM_CHECKS: z.coerce.boolean().optional(),
NEXT_PUBLIC_DIGEST_ENABLED: z.coerce.boolean().optional(),
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Feature flag missing default value. Per project conventions, feature flags should use .default(false) to avoid undefined values in boolean checks. Other similar flags like NEXT_PUBLIC_CONTACTS_ENABLED follow this pattern.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/env.ts, line 190:

<comment>Feature flag missing default value. Per project conventions, feature flags should use `.default(false)` to avoid `undefined` values in boolean checks. Other similar flags like `NEXT_PUBLIC_CONTACTS_ENABLED` follow this pattern.</comment>

<file context>
@@ -187,6 +187,9 @@ export const env = createEnv({
       .default(false),
     NEXT_PUBLIC_USE_AEONIK_FONT: z.coerce.boolean().optional().default(false),
     NEXT_PUBLIC_BYPASS_PREMIUM_CHECKS: z.coerce.boolean().optional(),
+    NEXT_PUBLIC_DIGEST_ENABLED: z.coerce.boolean().optional(),
+    NEXT_PUBLIC_MEETING_BRIEFS_ENABLED: z.coerce.boolean().optional(),
+    NEXT_PUBLIC_INTEGRATIONS_ENABLED: z.coerce.boolean().optional(),
</file context>
Suggested change
NEXT_PUBLIC_DIGEST_ENABLED: z.coerce.boolean().optional(),
NEXT_PUBLIC_DIGEST_ENABLED: z.coerce.boolean().optional().default(false),
Fix with Cubic

NEXT_PUBLIC_MEETING_BRIEFS_ENABLED: z.coerce.boolean().optional(),
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Feature flag missing default value. Per project conventions, feature flags should use .default(false) to avoid undefined values in boolean checks.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/env.ts, line 191:

<comment>Feature flag missing default value. Per project conventions, feature flags should use `.default(false)` to avoid `undefined` values in boolean checks.</comment>

<file context>
@@ -187,6 +187,9 @@ export const env = createEnv({
     NEXT_PUBLIC_USE_AEONIK_FONT: z.coerce.boolean().optional().default(false),
     NEXT_PUBLIC_BYPASS_PREMIUM_CHECKS: z.coerce.boolean().optional(),
+    NEXT_PUBLIC_DIGEST_ENABLED: z.coerce.boolean().optional(),
+    NEXT_PUBLIC_MEETING_BRIEFS_ENABLED: z.coerce.boolean().optional(),
+    NEXT_PUBLIC_INTEGRATIONS_ENABLED: z.coerce.boolean().optional(),
   },
</file context>
Suggested change
NEXT_PUBLIC_MEETING_BRIEFS_ENABLED: z.coerce.boolean().optional(),
NEXT_PUBLIC_MEETING_BRIEFS_ENABLED: z.coerce.boolean().optional().default(false),
Fix with Cubic

NEXT_PUBLIC_INTEGRATIONS_ENABLED: z.coerce.boolean().optional(),
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Feature flag missing default value. Per project conventions, feature flags should use .default(false) to avoid undefined values in boolean checks.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/env.ts, line 192:

<comment>Feature flag missing default value. Per project conventions, feature flags should use `.default(false)` to avoid `undefined` values in boolean checks.</comment>

<file context>
@@ -187,6 +187,9 @@ export const env = createEnv({
     NEXT_PUBLIC_BYPASS_PREMIUM_CHECKS: z.coerce.boolean().optional(),
+    NEXT_PUBLIC_DIGEST_ENABLED: z.coerce.boolean().optional(),
+    NEXT_PUBLIC_MEETING_BRIEFS_ENABLED: z.coerce.boolean().optional(),
+    NEXT_PUBLIC_INTEGRATIONS_ENABLED: z.coerce.boolean().optional(),
   },
   // For Next.js &gt;= 13.4.4, you only need to destructure client variables:
</file context>
Suggested change
NEXT_PUBLIC_INTEGRATIONS_ENABLED: z.coerce.boolean().optional(),
NEXT_PUBLIC_INTEGRATIONS_ENABLED: z.coerce.boolean().optional().default(false),
Fix with Cubic

},
// For Next.js >= 13.4.4, you only need to destructure client variables:
experimental__runtimeEnv: {
Expand Down Expand Up @@ -243,5 +246,10 @@ export const env = createEnv({
NEXT_PUBLIC_USE_AEONIK_FONT: process.env.NEXT_PUBLIC_USE_AEONIK_FONT,
NEXT_PUBLIC_BYPASS_PREMIUM_CHECKS:
process.env.NEXT_PUBLIC_BYPASS_PREMIUM_CHECKS,
NEXT_PUBLIC_DIGEST_ENABLED: process.env.NEXT_PUBLIC_DIGEST_ENABLED,
NEXT_PUBLIC_MEETING_BRIEFS_ENABLED:
process.env.NEXT_PUBLIC_MEETING_BRIEFS_ENABLED,
NEXT_PUBLIC_INTEGRATIONS_ENABLED:
process.env.NEXT_PUBLIC_INTEGRATIONS_ENABLED,
},
});
7 changes: 6 additions & 1 deletion apps/web/hooks/useFeatureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ import {
useFeatureFlagEnabled,
useFeatureFlagVariantKey,
} from "posthog-js/react";
import { env } from "@/env";

export function useCleanerEnabled() {
return useFeatureFlagEnabled("inbox-cleaner");
}

export function useMeetingBriefsEnabled() {
return useFeatureFlagEnabled("meeting-briefs");
return env.NEXT_PUBLIC_MEETING_BRIEFS_ENABLED;
}

export function useIntegrationsEnabled() {
return env.NEXT_PUBLIC_INTEGRATIONS_ENABLED;
}

const HERO_FLAG_NAME = "hero-copy-7";
Expand Down
8 changes: 4 additions & 4 deletions apps/web/utils/action-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,15 @@ export const actionInputs: Record<
},
[ActionType.FORWARD]: {
fields: [
{
name: "to",
label: "To",
},
{
name: "content",
label: "Extra Content",
textArea: true,
},
{
name: "to",
label: "To",
},
{
name: "cc",
label: "CC",
Expand Down
3 changes: 3 additions & 0 deletions docs/hosting/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ cp apps/web/.env.example apps/web/.env
| `NEXT_PUBLIC_CONTACTS_ENABLED` | No | Enable contacts feature | `false` |
| `NEXT_PUBLIC_EMAIL_SEND_ENABLED` | No | Enable email sending | `true` |
| `NEXT_PUBLIC_BYPASS_PREMIUM_CHECKS` | No | Bypass premium checks (recommended for self-hosting) | `true` |
| `NEXT_PUBLIC_DIGEST_ENABLED` | No | Enable email digest feature, which sends periodic summaries of emails. Requires QStash to be configured. | `false` |
| `NEXT_PUBLIC_MEETING_BRIEFS_ENABLED` | No | Enable meeting briefs, which automatically sends pre-meeting briefings to users. Requires the meeting briefs cron job to be running. | `false` |
| `NEXT_PUBLIC_INTEGRATIONS_ENABLED` | No | Enable the integrations feature, allowing users to connect external services. | `false` |
| **Debugging** ||||
| `LOG_ZOD_ERRORS` | No | Log Zod validation errors | — |
| `ENABLE_DEBUG_LOGS` | No | Enable debug logging | `false` |
Expand Down
6 changes: 5 additions & 1 deletion turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@
"NEXT_PUBLIC_AXIOM_DATASET",
"NEXT_PUBLIC_AXIOM_TOKEN",
"NEXT_PUBLIC_DUB_REFER_DOMAIN",
"NEXT_PUBLIC_USE_AEONIK_FONT"
"NEXT_PUBLIC_USE_AEONIK_FONT",
"NEXT_PUBLIC_BYPASS_PREMIUM_CHECKS",
"NEXT_PUBLIC_DIGEST_ENABLED",
"NEXT_PUBLIC_MEETING_BRIEFS_ENABLED",
"NEXT_PUBLIC_INTEGRATIONS_ENABLED"
],
"outputs": [".next/**", "!.next/cache/**"]
},
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.23.0
v2.23.1
Loading