Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(edit): move board between organizations and personal boards #1537

Merged
merged 13 commits into from
Jun 21, 2024
2 changes: 1 addition & 1 deletion next-tavla/app/(admin)/components/CreateBoard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ function NameAndOrganizationSelector({
</Checkbox>
<HiddenInput
id="organization"
value={selectedOrganization?.value}
value={selectedOrganization?.value.id}
/>
<div className="flex flex-row justify-end mt-8 ">
<PrimaryButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
import { getFormFeedbackForError } from 'app/(admin)/utils'
import {
hasBoardEditorAccess,
hasBoardOwnerAccess,
initializeAdminApp,
userCanEditOrganization,
} from 'app/(admin)/utils/firebase'
import { firestore } from 'firebase-admin'
import admin, { firestore } from 'firebase-admin'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
import { TFontSize, TLocation } from 'types/meta'
import { TBoard, TBoardID } from 'types/settings'
import { TBoard, TBoardID, TOrganizationID } from 'types/settings'
import { getBoard, getWalkingDistanceTile } from '../../actions'
import { getUserFromSessionCookie } from 'app/(admin)/utils/server'

initializeAdminApp()

Expand Down Expand Up @@ -64,3 +67,44 @@ async function getTilesWithDistance(board: TBoard, location?: TLocation) {
}),
)
}

export async function moveBoard(
bid: TBoardID,
oid?: TOrganizationID,
fromOrganization?: TOrganizationID,
) {
const user = await getUserFromSessionCookie()
if (!user) return redirect('/')

const access = await hasBoardOwnerAccess(bid)
if (!access) return redirect('/')

if (fromOrganization && !(await userCanEditOrganization(fromOrganization)))
return redirect('/')

if (oid && !(await userCanEditOrganization(oid))) return redirect('/')

if (fromOrganization)
firestore()
.collection('organizations')
.doc(fromOrganization)
.update({ boards: admin.firestore.FieldValue.arrayRemove(bid) })
else
firestore()
.collection('users')
.doc(user.uid)
.update({ owner: admin.firestore.FieldValue.arrayRemove(bid) })

if (oid)
firestore()
.collection('organizations')
.doc(oid)
.update({ boards: admin.firestore.FieldValue.arrayUnion(bid) })
else
firestore()
.collection('users')
.doc(user.uid)
.update({ owner: admin.firestore.FieldValue.arrayUnion(bid) })

revalidatePath(`/edit/${bid}`)
}
73 changes: 69 additions & 4 deletions next-tavla/app/(admin)/edit/[id]/components/MetaSettings/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
'use client'
import { TextField } from '@entur/form'
import { Checkbox, TextField } from '@entur/form'
import { Heading3 } from '@entur/typography'
import { TFontSize, TMeta } from 'types/meta'
import { saveFont, saveTitle } from './actions'
import { TBoardID } from 'types/settings'
import { moveBoard, saveFont, saveTitle } from './actions'
import { TBoardID, TOrganization } from 'types/settings'
import { FontChoiceChip } from './FontChoiceChip'
import { SubmitButton } from 'components/Form/SubmitButton'
import { Address } from './Adress'
import { DEFAULT_BOARD_NAME } from 'app/(admin)/utils/constants'
import { useToast } from '@entur/alert'
import { isEmptyOrSpaces } from 'app/(admin)/edit/utils'
import { Dropdown } from '@entur/dropdown'
import { useOrganizations } from 'app/(admin)/hooks/useOrganizations'
import { useState } from 'react'
import {
TFormFeedback,
getFormFeedbackForError,
getFormFeedbackForField,
} from 'app/(admin)/utils'

function MetaSettings({ bid, meta }: { bid: TBoardID; meta?: TMeta }) {
function MetaSettings({
bid,
meta,

organization,
}: {
bid: TBoardID
meta: TMeta
organization?: TOrganization
}) {
const { addToast } = useToast()
const { organizations, selectedOrganization, setSelectedOrganization } =
useOrganizations(organization)
const [personal, setPersonal] = useState(organization ? false : true)
const [state, setFormError] = useState<TFormFeedback | undefined>()
return (
<>
<form
Expand Down Expand Up @@ -63,6 +84,50 @@ function MetaSettings({ bid, meta }: { bid: TBoardID; meta?: TMeta }) {
</SubmitButton>
</div>
</form>
<form
action={async () => {
if (!selectedOrganization && !personal) {
return setFormError(
getFormFeedbackForError(
'create/organization-missing',
),
)
}
await moveBoard(
bid,
personal ? undefined : selectedOrganization?.value.id,
organization?.id,
)
setFormError(undefined)
addToast('Organisasjon lagret!')
}}
className="box flex flex-col"
>
<Heading3 margin="bottom">Organisasjon</Heading3>
<Dropdown
items={organizations}
label="Dine organisasjoner"
selectedItem={selectedOrganization}
onChange={setSelectedOrganization}
clearable
className="mb-4"
aria-required="true"
disabled={personal}
{...getFormFeedbackForField('organization', state)}
/>
<Checkbox
checked={personal}
onChange={() => setPersonal(!personal)}
name="personal"
>
Privat tavle
</Checkbox>
<div className="flex flex-row mt-8 justify-end">
<SubmitButton variant="secondary" className="max-sm:w-full">
Lagre organisasjon
</SubmitButton>
oyvindgrutle marked this conversation as resolved.
Show resolved Hide resolved
</div>
</form>
</>
)
}
Expand Down
8 changes: 6 additions & 2 deletions next-tavla/app/(admin)/edit/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ export default async function EditPage({ params }: TProps) {
<RefreshButton board={board} />
</div>
</div>
<div className="grid grid-cols-[repeat(auto-fit,minmax(300px,1fr))] gap-8">
<MetaSettings bid={params.id} meta={board.meta} />
<div className="grid grid-cols-[repeat(auto-fill,minmax(400px,1fr))] gap-8">
<MetaSettings
bid={params.id}
meta={board.meta}
organization={organization}
/>
<Footer
bid={params.id}
footer={board.footer}
Expand Down
10 changes: 10 additions & 0 deletions next-tavla/app/(admin)/edit/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getTransportIcon } from 'components/TransportIcon'
import { uniq } from 'lodash'
import { TTransportMode } from 'types/graphql-schema'
import { TLocation } from 'types/meta'
import { TOrganization } from 'types/settings'

export type TCategory =
| 'onstreetBus'
Expand Down Expand Up @@ -31,6 +32,15 @@ export function locationToDropdownItem(
}
}

export function organizationToDropdownItem(
organization: TOrganization,
): NormalizedDropdownItemType<TOrganization> {
return {
label: organization.name ?? '',
value: organization ?? undefined,
}
}

export function categoryToTransportmode(category: TCategory): TTransportMode {
switch (category) {
case 'onstreetBus':
Expand Down
15 changes: 11 additions & 4 deletions next-tavla/app/(admin)/hooks/useOrganizations.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { NormalizedDropdownItemType } from '@entur/dropdown'
import { useCallback, useEffect, useState } from 'react'
import { getOrganizationsForUser } from '../actions'
import { TOrganization } from 'types/settings'
import { organizationToDropdownItem } from '../edit/utils'

function useOrganizations() {
function useOrganizations(organization?: TOrganization) {
const [organizationList, setOrganizationList] = useState<
NormalizedDropdownItemType<string>[]
NormalizedDropdownItemType<TOrganization>[]
>([])
const [selectedOrganization, setSelectedOrganization] =
useState<NormalizedDropdownItemType | null>(null)
useState<NormalizedDropdownItemType<TOrganization> | null>(
organization ? organizationToDropdownItem(organization) : null,
)

useEffect(() => {
getOrganizationsForUser().then((res) => {
setOrganizationList(
res?.map((o) => ({ value: o.id ?? '', label: o.name ?? '' })),
res?.map((o) => ({
value: o ?? undefined,
label: o.name ?? '',
})),
)
})
}, [])
Expand Down