diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx index 9a56fd87b21..03d791ca891 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx @@ -1,6 +1,13 @@ import { LOCAL_STORAGE_KEYS } from 'common' export const FEATURE_PREVIEWS = [ + { + key: LOCAL_STORAGE_KEYS.UI_PREVIEW_SECURITY_NOTIFICATIONS, + name: 'Security notification templates', + discussionsUrl: undefined, + isNew: true, + isPlatformOnly: true, + }, { key: LOCAL_STORAGE_KEYS.UI_PREVIEW_NEW_STORAGE_UI, name: 'New Storage interface', diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx index 885724aca13..bb2b449c4d5 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx @@ -115,6 +115,11 @@ export const useIsNewStorageUIEnabled = () => { return flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_NEW_STORAGE_UI] } +export const useIsSecurityNotificationsEnabled = () => { + const { flags } = useFeaturePreviewContext() + return flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_SECURITY_NOTIFICATIONS] +} + export const useFeaturePreviewModal = () => { const [featurePreviewModal, setFeaturePreviewModal] = useQueryState('featurePreviewModal') @@ -122,6 +127,7 @@ export const useFeaturePreviewModal = () => { const advisorRulesEnabled = useFlag('advisorRules') const isUnifiedLogsPreviewAvailable = useFlag('unifiedLogs') const isNewStorageUIAvailable = useFlag('storageAnalyticsVector') + const isSecurityNotificationsAvailable = useFlag('securityNotifications') const selectedFeatureKeyFromQuery = featurePreviewModal?.trim() ?? null const showFeaturePreviewModal = selectedFeatureKeyFromQuery !== null @@ -138,11 +144,19 @@ export const useFeaturePreviewModal = () => { return isUnifiedLogsPreviewAvailable case 'new-storage-ui': return isNewStorageUIAvailable + case 'security-notifications': + return isSecurityNotificationsAvailable default: return true } }, - [gitlessBranchingEnabled, advisorRulesEnabled, isUnifiedLogsPreviewAvailable] + [ + gitlessBranchingEnabled, + advisorRulesEnabled, + isUnifiedLogsPreviewAvailable, + isNewStorageUIAvailable, + isSecurityNotificationsAvailable, + ] ) const selectedFeatureKey = ( diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx index ec463be8a64..3fa262e4b12 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx @@ -16,6 +16,7 @@ import { useFeaturePreviewContext, useFeaturePreviewModal } from './FeaturePrevi import { InlineEditorPreview } from './InlineEditorPreview' import { NewStorageUIPreview } from './NewStorageUIPreview' import { UnifiedLogsPreview } from './UnifiedLogsPreview' +import { SecurityNotificationsPreview } from './SecurityNotificationsPreview' const FEATURE_PREVIEW_KEY_TO_CONTENT: { [key: string]: ReactNode @@ -27,6 +28,7 @@ const FEATURE_PREVIEW_KEY_TO_CONTENT: { [LOCAL_STORAGE_KEYS.UI_PREVIEW_CLS]: , [LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS]: , [LOCAL_STORAGE_KEYS.UI_PREVIEW_NEW_STORAGE_UI]: , + [LOCAL_STORAGE_KEYS.UI_PREVIEW_SECURITY_NOTIFICATIONS]: , } const FeaturePreviewModal = () => { diff --git a/apps/studio/components/interfaces/App/FeaturePreview/SecurityNotificationsPreview.tsx b/apps/studio/components/interfaces/App/FeaturePreview/SecurityNotificationsPreview.tsx new file mode 100644 index 00000000000..8857881dfa4 --- /dev/null +++ b/apps/studio/components/interfaces/App/FeaturePreview/SecurityNotificationsPreview.tsx @@ -0,0 +1,46 @@ +import Image from 'next/image' + +import { useParams } from 'common' +import { InlineLink } from 'components/ui/InlineLink' +import { BASE_PATH } from 'lib/constants' +import { useIsSecurityNotificationsEnabled } from './FeaturePreviewContext' + +export const SecurityNotificationsPreview = () => { + const { ref } = useParams() + const isSecurityNotificationsEnabled = useIsSecurityNotificationsEnabled() + + return ( +
+ Security notifications preview +
+

+ Try out our expanded set of{' '} + + email templates + {' '} + with support for security-related notifications. +

+

Enabling this preview will:

+
    +
  • Add a dedicated sidebar section for contact methods like email and SMS
  • +
  • Add new email templates for security-related notifications
  • +
  • Move each (existing and new) template into its own dynamic route
  • +
+

+ These changes are necessary to support incoming security-related notification templates. + Given that the list of our email templates is doubling in size, this change requires some + wider interface changes. Ones that we think make for a clearer experience overall. Win + win! +

+
+
+ ) +} diff --git a/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx b/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx index 70c4cfe7b3d..5e869db10b5 100644 --- a/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx +++ b/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx @@ -1,20 +1,21 @@ import { useRouter } from 'next/router' import { PropsWithChildren } from 'react' -import { useParams } from 'common' +import { useFlag, useParams } from 'common' +import { useIsSecurityNotificationsEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { ProductMenu } from 'components/ui/ProductMenu' import { useAuthConfigPrefetch } from 'data/auth/auth-config-query' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { withAuth } from 'hooks/misc/withAuth' import { ProjectLayout } from '../ProjectLayout/ProjectLayout' import { generateAuthMenu } from './AuthLayout.utils' -import { useFlag } from 'common' const AuthProductMenu = () => { const router = useRouter() const { ref: projectRef = 'default' } = useParams() const authenticationShowOverview = useFlag('authOverviewPage') + const authenticationShowSecurityNotifications = useIsSecurityNotificationsEnabled() const { authenticationSignInProviders, @@ -46,6 +47,7 @@ const AuthProductMenu = () => { authenticationAttackProtection, authenticationAdvanced, authenticationShowOverview, + authenticationShowSecurityNotifications, })} /> ) diff --git a/apps/studio/components/layouts/AuthLayout/AuthLayout.utils.ts b/apps/studio/components/layouts/AuthLayout/AuthLayout.utils.ts index 67bad8717a3..27f0a52e396 100644 --- a/apps/studio/components/layouts/AuthLayout/AuthLayout.utils.ts +++ b/apps/studio/components/layouts/AuthLayout/AuthLayout.utils.ts @@ -11,6 +11,7 @@ export const generateAuthMenu = ( authenticationAttackProtection: boolean authenticationAdvanced: boolean authenticationShowOverview: boolean + authenticationShowSecurityNotifications: boolean } ): ProductMenuGroup[] => { const { @@ -21,6 +22,7 @@ export const generateAuthMenu = ( authenticationAttackProtection, authenticationAdvanced, authenticationShowOverview, + authenticationShowSecurityNotifications, } = flags ?? {} return [ @@ -33,6 +35,26 @@ export const generateAuthMenu = ( { name: 'Users', key: 'users', url: `/project/${ref}/auth/users`, items: [] }, ], }, + ...(authenticationEmails && authenticationShowSecurityNotifications && IS_PLATFORM + ? [ + { + title: 'Notifications', + items: [ + ...(authenticationEmails + ? [ + { + name: 'Email', + key: 'email', + pages: ['templates', 'smtp'], + url: `/project/${ref}/auth/templates`, + items: [], + }, + ] + : []), + ], + }, + ] + : []), { title: 'Configuration', items: [ @@ -71,7 +93,7 @@ export const generateAuthMenu = ( }, ] : []), - ...(authenticationEmails + ...(authenticationEmails && !authenticationShowSecurityNotifications ? [ { name: 'Emails', diff --git a/apps/studio/public/img/previews/security-notifications-preview.png b/apps/studio/public/img/previews/security-notifications-preview.png new file mode 100644 index 00000000000..262ed16f4d9 Binary files /dev/null and b/apps/studio/public/img/previews/security-notifications-preview.png differ diff --git a/packages/common/constants/local-storage.ts b/packages/common/constants/local-storage.ts index c77786c7661..3784822e32b 100644 --- a/packages/common/constants/local-storage.ts +++ b/packages/common/constants/local-storage.ts @@ -18,6 +18,7 @@ export const LOCAL_STORAGE_KEYS = { UI_PREVIEW_BRANCHING_2_0: 'supabase-ui-branching-2-0', UI_PREVIEW_ADVISOR_RULES: 'supabase-ui-advisor-rules', UI_PREVIEW_NEW_STORAGE_UI: 'new-storage-ui', + UI_PREVIEW_SECURITY_NOTIFICATIONS: 'security-notifications', NEW_LAYOUT_NOTICE_ACKNOWLEDGED: 'new-layout-notice-acknowledge', TABS_INTERFACE_ACKNOWLEDGED: 'tabs-interface-acknowledge',