From 9f6faa65bb4b2bf5ec1eef1ca3402073fae33aa6 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Fri, 11 Apr 2025 00:11:07 -0230 Subject: [PATCH] Fix toggling of roles apply to all projects (#34871) * Store initial state * fixes * Minor --------- Co-authored-by: Joshen Lim --- .../SubscriptionPlanUpdateDialog.tsx | 40 ++++----- .../UpdateRolesConfirmationModal.tsx | 2 +- .../UpdateRolesPanel/UpdateRolesPanel.tsx | 83 +++++++++++-------- .../ui-patterns/Dialogs/ConfirmationModal.tsx | 4 +- 4 files changed, 71 insertions(+), 58 deletions(-) diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx index 280e43a0adf..e41ecd778cf 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx @@ -1,34 +1,36 @@ import { useQueryClient } from '@tanstack/react-query' -import { InfoIcon, Check, ExternalLink } from 'lucide-react' +import { Check, ExternalLink, InfoIcon } from 'lucide-react' import Link from 'next/link' -import { useState, useEffect } from 'react' -import { toast } from 'sonner' +import { useEffect, useState } from 'react' import tweets from 'shared-data/tweets' +import { toast } from 'sonner' +import { billingPartnerLabel } from 'components/interfaces/Billing/Subscription/Subscription.utils' import AlertError from 'components/ui/AlertError' import ShimmeringLoader from 'components/ui/ShimmeringLoader' +import { organizationKeys } from 'data/organizations/keys' +import { OrganizationBillingSubscriptionPreviewResponse } from 'data/organizations/organization-billing-subscription-preview' +import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation' +import { SubscriptionTier } from 'data/subscriptions/types' +import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants' +import { formatCurrency } from 'lib/helpers' import { + Badge, + Button, + Card, + CardContent, + Dialog, + DialogContent, + HoverCard, + HoverCardContent, + HoverCardTrigger, Table, TableBody, TableCell, TableRow, - HoverCard, - HoverCardContent, - HoverCardTrigger, - Card, - CardContent, - Badge, } from 'ui' -import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation' -import { organizationKeys } from 'data/organizations/keys' -import { formatCurrency } from 'lib/helpers' -import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants' -import { SubscriptionTier } from 'data/subscriptions/types' -import { billingPartnerLabel } from 'components/interfaces/Billing/Subscription/Subscription.utils' -import PaymentMethodSelection from './PaymentMethodSelection' -import { Button, Dialog, DialogContent } from 'ui' import { InfoTooltip } from 'ui-patterns/info-tooltip' -import { OrganizationBillingSubscriptionPreviewResponse } from 'data/organizations/organization-billing-subscription-preview' +import PaymentMethodSelection from './PaymentMethodSelection' const getRandomTweet = () => { const filteredTweets = tweets.filter((it) => it.text.length < 180) @@ -576,7 +578,7 @@ const SubscriptionPlanUpdateDialog = ({ onClick={onUpdateSubscription} className="flex-1" > - Confirm {planMeta?.change_type === 'downgrade' ? 'Downgrade' : 'Upgrade'} + Confirm {planMeta?.change_type === 'downgrade' ? 'downgrade' : 'upgrade'} diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesConfirmationModal.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesConfirmationModal.tsx index 1756678bdc9..fc37dd4fe91 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesConfirmationModal.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesConfirmationModal.tsx @@ -219,7 +219,7 @@ export const UpdateRolesConfirmationModal = ({ return (
  • From {originalRoleName} to{' '} - {updatedRole?.name} on{' '} + {updatedRole?.name ?? 'Unknown'} on{' '} {project?.name ?? 'organization'} diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanel.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanel.tsx index 93091c695d6..4782245ccd3 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanel.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanel.tsx @@ -3,6 +3,7 @@ import { X } from 'lucide-react' import { useEffect, useState } from 'react' import { useParams } from 'common' +import { ButtonTooltip } from 'components/ui/ButtonTooltip' import { DocsButton } from 'components/ui/DocsButton' import { useOrganizationRolesV2Query } from 'data/organization-members/organization-roles-query' import { OrganizationMember } from 'data/organizations/organization-members-query' @@ -85,21 +86,26 @@ export const UpdateRolesPanel = ({ visible, member, onClose }: UpdateRolesPanelP allRoles !== undefined ? formatMemberRoleToProjectRoleConfiguration(member, allRoles, projects ?? []) : [] + const originalConfigurationType = + originalConfiguration.length === 1 && + !!orgScopedRoles.find((r) => r.id === originalConfiguration[0].roleId) + ? 'org-scope' + : 'project-scope' + const orgProjects = (projects ?? []).filter((p) => p.organization_id === organization?.id) const isApplyingRoleToAllProjects = projectsRoleConfiguration.length === 1 && projectsRoleConfiguration[0]?.ref === undefined const canSaveRoles = projectsRoleConfiguration.length > 0 const lowerPermissionsRole = orgScopedRoles.find((r) => r.name === 'Developer')?.id - const sortByObject: any = ['Owner', 'Administrator', 'Developer'].reduce((obj, item, index) => { - return { ...obj, [item]: index } - }, {}) const noAccessProjects = orgProjects.filter((project) => { return !projectsRoleConfiguration.some((p) => p.ref === project.ref) }) const numberOfProjectsWithAccess = orgProjects.length - noAccessProjects.length const numberOfAccessHasChanges = originalConfiguration.length !== noAccessProjects.length + const hasNoChanges = isEqual(projectsRoleConfiguration, originalConfiguration) + const onSelectProject = (ref: string) => { setProjectsRoleConfiguration( projectsRoleConfiguration.concat({ @@ -131,17 +137,27 @@ export const UpdateRolesPanel = ({ visible, member, onClose }: UpdateRolesPanelP } } - const onToggleApplyToAllProjects = () => { - const roleIdToApply = - projectsRoleConfiguration[0]?.roleId ?? lowerPermissionsRole ?? orgScopedRoles[0].id - if (isApplyingRoleToAllProjects) { - setProjectsRoleConfiguration( - orgProjects.map((p) => { - return { ref: p.ref, projectId: p.id, roleId: roleIdToApply } - }) - ) + const onToggleApplyToAllProjects = (isApplyAllProjects: boolean) => { + const roleIdToApply = lowerPermissionsRole ?? orgScopedRoles[0].id + + if (isApplyAllProjects) { + if (originalConfigurationType === 'org-scope') { + console.log('Reset: Org scoped') + setProjectsRoleConfiguration(originalConfiguration) + } else { + setProjectsRoleConfiguration([{ ref: undefined, roleId: roleIdToApply }]) + } } else { - setProjectsRoleConfiguration([{ ref: undefined, roleId: roleIdToApply }]) + if (originalConfigurationType === 'project-scope') { + console.log('Reset: Project scoped') + setProjectsRoleConfiguration(originalConfiguration) + } else { + setProjectsRoleConfiguration( + orgProjects.map((p) => { + return { ref: p.ref, projectId: p.id, roleId: roleIdToApply } + }) + ) + } } } @@ -275,7 +291,7 @@ export const UpdateRolesPanel = ({ visible, member, onClose }: UpdateRolesPanelP {role.name} @@ -288,22 +304,21 @@ export const UpdateRolesPanel = ({ visible, member, onClose }: UpdateRolesPanelP )} {!isApplyingRoleToAllProjects && ( - - -