Skip to content

Commit

Permalink
feat(user): delete user (#1587)
Browse files Browse the repository at this point in the history
* feat(user): delete user

* fix(user): aria label

* chore(user): refactor

* chore(user): specify consequences of deleting user
  • Loading branch information
oyvindgrutle authored Jul 19, 2024
1 parent 3695a75 commit c8cdb1f
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 2 deletions.
74 changes: 74 additions & 0 deletions next-tavla/app/(admin)/components/Footer/components/DeleteUser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { SecondarySquareButton } from '@entur/button'
import { CloseIcon } from '@entur/icons'
import { Modal } from '@entur/modal'
import { Heading2, Label, Paragraph } from '@entur/typography'
import { useSearchParamsModal } from 'app/(admin)/hooks/useSearchParamsModal'
import Image from 'next/image'
import ducks from 'assets/illustrations/Ducks.png'
import { SubmitButton } from 'components/Form/SubmitButton'
import { TextField } from '@entur/form'
import { useFormState } from 'react-dom'

import { getFormFeedbackForField } from 'app/(admin)/utils'
import { deleteProfile } from './actions'

function DeleteUser() {
const [open, close] = useSearchParamsModal('deleteProfile')

const [state, action] = useFormState(deleteProfile, undefined)
return (
<>
<Modal
open={open}
size="small"
onDismiss={close}
closeLabel="Avbryt sletting"
className="flex flex-col justify-start items-center text-center"
>
<SecondarySquareButton
aria-label="Avbryt sletting"
className="ml-auto"
onClick={close}
>
<CloseIcon />
</SecondarySquareButton>
<Image src={ducks} alt="" className="h-1/2 w-1/2" />
<Heading2>Slett bruker</Heading2>
<Paragraph>
Alle dine private tavler, samt tavler tilknyttet
organisasjoner hvor du er det eneste medlemmet, vil også bli
slettet.
</Paragraph>
<form
action={action}
className="flex flex-col w-full gap-4"
aria-live="polite"
aria-relevant="all"
>
<Label className="font-medium text-left">
Bekreft ved å skrive inn din e-postadresse
</Label>
<TextField
name="email"
label="E-post"
type="text"
required
aria-required
className="w-full"
{...getFormFeedbackForField('email', state)}
/>

<SubmitButton
variant="primary"
width="fluid"
aria-label="Slett bruker"
>
Ja, slett
</SubmitButton>
</form>
</Modal>
</>
)
}

export { DeleteUser }
58 changes: 58 additions & 0 deletions next-tavla/app/(admin)/components/Footer/components/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use server'

import { TFormFeedback, getFormFeedbackForError } from 'app/(admin)/utils'
import {
deleteOrganization,
deleteUserBoards,
initializeAdminApp,
} from 'app/(admin)/utils/firebase'
import { getUserFromSessionCookie } from 'app/(admin)/utils/server'
import { getAuth } from 'firebase-admin/auth'
import { redirect } from 'next/navigation'
import { logout } from '../../Login/actions'
import { getOrganizationsForUser } from 'app/(admin)/actions'
import { TUserID } from 'types/settings'

initializeAdminApp()

export async function deleteProfile(
prevState: TFormFeedback | undefined,
data: FormData,
) {
const user = await getUserFromSessionCookie()
if (!user) return redirect('/')

const adminAuth = getAuth()

const email = data.get('email') as string

try {
const userRecord = await adminAuth.getUser(user.uid)

if (email !== userRecord.email)
return getFormFeedbackForError('auth/email-mismatch')
await deleteUserBoardsAndOrganizations(userRecord.uid)
await adminAuth.deleteUser(userRecord.uid)
await logout()
} catch (error) {
return getFormFeedbackForError()
}
}

async function deleteUserBoardsAndOrganizations(uid: TUserID) {
try {
const organizations = await getOrganizationsForUser()

await deleteUserBoards()
return Promise.all(
organizations.map((org) => {
if (org.owners?.includes(uid) && org.owners.length < 2) {
if (!org.id) return
deleteOrganization(org.id)
}
}),
)
} catch (error) {
return getFormFeedbackForError()
}
}
11 changes: 10 additions & 1 deletion next-tavla/app/(admin)/components/Footer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { Heading3, Link as EnturLink, Paragraph } from '@entur/typography'
import Link from 'next/link'
import { ExternalIcon, GithubIcon } from '@entur/icons'
import { usePostHog } from 'posthog-js/react'
import { DeleteUser } from './components/DeleteUser'

function Footer() {
function Footer({ loggedIn }: { loggedIn: boolean }) {
const posthog = usePostHog()
return (
<footer className="eds-contrast">
Expand Down Expand Up @@ -81,6 +82,14 @@ function Footer() {
<ExternalIcon aria-hidden />
<GithubIcon size={25} aria-hidden />
</div>
{loggedIn && (
<div className="flex flex-row gap-1 items-center">
<EnturLink as={Link} href="?deleteProfile">
Slett bruker
</EnturLink>
<DeleteUser />
</div>
)}
</div>
</div>
</div>
Expand Down
14 changes: 14 additions & 0 deletions next-tavla/app/(admin)/utils/firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getOrganizationIfUserHasAccess,
} from '../actions'
import { getFormFeedbackForError } from '.'
import { getPrivateBoardsForUser } from '../actions'

initializeAdminApp()

Expand Down Expand Up @@ -149,3 +150,16 @@ export async function deleteOrganizationBoard(
if (!access) throw 'auth/operation-not-allowed'
return firestore().collection('boards').doc(bid).delete()
}

export async function deleteUserBoards() {
const user = await getUser()
if (!user) return

const boards = await getPrivateBoardsForUser()

return Promise.all(
boards
.filter((board) => board !== undefined)
.map((board) => board?.id && deleteBoard(board.id)),
)
}
6 changes: 6 additions & 0 deletions next-tavla/app/(admin)/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ export function getFormFeedbackForError(
feedback: 'Skriv inn en e-postadresse.',
variant: 'warning',
}
case 'auth/email-mismatch':
return {
form_type: 'email',
feedback: 'E-postadressen stemmer ikke.',
variant: 'error',
}
case 'organization/not-found':
return {
form_type: 'general',
Expand Down
2 changes: 1 addition & 1 deletion next-tavla/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async function RootLayout({ children }: { children: ReactNode }) {
<PostHogPageView />
{children}
<FloatingContact />
<Footer />
<Footer loggedIn={loggedIn} />
</body>
</EnturToastProvider>
</PHProvider>
Expand Down

0 comments on commit c8cdb1f

Please sign in to comment.