Skip to content

Commit

Permalink
fix(console): Group invite delete button not working (#2694)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cosmin-Parvulescu committed Sep 19, 2023
1 parent b3a04b8 commit b275dce
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 10 deletions.
96 changes: 93 additions & 3 deletions apps/console/app/routes/__layout/spuorg/$groupID/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,9 @@ const RemoveMemberModal = ({
</section>

<div className="flex justify-end items-center space-x-3">
<Button btnType="secondary-alt">Cancel</Button>
<Button btnType="secondary-alt" onClick={handleClose}>
Cancel
</Button>
<Button type="submit" btnType="dangerous">
Remove Member
</Button>
Expand All @@ -367,6 +369,72 @@ const RemoveMemberModal = ({
)
}

const RemoveInvitationModal = ({
groupID,
invitationCode,
userAlias,
isOpen,
handleClose,
}: {
groupID: string
invitationCode: string
userAlias: string
isOpen: boolean
handleClose: () => void
purge?: boolean
}) => {
return (
<Modal isOpen={isOpen} handleClose={handleClose}>
<div
className={`w-fit rounded-lg bg-white p-4
text-left transition-all sm:p-5 overflow-y-auto flex items-start space-x-4`}
>
<img src={dangerVector} alt="danger" />

<Form
method="post"
action={`/spuorg/${groupID}/uninvite`}
onSubmit={() => {
handleClose()
}}
>
<input name="invitationCode" type="hidden" value={invitationCode} />

<div className="flex flex-row w-full items-center justify-between mb-2">
<Text size="lg" weight="semibold">
Cancel Invitation
</Text>
<div
className={`bg-white p-2 rounded-lg text-xl cursor-pointer
hover:bg-[#F3F4F6]`}
onClick={() => {
handleClose()
}}
>
<HiOutlineX />
</div>
</div>

<section className="mb-4">
<Text size="sm" weight="normal" className="text-gray-500 my-3">
Are you sure you want to cancel the invitation for {userAlias}?
</Text>
</section>

<div className="flex justify-end items-center space-x-3">
<Button btnType="secondary-alt" onClick={handleClose}>
Cancel
</Button>
<Button type="submit" btnType="dangerous">
Cancel invitation
</Button>
</div>
</Form>
</div>
</Modal>
)
}

export default () => {
const { apps, group, groupID, PASSPORT_URL, identityURN, invitations } =
useOutletContext<GroupDetailsContextData>()
Expand All @@ -376,8 +444,11 @@ export default () => {
const hydrated = useHydrated()

const [selectedMemberURN, setSelectedMemberURN] = useState<IdentityURN>()
const [selectedInvitationCode, setSelectedInvitationCode] = useState<string>()
const [selectedMemberAlias, setSelectedMemberAlias] = useState<string>('')
const [removeMemberModalOpen, setRemoveMemberModalOpen] = useState(false)
const [removeInvitationModalOpen, setRemoveInvitationModalOpen] =
useState(false)

const groupApps = apps.filter((a) => a.groupID === groupID)

Expand All @@ -403,6 +474,16 @@ export default () => {
/>
)}

{group && selectedInvitationCode && (
<RemoveInvitationModal
isOpen={removeInvitationModalOpen}
handleClose={() => setRemoveInvitationModalOpen(false)}
groupID={groupID}
invitationCode={selectedInvitationCode}
userAlias={selectedMemberAlias}
/>
)}

{group && (
<section className="-mt-4">
<Breadcrumbs
Expand Down Expand Up @@ -815,8 +896,17 @@ export default () => {
</div>

<div className="flex flex-row items-center gap-4 p-2">
<button className="p-2" disabled>
<HiOutlineTrash className="w-4 h-4 text-gray-500" />
<button className="p-2">
<HiOutlineTrash
className="w-4 h-4 text-gray-500"
onClick={() => {
setSelectedInvitationCode(
_.last(item.val.invitationURL.split('/'))
)
setSelectedMemberAlias(item.val.identifier)
setRemoveInvitationModalOpen(true)
}}
/>
</button>

<Button
Expand Down
70 changes: 70 additions & 0 deletions apps/console/app/routes/__layout/spuorg/$groupID/uninvite.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { generateTraceContextHeaders } from '@proofzero/platform-middleware/trace'
import { getRollupReqFunctionErrorWrapper } from '@proofzero/utils/errors'
import { redirect, type ActionFunction } from '@remix-run/cloudflare'
import { commitFlashSession, requireJWT } from '~/utilities/session.server'
import createCoreClient from '@proofzero/platform-clients/core'
import { getAuthzHeaderConditionallyFromToken } from '@proofzero/utils'
import { BadRequestError } from '@proofzero/errors'
import {
type IdentityGroupURN,
IdentityGroupURNSpace,
} from '@proofzero/urns/identity-group'
import { appendToastToFlashSession } from '~/utils/toast.server'
import { ToastType } from '@proofzero/design-system/src/atoms/toast'

export const action: ActionFunction = getRollupReqFunctionErrorWrapper(
async ({ request, params, context }) => {
const jwt = await requireJWT(request, context.env)
const traceHeader = generateTraceContextHeaders(context.traceSpan)

const groupID = params.groupID as string
const identityGroupURN = IdentityGroupURNSpace.urn(
groupID as string
) as IdentityGroupURN

const fd = await request.formData()
const invitationCode = fd.get('invitationCode') as string | undefined
if (!invitationCode) {
throw new BadRequestError({
message: 'invitationCode is required',
})
}

const coreClient = createCoreClient(context.env.Core, {
...getAuthzHeaderConditionallyFromToken(jwt),
...traceHeader,
})

let toastSession
try {
await coreClient.identity.deleteIdentityGroupInvitation.mutate({
identityGroupURN,
invitationCode,
})

toastSession = await appendToastToFlashSession(
request,
{
message: `Succesfully removed invitation`,
type: ToastType.Success,
},
context.env
)
} catch (e) {
toastSession = await appendToastToFlashSession(
request,
{
message: `There was an error removing the invitation`,
type: ToastType.Error,
},
context.env
)
}

return redirect(`/spuorg/${groupID}`, {
headers: {
'Set-Cookie': await commitFlashSession(toastSession, context.env),
},
})
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ export const acceptIdentityGroupMemberInvitation = async ({
}): Promise<void> => {
const { identityGroupURN, invitationCode } = input

const node = await initIdentityGroupNodeByName(
identityGroupURN,
ctx.IdentityGroup
)
const node = initIdentityGroupNodeByName(identityGroupURN, ctx.IdentityGroup)
if (!node) {
throw new InternalServerError({
message: 'Identity group DO not found',
Expand Down Expand Up @@ -62,7 +59,7 @@ export const acceptIdentityGroupMemberInvitation = async ({
})
}

await node.class.claimInvitation({
await node.class.clearInvitation({
inviteCode: invitationCode,
})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { z } from 'zod'

import { Context } from '../../../context'
import { IdentityGroupURNValidator } from '@proofzero/platform-middleware/inputValidators'
import { InternalServerError } from '@proofzero/errors'
import { initIdentityGroupNodeByName } from '../../../nodes'

export const DeleteIdentityGroupInvitationInputSchema = z.object({
identityGroupURN: IdentityGroupURNValidator,
invitationCode: z.string(),
})
type DeleteIdentityGroupInvitationInput = z.infer<
typeof DeleteIdentityGroupInvitationInputSchema
>

export const deleteIdentityGroupInvitation = async ({
input,
ctx,
}: {
input: DeleteIdentityGroupInvitationInput
ctx: Context
}): Promise<void> => {
const { identityGroupURN, invitationCode } = input

const node = initIdentityGroupNodeByName(identityGroupURN, ctx.IdentityGroup)
if (!node) {
throw new InternalServerError({
message: 'Identity group DO not found',
})
}

await node.class.clearInvitation({ inviteCode: invitationCode })
}
12 changes: 12 additions & 0 deletions platform/identity/src/jsonrpc/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ import {
HasIdentityGroupPermissionsOutputSchema,
hasIdentityGroupPermissions,
} from './methods/identity-groups/hasIdentityGroupPermissions'
import {
DeleteIdentityGroupInvitationInputSchema,
deleteIdentityGroupInvitation,
} from './methods/identity-groups/deleteIdentityGroupInvitation'

const t = initTRPC.context<Context>().create({ errorFormatter })

Expand Down Expand Up @@ -259,4 +263,12 @@ export const appRouter = t.router({
.input(HasIdentityGroupPermissionsInputSchema)
.output(HasIdentityGroupPermissionsOutputSchema)
.query(hasIdentityGroupPermissions),
deleteIdentityGroupInvitation: t.procedure
.use(LogUsage)
.use(Analytics)
.use(AuthorizationTokenFromHeader)
.use(ValidateJWT)
.use(RequireIdentity)
.input(DeleteIdentityGroupInvitationInputSchema)
.mutation(deleteIdentityGroupInvitation),
})
4 changes: 2 additions & 2 deletions platform/identity/src/nodes/identity-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type MemberInvitation = InviteMemberInput & {
timestamp: number
}

export type ClaimInvitationInput = {
export type ClearInvitationInput = {
inviteCode: string
}

Expand Down Expand Up @@ -124,7 +124,7 @@ export default class IdentityGroup extends DOProxy {
)
}

async claimInvitation({ inviteCode }: ClaimInvitationInput): Promise<void> {
async clearInvitation({ inviteCode }: ClearInvitationInput): Promise<void> {
const invitations =
(await this.state.storage.get<MemberInvitation[]>('invitations')) || []

Expand Down

0 comments on commit b275dce

Please sign in to comment.