From 71305867b26e4c97d829a6744a508da842e65def Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 13 Feb 2024 23:46:24 +0800 Subject: [PATCH] Chore/fix layout header alignment right buttons (#21225) * Fix NotificationV2 popover trigger alignment * Remove unnecessary alt prop on FeedbackDropdown * Deprecate old NotificationsPopover folder --- .../layouts/AppLayout/AppHeader.tsx | 13 +- .../FeedbackDropdown/FeedbackDropdown.tsx | 25 +- .../LayoutHeader/HelpPopover.tsx | 48 +-- .../LayoutHeader/LayoutHeader.tsx | 16 +- .../NotificationActions.tsx | 83 ---- .../NotificationsPopover/NotificationRow.tsx | 104 ----- .../NotificationRows.utils.tsx | 178 --------- .../NotificationsPopover/index.tsx | 347 ----------------- .../NotificationsPopover.tsx | 362 +++++++++--------- 9 files changed, 212 insertions(+), 964 deletions(-) delete mode 100644 apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationActions.tsx delete mode 100644 apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationRow.tsx delete mode 100644 apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationRows.utils.tsx delete mode 100644 apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx diff --git a/apps/studio/components/layouts/AppLayout/AppHeader.tsx b/apps/studio/components/layouts/AppLayout/AppHeader.tsx index fc1e317dd74..a93adbc4a19 100644 --- a/apps/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/apps/studio/components/layouts/AppLayout/AppHeader.tsx @@ -1,11 +1,11 @@ +import { useParams } from 'common' import Link from 'next/link' import { useRouter } from 'next/router' -import { useParams } from 'common' import { useFlag, useSelectedOrganization, useSelectedProject } from 'hooks' import FeedbackDropdown from '../ProjectLayout/LayoutHeader/FeedbackDropdown' import HelpPopover from '../ProjectLayout/LayoutHeader/HelpPopover' -import NotificationsPopover from '../ProjectLayout/LayoutHeader/NotificationsPopover' +import NotificationsPopoverV2 from '../ProjectLayout/LayoutHeader/NotificationsPopoverV2/NotificationsPopover' import BranchDropdown from './BranchDropdown' import EnableBranchingButton from './EnableBranchingButton/EnableBranchingButton' import OrganizationDropdown from './OrganizationDropdown' @@ -13,6 +13,9 @@ import ProjectDropdown from './ProjectDropdown' import SettingsButton from './SettingsButton' import UserSettingsDropdown from './UserSettingsDropdown' +// [Joshen] Just FYI this is only for Nav V2 which is still going through design iteration +// Component is not currently in use + const AppHeader = () => { const router = useRouter() const { ref } = useParams() @@ -47,9 +50,9 @@ const AppHeader = () => {
- - - + + +
diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx index 4222875c6b2..777ede00cce 100644 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx +++ b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx @@ -1,18 +1,8 @@ import { useState } from 'react' -import { - Button, - IconMessageCircle, - PopoverContent_Shadcn_, - PopoverTrigger_Shadcn_, - Popover_Shadcn_, -} from 'ui' +import { Button, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_ } from 'ui' import FeedbackWidget from './FeedbackWidget' -interface FeedbackDropdownProps { - alt?: boolean -} - -const FeedbackDropdown = ({ alt = false }: FeedbackDropdownProps) => { +const FeedbackDropdown = () => { const [isOpen, setIsOpen] = useState(false) const [feedback, setFeedback] = useState('') const [screenshot, setScreenshot] = useState() @@ -37,16 +27,7 @@ const FeedbackDropdown = ({ alt = false }: FeedbackDropdownProps) => { }} > - diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx index 492cb55b66a..4a65425fbeb 100644 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx +++ b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx @@ -17,11 +17,7 @@ import { Popover_Shadcn_, } from 'ui' -interface HelpPopoverProps { - alt?: boolean -} - -const HelpPopover = ({ alt = false }: HelpPopoverProps) => { +const HelpPopover = () => { const router = useRouter() const projectRef = router.query.ref const supportUrl = `/support/new${projectRef ? `?ref=${projectRef}` : ''}` @@ -34,36 +30,28 @@ const HelpPopover = ({ alt = false }: HelpPopoverProps) => {
+ />
- {alt ? ( - - - -
-

Help

-
-
-
- ) : null} + + + +
+

Help

+
+
+
diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx index bdff4502d44..124adf73571 100644 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx +++ b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx @@ -15,7 +15,6 @@ import { IS_PLATFORM } from 'lib/constants' import BreadcrumbsView from './BreadcrumbsView' import FeedbackDropdown from './FeedbackDropdown' import HelpPopover from './HelpPopover' -import NotificationsPopover from './NotificationsPopover' import NotificationsPopoverV2 from './NotificationsPopoverV2/NotificationsPopover' const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerBorder = true }: any) => { @@ -24,7 +23,6 @@ const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerBorder = const selectedOrganization = useSelectedOrganization() const enableBranchManagement = useFlag('branchManagement') - const notificationsV2 = useFlag('notificationsV2') const isBranchingEnabled = selectedProject?.is_branch_enabled === true || selectedProject?.parent_project_ref !== undefined @@ -117,19 +115,13 @@ const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerBorder = {/* Additional breadcrumbs are supplied */}
-
+
{customHeaderComponents && customHeaderComponents} - {IS_PLATFORM && } - {IS_PLATFORM && !notificationsV2 && ( - <> - - - - )} - {IS_PLATFORM && notificationsV2 && ( + {IS_PLATFORM && ( <> + - + )}
diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationActions.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationActions.tsx deleted file mode 100644 index 55e43142abb..00000000000 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationActions.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { Action, ActionReason, ActionType } from '@supabase/shared-types/out/notifications' -import Link from 'next/link' -import { useRouter } from 'next/router' -import { Fragment } from 'react' -import { Button, IconExternalLink } from 'ui' - -import { Organization } from 'types' - -interface NotificationActionsProps { - organization: Organization - changelogLink?: string - availableActions: Action[] - onSelectRestartProject: () => void - onSelectApplyMigration: () => void - onSelectRollbackMigration: () => void -} - -const NotificationActions = ({ - organization, - changelogLink, - availableActions, - onSelectRestartProject, - onSelectApplyMigration, - onSelectRollbackMigration, -}: NotificationActionsProps) => { - const router = useRouter() - - const onSelectUpgradePlan = () => { - return router.push(`/org/${organization.slug}/billing?panel=subscriptionPlan`) - } - - const renderActionButton = (action: Action) => { - switch (action.action_type) { - case ActionType.UpgradeProjectToPro: - return ( - - ) - case ActionType.SchedulePostgresRestart: - return ( - - ) - case ActionType.MigratePostgresSchema: - if (action.reason === ActionReason.Rollback) { - return ( - - ) - } else { - return ( - - ) - } - } - } - - return ( -
- {availableActions.map((action) => { - return ( - - {renderActionButton(action)} - - ) - })} - {changelogLink && ( - - )} -
- ) -} - -export default NotificationActions diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationRow.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationRow.tsx deleted file mode 100644 index 514cd5e2ac1..00000000000 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationRow.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { Notification, NotificationStatus } from '@supabase/shared-types/out/notifications' -import dayjs from 'dayjs' -import { Button, IconX } from 'ui' - -import { useNotificationsDismissMutation } from 'data/notifications/notifications-dismiss-mutation' -import { useOrganizationsQuery } from 'data/organizations/organizations-query' -import { ProjectInfo, useProjectsQuery } from 'data/projects/projects-query' -import { useStore } from 'hooks' -import NotificationActions from './NotificationActions' -import { formatNotificationCTAText, formatNotificationText } from './NotificationRows.utils' - -export interface NotificationRowProps { - notification: Notification - onSelectRestartProject: (project: ProjectInfo, notification: Notification) => void - onSelectApplyMigration: (project: ProjectInfo, notification: Notification) => void - onSelectRollbackMigration: (project: ProjectInfo, notification: Notification) => void -} - -const NotificationRow = ({ - notification, - onSelectRestartProject, - onSelectApplyMigration, - onSelectRollbackMigration, -}: NotificationRowProps) => { - const { ui } = useStore() - const { data: projects } = useProjectsQuery() - const { data: organizations } = useOrganizationsQuery() - const project = projects?.find((project) => project.id === notification.project_id) - const organization = organizations?.find((org) => org.id === project?.organization_id) - - const insertedAt = dayjs(notification.inserted_at).format('DD MMM YYYY, HH:mma') - const changelogLink = (notification.data as any).changelog_link - const availableActions = notification.meta?.actions_available ?? [] - - const { mutate: dismissNotifications, isLoading: isDismissing } = useNotificationsDismissMutation( - { - onError: (error) => { - ui.setNotification({ - error, - category: 'error', - message: `Failed to dismiss notification: ${error.message}`, - duration: 4000, - }) - }, - } - ) - - const dismissNotification = async (notificationId: string) => { - if (!notificationId) return - dismissNotifications({ ids: [notificationId] }) - } - - if (!project || !organization) return null - - return ( -
-
- {notification.notification_status !== NotificationStatus.Seen && ( -
- )} -
-
-
-
- {formatNotificationText(project.name, notification)} - {formatNotificationCTAText(availableActions)} -

{insertedAt}

-
-
-
-
-
-
- {(availableActions.length > 0 || changelogLink !== undefined) && ( -
- onSelectRestartProject(project, notification)} - onSelectApplyMigration={() => onSelectApplyMigration(project, notification)} - onSelectRollbackMigration={() => onSelectRollbackMigration(project, notification)} - /> -
- )} -
-
- ) -} - -export default NotificationRow diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationRows.utils.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationRows.utils.tsx deleted file mode 100644 index 4e6875d16b1..00000000000 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/NotificationRows.utils.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import { - Action, - ActionType, - ExtensionsUpgrade, - Notification, - NotificationName, - ServerUpgrade, - ServiceUpgrade, - ViolatedLimit, -} from '@supabase/shared-types/out/notifications' -import dayjs from 'dayjs' -import Link from 'next/link' -import { Button, IconArrowRight, IconExternalLink } from 'ui' - -import { Markdown } from 'components/interfaces/Markdown' - -export const formatNotificationText = (projectName: string, notification: Notification) => { - if (notification.data.name === NotificationName.ProjectExceedingTierLimit) { - const { violations } = notification.data - const violationsText = violations - .map((violation: ViolatedLimit) => violation.dimension as string) - .reduce((a: string, b: string) => `${a}, ${b}`) - - return ( -

- Your project "{projectName}" has exceeded its limits in the following areas:{' '} - {violationsText}. -

- ) - } else if (notification.data.name === NotificationName.PostgresqlUpgradeAvailable) { - const { upgrade_type, additional } = notification.data - - if (upgrade_type === 'postgresql-server') { - const { version_to } = additional as ServerUpgrade - return ( -

- New version of Postgres ({version_to}) is now available for project "{projectName}". -

- ) - } else if (upgrade_type === 'extensions') { - const { name, version_to } = additional as ExtensionsUpgrade - return ( -

- New version of "{name}" ({version_to}) is now available for project "{projectName}". -

- ) - } else if (upgrade_type === 'schema-migration') { - const { name, version_to } = additional as ExtensionsUpgrade - return ( -
-

A new schema migration is available for your project "{projectName}".

-
    -
  1. -
    -

    {name}

    - -

    {version_to}

    -
    -
  2. -
-
- ) - } - return '' - } else if (notification.data.name === NotificationName.PostgresqlUpgradeCompleted) { - const { upgrade_type, additional } = notification.data - - if (upgrade_type === 'postgresql-server') { - const { version_to } = additional as ServerUpgrade - return ( -

- Postgres ({version_to}) has been successfully updated to {version_to} for project " - {projectName}". -

- ) - } else if (upgrade_type === 'extensions') { - const { name, version_to } = additional as ExtensionsUpgrade - return ( -

- The extension "{name}" has been successfully updated to {version_to} for project " - {projectName}". -

- ) - } else if (upgrade_type === 'schema-migration') { - const { version_to } = additional - return ( -

- The schema migration "{version_to}" has been successfully applied for project " - {projectName}". -

- ) - } - } else if (notification.data.name === NotificationName.ProjectUpdateCompleted) { - const { upgrades } = notification.data - return ( -
-

- The following services have been successfully updated for project "{projectName}": -

-
    - {upgrades.map((upgrade: ServiceUpgrade) => ( -
  1. -
    -

    {upgrade.name}

    - -

    {upgrade.version_to}

    - {upgrade.changelog_link && ( -
    - - - -
    - )} -
    -
  2. - ))} -
-
- ) - } else if (notification.data.name === NotificationName.ProjectInformational) { - const buttons = notification.data.linked_buttons ?? [] - return ( - <> - - {buttons.map((button, index) => { - return ( - - - - ) - })} - - ) - } else { - return ( -

- Unknown notification type: {notification.notification_name} - Please reach out to support - for more information. -

- ) - } -} - -export const formatNotificationCTAText = (availableActions: Action[]) => { - const [action] = availableActions - if (!action) return

- - switch (action.action_type) { - case ActionType.SchedulePostgresRestart: - return

Restart your project to get the latest updates.

- case ActionType.UpgradeProjectToPro: - return

Upgrade your project to ensure continued availability.

- case ActionType.PgBouncerRestart: - return

Restart your connection pooler to get the latest updates.

- case ActionType.MigratePostgresSchema: - if (action.deadline) { - return ( -

- This patch will be automatically applied after{' '} - {dayjs(new Date(action.deadline)).format('DD MMM YYYY, HH:mma')} -

- ) - } - default: - return '' - } -} diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx deleted file mode 100644 index 6ca59e7b333..00000000000 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx +++ /dev/null @@ -1,347 +0,0 @@ -import * as Tooltip from '@radix-ui/react-tooltip' -import { - ActionType, - Notification, - NotificationStatus, - PostgresqlUpgradeData, -} from '@supabase/shared-types/out/notifications' -import { useQueryClient } from '@tanstack/react-query' -import dayjs from 'dayjs' -import { useRouter } from 'next/router' -import { Fragment, useState } from 'react' -import { Button, IconArrowRight, IconBell, IconInbox, Popover } from 'ui' - -import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog' -import { useNotificationsQuery } from 'data/notifications/notifications-query' -import { useNotificationsUpdateMutation } from 'data/notifications/notifications-update-mutation' -import { getProjectDetail } from 'data/projects/project-detail-query' -import { useProjectRestartServicesMutation } from 'data/projects/project-restart-services-mutation' -import { ProjectInfo, setProjectPostgrestStatus } from 'data/projects/projects-query' -import { useStore } from 'hooks' -import { delete_, post } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' -import NotificationRow from './NotificationRow' - -interface NotificationsPopoverProps { - alt?: boolean -} - -const NotificationsPopover = ({ alt = false }: NotificationsPopoverProps) => { - const router = useRouter() - const { meta, ui } = useStore() - const queryClient = useQueryClient() - - const { data: notifications } = useNotificationsQuery() - const { mutate: updateNotifications } = useNotificationsUpdateMutation({ - onError: () => console.error('Failed to update notifications'), - }) - - const [projectToRestart, setProjectToRestart] = useState() - const [projectToApplyMigration, setProjectToApplyMigration] = useState() - const [projectToRollbackMigration, setProjectToRollbackMigration] = useState() - const [targetNotification, setTargetNotification] = useState() - - const { mutate: restartProjectServices } = useProjectRestartServicesMutation({ - onSuccess: (res, variables) => { - setProjectToRestart(undefined) - setTargetNotification(undefined) - - setProjectPostgrestStatus(queryClient, variables.ref, 'OFFLINE') - ui.setNotification({ category: 'success', message: `Restarting services` }) - router.push(`/project/${variables.ref}`) - }, - }) - - const newNotifications = - notifications?.filter( - (notification) => notification.notification_status === NotificationStatus.New - ) ?? [] - const hasNewNotifications = newNotifications.length > 0 - - const onOpenChange = async (open: boolean) => { - // Mark notifications as seen - if (!open) { - const notificationIds = - notifications - ?.filter((notification) => notification.notification_status === NotificationStatus.New) - .map((notification) => notification.id) ?? [] - if (notificationIds.length > 0) updateNotifications({ ids: notificationIds }) - } - } - - const onConfirmProjectRestart = async () => { - if (!projectToRestart || !targetNotification) return - - const { id } = targetNotification - const { ref, region } = projectToRestart - if (!ref || !region) return console.error('Ref and region required') - - const serviceNamesByActionName: Record = { - [ActionType.PgBouncerRestart]: 'pgbouncer', - [ActionType.SchedulePostgresRestart]: 'postgresql', - [ActionType.MigratePostgresSchema]: 'postgresql', - } - const services: string[] = targetNotification.meta.actions_available - .map((action) => action.action_type) - .filter((actionName) => Object.keys(serviceNamesByActionName).indexOf(actionName) !== -1) - .map((actionName) => serviceNamesByActionName[actionName]) - - restartProjectServices({ ref, region, services: services as any, source_notification_id: id }) - } - - const onConfirmProjectApplyMigration = async () => { - if (!projectToApplyMigration) return - - const data = targetNotification?.data as PostgresqlUpgradeData - const { resource } = data.additional as any - if (!resource) return - - // [Joshen] Leaving this as an exception for RQ due to dynamic URL - const res = await post(`${API_URL}/database/${projectToApplyMigration.ref}/${resource}`, {}) - if (!res.error) { - const project = await getProjectDetail({ ref: projectToApplyMigration.ref }) - if (project) meta.setProjectDetails(project) - - ui.setNotification({ - category: 'success', - message: `Successfully applied migration for project "${projectToApplyMigration.name}"`, - }) - } else { - ui.setNotification({ - error: res.error, - category: 'error', - message: `Failed to apply migration: ${res.error.message}`, - }) - } - setProjectToApplyMigration(undefined) - } - - const onConfirmProjectRollbackMigration = async () => { - if (!projectToRollbackMigration) return - - const data = targetNotification?.data as PostgresqlUpgradeData - const { resource } = data.additional as any - if (!resource) return - - // [Joshen] Leaving this as an exception for RQ due to dynamic URL - const res = await delete_( - `${API_URL}/database/${projectToRollbackMigration.ref}/${resource}`, - {} - ) - if (!res.error) { - const project = await getProjectDetail({ ref: projectToRollbackMigration.ref }) - if (project) meta.setProjectDetails(project) - - ui.setNotification({ - category: 'success', - message: `Successfully rolled back migration for project "${projectToRollbackMigration.name}"`, - }) - } else { - ui.setNotification({ - error: res.error, - category: 'error', - message: `Failed to roll back migration: ${res.error.message}`, - }) - } - setProjectToRollbackMigration(undefined) - } - - if (!notifications || !Array.isArray(notifications)) return null - - return ( - <> - -
-

Notifications

- {/* Area for improvement: Paginate notifications and show in a side panel */} - {/*

- See all{' '} - {notifications.length > MAX_NOTIFICATIONS_TO_SHOW && `(${notifications.length})`} -

*/} -
-
- {notifications.length === 0 ? ( -
-

No notifications available

-
- ) : ( - <> - {notifications.map((notification, i: number) => ( - - { - setProjectToRestart(project) - setTargetNotification(notification) - }} - onSelectApplyMigration={(project, notification) => { - setProjectToApplyMigration(project) - setTargetNotification(notification) - }} - onSelectRollbackMigration={(project, notification) => { - setProjectToRollbackMigration(project) - setTargetNotification(notification) - }} - /> - {i !== notifications.length - 1 && } - - ))} - - )} -
-
- } - > - - -
- {hasNewNotifications && ( - <> - {alt ? null : ( -
-
-
-
- )} - - )} -
- ) : alt ? ( - - ) : ( - - ) - } - iconRight={ - hasNewNotifications ? ( - <> - {alt ? ( - - ) : ( - - )} - - ) : null - } - > - - -
- - - - -
- Notifications -
-
-
- - - - setProjectToRestart(undefined)} - onSelectConfirm={onConfirmProjectRestart} - /> - - -
-

The following schema migration will be applied to the project

-
    -
  1. -
    -

    {(targetNotification?.data as any)?.additional?.name}

    - -

    {(targetNotification?.data as any)?.additional?.version_to}

    -
    -
  2. -
-
-

- This change can be rolled back anytime up till{' '} - {dayjs( - new Date(targetNotification?.meta.actions_available?.[0]?.deadline ?? 0) - ).format('DD MMM YYYY, HH:mma ZZ')} - , after which the changes will be finalized and can no longer be undone. -

-
- } - buttonLabel="Confirm" - buttonLoadingLabel="Confirm" - onSelectCancel={() => setProjectToApplyMigration(undefined)} - onSelectConfirm={onConfirmProjectApplyMigration} - /> - - -
-

The following schema migration will be rolled back for the project

-
    -
  1. -
    -

    {(targetNotification?.data as any)?.additional?.name}

    - -

    {(targetNotification?.data as any)?.additional?.version_to}

    -
    -
  2. -
-
-

- This migration however will still be applied and finalized after{' '} - {dayjs( - new Date(targetNotification?.meta.actions_available?.[0]?.deadline ?? 0) - ).format('DD MMM YYYY, HH:mma ZZ')} - , after which the changes can no longer be undone. -

-
- } - buttonLabel="Confirm" - buttonLoadingLabel="Confirm" - onSelectCancel={() => setProjectToRollbackMigration(undefined)} - onSelectConfirm={onConfirmProjectRollbackMigration} - /> - - ) -} - -export default NotificationsPopover diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopoverV2/NotificationsPopover.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopoverV2/NotificationsPopover.tsx index a255fb4cc62..b3c47c85b3a 100644 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopoverV2/NotificationsPopover.tsx +++ b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopoverV2/NotificationsPopover.tsx @@ -65,7 +65,7 @@ const NotificationsPopoverV2 = () => { projects: snap.filterProjects, }, }) - const { data: summary, isSuccess: isSuccessSummary } = useNotificationsSummaryQuery() + const { data: summary } = useNotificationsSummaryQuery() const { mutate: updateNotifications } = useNotificationsV2UpdateMutation() const { mutate: archiveAllNotifications, isLoading: isArchiving } = useNotificationsArchiveAllMutation({ @@ -84,192 +84,188 @@ const NotificationsPopoverV2 = () => { } return ( -
- {/* wrapped in a div to stop space-x class impacting popover content */} - { - setOpen(open) - if (!open) markNotificationsRead() - }} - > - -
- {notifications.length > 0 && activeTab === 'inbox' && ( -
- +

{summary?.unread_count}

+
+ ) : null + } + iconRight={ + + } + /> + + +
+

Notifications

+
+ { + setActiveTab(tab as 'inbox' | 'archived') + if (tab === 'archived' && snap.filterStatuses.includes('unread')) { + snap.setFilters('unread', 'status') + } + }} + value={activeTab} + > +
+ + + Inbox +
9 ? 'px-0.5 w-auto' : 'w-4', + ])} + > + {summary?.unread_count} +
+
+ + Archived + +
+ +
+
+
+
+
+ {isLoading && ( +
+
)} - - -
+ {isError && ( +
+ +
+ )} + {isSuccess && ( +
+ {notifications.length > 0 && + !(activeTab === 'archived' && snap.filterStatuses.includes('unread')) ? ( + + +
+ } + itemProps={{ + setRowHeight: (idx: number, height: number) => { + if (rowHeights.current) { + rowHeights.current = { ...rowHeights.current, [idx]: height } + } + }, + getProject: (ref: string) => projects?.find((project) => project.ref === ref)!, + getOrganization: (id: number) => organizations?.find((org) => org.id === id)!, + onUpdateNotificationStatus: (id: string, status: 'archived' | 'seen') => { + updateNotifications({ ids: [id], status }) + }, + queueMarkRead: (id: string) => { + if (markedRead.current && !markedRead.current.includes(id)) { + markedRead.current.push(id) + } + }, + }} + getItemSize={(idx: number) => rowHeights?.current?.[idx] ?? 56} + hasNextPage={hasNextPage} + isLoadingNextPage={isFetchingNextPage} + onLoadNextPage={() => fetchNextPage()} + /> + ) : ( +
+ +
+

+ {activeTab === 'archived' + ? `No archived notifications${ + snap.numFiltersApplied > 0 + ? ` based on the ${snap.numFiltersApplied} filter${ + snap.numFiltersApplied > 1 ? 's' : '' + } applied` + : '' + }` + : snap.numFiltersApplied > 0 + ? `No notifications based on the ${snap.numFiltersApplied} filter${ + snap.numFiltersApplied > 1 ? 's' : '' + } applied` + : 'All caught up'} +

+

+ {activeTab === 'archived' + ? 'Notifications that you have previously archived will be shown here' + : 'You will be notified here for any notices on your organizations and projects'} +

+
+
+ )} + + )} + + {notifications.length > 0 && activeTab === 'inbox' && ( +
+ +
+ )} +
+ ) }