Skip to content

Commit

Permalink
fix(console): Group enablement bugs #2773 (#2775)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cosmin-Parvulescu committed Nov 24, 2023
1 parent e18b7fd commit 6c9c706
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 72 deletions.
1 change: 1 addition & 0 deletions apps/console/app/components/IconPicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ export default function IconPicker({
<Text type="span" size="xs">
Upload
</Text>
{iconURL && <input type="hidden" name={id} value={iconURL} />}
<input
type="file"
id={`${id}_file`}
Expand Down
23 changes: 19 additions & 4 deletions apps/console/app/routes/apps/$clientId/team.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ import {
import type { AccountURN } from '@proofzero/urns/account'
import type { ActionFunction, LoaderFunction } from '@remix-run/cloudflare'
import type { errorsTeamProps, notificationHandlerType } from '~/types'
import { BadRequestError } from '@proofzero/errors'
import { getRollupReqFunctionErrorWrapper } from '@proofzero/utils/errors'
import { BadRequestError, UnauthorizedError } from '@proofzero/errors'
import {
getErrorCause,
getRollupReqFunctionErrorWrapper,
} from '@proofzero/utils/errors'
import {
Dropdown,
DropdownSelectListItem,
Expand Down Expand Up @@ -114,8 +117,14 @@ export const action: ActionFunction = getRollupReqFunctionErrorWrapper(
account: accountURN,
clientId,
})
} catch (e) {
errors.upserteAppContactAddress = "Failed to upsert app's contact address"
} catch (error) {
const cause = getErrorCause(error)
if (cause instanceof UnauthorizedError) {
errors.upsertAppContactAddress = "You don't have permission to do this"
} else {
errors.upsertAppContactAddress =
"Failed to upsert app's contact address"
}
}

// Remix preserves route from before
Expand Down Expand Up @@ -273,6 +282,12 @@ export default () => {
)}
</div>

{errors?.upsertAppContactAddress && (
<Text size="sm" weight="normal" className="text-red-500">
{errors.upsertAppContactAddress}
</Text>
)}

<Text size="sm" weight="normal" className="text-gray-500">
This will be used for notifications about your application
</Text>
Expand Down
8 changes: 7 additions & 1 deletion apps/console/app/routes/apps/delete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {
getErrorCause,
getRollupReqFunctionErrorWrapper,
} from '@proofzero/utils/errors'
import { BadRequestError, InternalServerError } from '@proofzero/errors'
import {
BadRequestError,
InternalServerError,
UnauthorizedError,
} from '@proofzero/errors'

export const action: ActionFunction = getRollupReqFunctionErrorWrapper(
async ({ request, context }) => {
Expand All @@ -33,6 +37,8 @@ export const action: ActionFunction = getRollupReqFunctionErrorWrapper(
const traceparent = context.traceSpan.getTraceParent()
if (cause instanceof BadRequestError) {
throw cause
} else if (cause instanceof UnauthorizedError) {
throw error
} else {
console.error(error)
throw JsonError(
Expand Down
18 changes: 13 additions & 5 deletions apps/console/app/routes/onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const loader: LoaderFunction = getRollupReqFunctionErrorWrapper(
: undefined

return json({
url: request.url,
currentPageURL: request.url,
profile,
connectedEmails,
PASSPORT_URL: context.env.PASSPORT_URL,
Expand All @@ -87,12 +87,12 @@ export const shouldRevalidate = ({
}

export default function Onboarding() {
const { connectedEmails, PASSPORT_URL, profile, url, targetIG } =
const { connectedEmails, PASSPORT_URL, profile, currentPageURL, targetIG } =
useLoaderData<{
connectedEmails: DropdownSelectListItem[]
PASSPORT_URL: string
profile: Profile
url: string
currentPageURL: string
targetIG:
| {
name: string
Expand All @@ -102,7 +102,9 @@ export default function Onboarding() {
}>()

const currentPage =
new URL(url).searchParams.get('rollup_result') || targetIG ? 1 : 0
new URL(currentPageURL).searchParams.get('rollup_result') || targetIG
? 1
: 0

useConnectResult()

Expand All @@ -119,7 +121,13 @@ export default function Onboarding() {
}
>
<Outlet
context={{ connectedEmails, PASSPORT_URL, currentPage, targetIG }}
context={{
connectedEmails,
PASSPORT_URL,
currentPageURL,
currentPage,
targetIG,
}}
/>
</div>
<div className="basis-3/5 h-[100dvh] w-full hidden lg:flex justify-end items-center bg-gray-50 dark:bg-gray-800 overflow-hidden">
Expand Down
87 changes: 58 additions & 29 deletions apps/console/app/routes/onboarding/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
} from '@proofzero/design-system/src/atoms/dropdown/DropdownSelectList'
import { useFetcher, useNavigate, useOutletContext } from '@remix-run/react'
import { getEmailIcon } from '@proofzero/utils/getNormalisedConnectedAccounts'
import { redirectToPassport } from '~/utils'
import {
OnboardTypeValues,
RedirectQueryParamKeys,
redirectToPassport,
} from '~/utils'
import { HiOutlineArrowLeft, HiOutlineMail } from 'react-icons/hi'
import { Input } from '@proofzero/design-system/src/atoms/form/Input'
import { DocumentationBadge } from '~/components/DocumentationBadge'
Expand Down Expand Up @@ -125,8 +129,8 @@ const Option = ({
description: string
selected?: boolean
disabled?: boolean
setSelectedType: (value: 'solo' | 'team') => void
type: 'solo' | 'team'
setSelectedType: (value: OnboardTypeValues) => void
type: OnboardTypeValues
}) => {
return (
<div
Expand Down Expand Up @@ -165,8 +169,8 @@ const SelectOrgType = ({
}: {
setPage: (value: number) => void
page: number
setOrgType: (value: 'solo' | 'team') => void
orgType: 'solo' | 'team'
setOrgType: (value: OnboardTypeValues) => void
orgType: OnboardTypeValues
}) => {
return (
<div
Expand All @@ -188,17 +192,17 @@ const SelectOrgType = ({
Icon={TbUser}
header="I'm solo developer"
description="I'm setting up app for myself"
selected={orgType === 'solo'}
setSelectedType={() => setOrgType('solo')}
type="solo"
selected={orgType === OnboardTypeValues.Solo}
setSelectedType={() => setOrgType(OnboardTypeValues.Solo)}
type={OnboardTypeValues.Solo}
/>
<Option
Icon={TbUsers}
header="I'm part of a team"
description="I'm setting up app for a team"
selected={orgType === 'team'}
type="team"
setSelectedType={() => setOrgType('team')}
selected={orgType === OnboardTypeValues.Team}
type={OnboardTypeValues.Team}
setSelectedType={() => setOrgType(OnboardTypeValues.Team)}
/>
<Button
className="w-full"
Expand Down Expand Up @@ -227,12 +231,14 @@ const ConnectEmail = ({
setPage,
page,
setEmailAccountURN,
onboardType,
}: {
connectedEmails: DropdownSelectListItem[]
PASSPORT_URL: string
setPage: (value: number) => void
page: number
setEmailAccountURN: (value: AccountURN) => void
onboardType: OnboardTypeValues
}) => {
const [name, setName] = useState('')
const [email, setEmail] = useState<DropdownSelectListItem | null>(null)
Expand Down Expand Up @@ -284,6 +290,9 @@ const ConnectEmail = ({
PASSPORT_URL,
login_hint: 'email microsoft google apple',
rollup_action: 'connect',
redirectQueryParams: {
[RedirectQueryParamKeys.OnboardType]: onboardType,
},
})
}
className="w-full"
Expand Down Expand Up @@ -335,6 +344,9 @@ const ConnectEmail = ({
PASSPORT_URL,
login_hint: 'email microsoft google apple',
rollup_action: 'connect',
redirectQueryParams: {
[RedirectQueryParamKeys.OnboardType]: onboardType,
},
})
}
ConnectButtonPhrase="Connect New Email Address"
Expand Down Expand Up @@ -758,22 +770,34 @@ const CongratsPage = ({
}

export default function Landing() {
const { connectedEmails, PASSPORT_URL, currentPage, targetIG } =
useOutletContext<{
connectedEmails: DropdownSelectListItem[]
PASSPORT_URL: string
currentPage: number
targetIG:
| undefined
| {
name: string
URN: IdentityGroupURN
}
}>()
const {
connectedEmails,
PASSPORT_URL,
currentPage,
targetIG,
currentPageURL,
} = useOutletContext<{
connectedEmails: DropdownSelectListItem[]
PASSPORT_URL: string
currentPage: number
targetIG:
| undefined
| {
name: string
URN: IdentityGroupURN
}
currentPageURL: string
}>()

// For coming back from an e-mail connect flow
const onboardTypeQueryParam = new URL(currentPageURL).searchParams.get(
RedirectQueryParamKeys.OnboardType
)

// Currently 'team' is not an option. It is here for future use.
const [orgType, setOrgType] = useState<'solo' | 'team'>(
targetIG ? 'team' : 'solo'
const [orgType, setOrgType] = useState<OnboardTypeValues>(
targetIG || onboardTypeQueryParam === OnboardTypeValues.Team
? OnboardTypeValues.Team
: OnboardTypeValues.Solo
)
const [clientId, setClientId] = useState('')
const [emailAccountURN, setEmailAccountURN] = useState<AccountURN>()
Expand Down Expand Up @@ -801,6 +825,7 @@ export default function Landing() {
setPage={setPage}
page={page}
setEmailAccountURN={setEmailAccountURN}
onboardType={orgType}
/>
{targetIG && (
<EnrollToGroup
Expand All @@ -812,7 +837,7 @@ export default function Landing() {

{!targetIG && (
<>
{orgType === 'team' ? (
{orgType === OnboardTypeValues.Team ? (
<>
{emailAccountURN && (
<CreateGroup
Expand All @@ -835,11 +860,15 @@ export default function Landing() {

<CongratsPage
navigateUrl={
orgType === 'solo' ? `/apps/${clientId}` : `/groups/${groupID}`
orgType === OnboardTypeValues.Solo
? `/apps/${clientId}`
: `/groups/${groupID}`
}
page={page}
navigateText={
orgType === 'solo' ? 'Go to my Application' : 'Go to my Group'
orgType === OnboardTypeValues.Solo
? 'Go to my Application'
: 'Go to my Group'
}
/>
</div>
Expand Down
14 changes: 8 additions & 6 deletions apps/console/app/services/billing/stripe.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { InternalServerError } from '@proofzero/errors'
import { type CoreClientType } from '@proofzero/platform-clients/core'
import { IDENTITY_GROUP_OPTIONS } from '@proofzero/platform/identity/src/constants'
import { type ReconcileAppsSubscriptionsOutput } from '@proofzero/platform/starbase/src/jsonrpc/methods/reconcileAppSubscriptions'
import { ServicePlanType } from '@proofzero/types/billing'
import {
Expand Down Expand Up @@ -354,25 +355,26 @@ export const reconcileSubscriptions = async (

if (seatQuantities) {
const { quantity: stripeSeatQuantity } = seatQuantities
const groupSeats = await coreClient.billing.getIdentityGroupSeats.query(
{
const usedSeats =
await coreClient.billing.getUsedIdentityGroupSeats.query({
URN: URN as IdentityGroupURN,
}
)
})

// If the group has more seats than the subscription, set payment failed
// because this flag is responsible for displaying the "Payment failed"
// in the UI
if (
!paidInvoice ||
(groupSeats && groupSeats.quantity > stripeSeatQuantity!)
usedSeats >
stripeSeatQuantity! + IDENTITY_GROUP_OPTIONS.maxFreeMembers
) {
await coreClient.billing.setPaymentFailed.mutate({
URN: URN as IdentityGroupURN,
})
} else if (
paidInvoice &&
(!groupSeats || groupSeats.quantity <= stripeSeatQuantity!)
usedSeats <=
stripeSeatQuantity! + IDENTITY_GROUP_OPTIONS.maxFreeMembers
) {
await coreClient.billing.setPaymentFailed.mutate({
URN: URN as IdentityGroupURN,
Expand Down
2 changes: 1 addition & 1 deletion apps/console/app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export type errorsAuthProps = {
}

export type errorsTeamProps = {
upserteAppContactAddress?: string
upsertAppContactAddress?: string
}

export type AuthorizedProfile = AuthorizedUser
Expand Down
Loading

0 comments on commit 6c9c706

Please sign in to comment.