Skip to content

Commit

Permalink
feat(console): delete app modal checks custom domain config
Browse files Browse the repository at this point in the history
  • Loading branch information
szkl committed Jun 7, 2023
1 parent 2c81adc commit b4d56ed
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 36 deletions.
77 changes: 53 additions & 24 deletions apps/console/app/components/DeleteAppModal/DeleteAppModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, { useState } from 'react'
import { useEffect, useState } from 'react'
import { Link, useFetcher } from '@remix-run/react'
import type { FetcherWithComponents } from '@remix-run/react'

import { Button } from '@proofzero/design-system/src/atoms/buttons/Button'
import { Modal } from '@proofzero/design-system/src/molecules/modal/Modal'
Expand All @@ -21,15 +23,23 @@ export const DeleteAppModal = ({
isOpen,
deleteAppCallback,
}: DeleteAppModalProps) => {
const [isSubmitting, setIsSubmitting] = useState(false)
const fetcher = useFetcher()
const [hasCustomDomain, setHasCustomDomain] = useState(
Boolean(appDetails.customDomain)
)

useEffect(() => {
if (fetcher.data?.message === 'HAS_CUSTOM_DOMAIN') setHasCustomDomain(true)
}, [fetcher])

return (
<Modal
isOpen={isOpen}
closable={!isSubmitting}
closable={fetcher.state !== 'submitting'}
handleClose={() => deleteAppCallback(false)}
>
<div
className={`w-[62vw] rounded-lg bg-white px-4 pb-4
className={`w-[48vw] rounded-lg bg-white px-4 pb-4
text-left transition-all sm:px-6 sm:pb-6 overflow-y-auto flex items-start space-x-4`}
>
<img src={dangerVector} />
Expand All @@ -39,38 +49,36 @@ export const DeleteAppModal = ({
Delete Application
</Text>

<DeleteModalAppForm
appDetails={appDetails}
isSubmitting={isSubmitting}
setIsSubmitting={setIsSubmitting}
callback={deleteAppCallback}
/>
{hasCustomDomain && (
<HasCustomDomain appDetails={appDetails}></HasCustomDomain>
)}
{!hasCustomDomain && (
<DeleteModalAppForm
fetcher={fetcher}
appDetails={appDetails}
callback={deleteAppCallback}
/>
)}
</div>
</div>
</Modal>
)
}

type DeleteModalAppFormProps = {
fetcher: FetcherWithComponents<any>
appDetails: appDetailsProps
isSubmitting: boolean
setIsSubmitting: (state: boolean) => unknown
callback: (state: boolean) => unknown
}

const DeleteModalAppForm = ({
fetcher,
appDetails,
isSubmitting,
setIsSubmitting,
callback,
}: DeleteModalAppFormProps) => {
const [isAppNameMatches, setAppNameMatches] = useState(false)
return (
<form
method="post"
action="/apps/delete"
onSubmit={() => setIsSubmitting(true)}
>
<fetcher.Form method="post" action="/apps/delete">
<section className="mb-4">
<Text size="sm" weight="normal" className="text-gray-500 my-3">
Are you sure you want to delete <b>{appDetails.app.name}</b> app? This
Expand All @@ -95,23 +103,44 @@ const DeleteModalAppForm = ({
<div className="flex justify-end items-center space-x-3">
<Button
btnType="secondary-alt"
disabled={isSubmitting}
disabled={fetcher.state === 'submitting'}
onClick={() => callback(false)}
>
Cancel
</Button>
<Button
disabled={!isAppNameMatches || isSubmitting}
disabled={!isAppNameMatches || fetcher.state === 'submitting'}
type="submit"
btnType="dangerous"
className={
isSubmitting ? 'flex items-center justify-between transition' : ''
fetcher.state === 'submitting'
? 'flex items-center justify-between transition'
: ''
}
>
{isSubmitting && <RiLoader5Fill className="animate-spin" size={22} />}
{fetcher.state === 'submitting' && (
<RiLoader5Fill className="animate-spin" size={22} />
)}
Delete
</Button>
</div>
</form>
</fetcher.Form>
)
}

type HasCustomDomainProps = {
appDetails: appDetailsProps
}

const HasCustomDomain = ({ appDetails }: HasCustomDomainProps) => (
<section className="flex flex-col mb-4">
<Text size="sm" weight="normal" className="text-gray-500 my-3">
This application has a custom domain configured. You need to delete it
before you can delete the application.
</Text>

<Link to={`/apps/${appDetails.clientId}/domain-wip`} className="self-end">
<Button btnType="secondary-alt">Go to Custom Domain</Button>
</Link>
</section>
)
38 changes: 26 additions & 12 deletions apps/console/app/routes/apps/delete.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { ActionFunction, json, redirect } from '@remix-run/cloudflare'
import { redirect } from '@remix-run/cloudflare'
import type { ActionFunction } from '@remix-run/cloudflare'
import createStarbaseClient from '@proofzero/platform-clients/starbase'
import { requireJWT } from '~/utilities/session.server'
import { getAuthzHeaderConditionallyFromToken } from '@proofzero/utils'
import { generateTraceContextHeaders } from '@proofzero/platform-middleware/trace'
import { getRollupReqFunctionErrorWrapper } from '@proofzero/utils/errors'
import { InternalServerError } from '@proofzero/errors'
import {
JsonError,
getErrorCause,
getRollupReqFunctionErrorWrapper,
} from '@proofzero/utils/errors'
import { BadRequestError, InternalServerError } from '@proofzero/errors'

export const action: ActionFunction = getRollupReqFunctionErrorWrapper(
async ({ request, context }) => {
const formData = await request.formData()
const clientId = formData.get('clientId')?.toString()

if (!clientId) throw 'Client ID is required'
if (!clientId)
throw new BadRequestError({ message: 'Client ID is required' })

const jwt = await requireJWT(request)

Expand All @@ -20,15 +26,23 @@ export const action: ActionFunction = getRollupReqFunctionErrorWrapper(
...generateTraceContextHeaders(context.traceSpan),
})
try {
await starbaseClient.deleteApp.mutate({
clientId,
})
return redirect(`/`)
await starbaseClient.deleteApp.mutate({ clientId })
return redirect('/')
} catch (error) {
console.error({ error })
return new InternalServerError({
message: 'Could not delete application',
})
const cause = getErrorCause(error)
const traceparent = context.traceSpan.getTraceParent()
if (cause instanceof BadRequestError) {
throw JsonError(cause, traceparent)
} else {
console.error(error)
throw JsonError(
new InternalServerError({
message: 'Could not delete the application',
cause: error,
}),
traceparent
)
}
}
}
)
2 changes: 2 additions & 0 deletions apps/console/app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {
AuthorizedUser,
AppObject,
EdgesMetadata,
CustomDomain,
} from '@proofzero/platform/starbase/src/types'

export enum RollType {
Expand All @@ -20,6 +21,7 @@ export type appDetailsProps = {
clientId?: string
secretTimestamp?: number
apiKeyTimestamp?: number
customDomain?: CustomDomain
}

export type errorsAuthProps = {
Expand Down
6 changes: 6 additions & 0 deletions platform/starbase/src/jsonrpc/methods/deleteApp.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { z } from 'zod'
import { Context } from '../context'
import { getApplicationNodeByClientId } from '../../nodes/application'
import { BadRequestError } from '@proofzero/errors'
import { ApplicationURNSpace } from '@proofzero/urns/application'
import createEdgesClient from '@proofzero/platform-clients/edges'
import { AppClientIdParamSchema } from '../validators/app'
Expand Down Expand Up @@ -28,6 +29,11 @@ export const deleteApp = async ({
ctx.StarbaseApp
)

if (await appDO.storage.get('customDomain'))
throw new BadRequestError({
message: 'HAS_CUSTOM_DOMAIN',
})

await ctx.edges.removeEdge.mutate({
src: ctx.accountURN,
dst: appURN,
Expand Down
3 changes: 3 additions & 0 deletions platform/starbase/src/jsonrpc/validators/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from 'zod'
import { CustomDomainSchema } from './customdomain'

export const AppObjectSchema = z.object({
name: z.string(),
Expand All @@ -8,6 +9,7 @@ export const AppObjectSchema = z.object({
termsURL: z.string().optional(),
privacyURL: z.string().optional(),
websiteURL: z.string().optional(),
customDomain: CustomDomainSchema.optional(),
})

export type AppObject = z.infer<typeof AppObjectSchema>
Expand All @@ -25,6 +27,7 @@ export const AppReadableFieldsSchema = z.object({
createdTimestamp: z.number().optional(),
termsURL: z.string().optional(),
privacyURL: z.string().optional(),
customDomain: CustomDomainSchema.optional(),
})

export const AppInternalFieldSchema = z.object({
Expand Down
1 change: 1 addition & 0 deletions platform/starbase/src/nodes/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export default class StarbaseApp extends DOProxy {
'createdTimestamp',
'termsURL',
'privacyURL',
'customDomain',
]
const appObj = await this.state.storage.get(keysWeWant)
const result = Object.fromEntries(appObj) as AppDetails
Expand Down

0 comments on commit b4d56ed

Please sign in to comment.