Skip to content

Commit

Permalink
feat(edit): migrate edit from pages to app router (#1383)
Browse files Browse the repository at this point in the history
* feat(edit): basic preview

* feat(edit): track changes to edit form

* feat(edit): tile selector

* feat(edit): fix debounced tile selector

* feat(edit): form based tile selector

* feat(edit): simple TileCard with add/delete functionality

* feat(edit): simple action for saving tile

* feat(edit): fetch data for tile

* feat(edit): wip TileCard

* feat(edit): cleanup TileCard

* feat(edit): wip form action

* chore(edit): linting and typing

* feat(edit): fix wrongful graphql query and quay data fetching

* feat(edit): working tile update

* feat(edit): update tiles in place

* feat(edit): close tile on save

* feat(edit): added floating action button

* feat(meta): added meta to edit

* feat(edit): remove unused/deprecated components

* revert(#8f79ee0): remove unused/deprecated components

This reverts commit 8f79ee0.

* feat(general): general deletion/cleanup of deprecated components

* chore(lint): linting and formatting
  • Loading branch information
lindtvedtsebastian authored Feb 6, 2024
1 parent d4ef2d8 commit aacd957
Show file tree
Hide file tree
Showing 74 changed files with 1,138 additions and 1,309 deletions.
Empty file modified .husky/pre-commit
100755 → 100644
Empty file.
22 changes: 22 additions & 0 deletions next-tavla/app/(admin)/edit/[id]/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use server'
import { firestore } from 'firebase-admin'
import { initializeAdminApp } from 'Admin/utils/firebase'
import { TBoard, TBoardID } from 'types/settings'
import { TTile } from 'types/tile'

initializeAdminApp()

export async function getBoard(bid: TBoardID) {
const board = await firestore().collection('boards').doc(bid).get()
return { id: board.id, ...board.data() } as TBoard
}

export async function addTile(bid: TBoardID, tile: TTile) {
await firestore()
.collection('boards')
.doc(bid)
.update({
tiles: firestore.FieldValue.arrayUnion(tile),
'meta.dateModified': Date.now(),
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use server'
import { initializeAdminApp } from 'Admin/utils/firebase'
import { firestore } from 'firebase-admin'
import { revalidatePath } from 'next/cache'
import { TMeta } from 'types/meta'
import { TBoardID } from 'types/settings'

initializeAdminApp()

export async function saveMeta(bid: TBoardID, meta: TMeta) {
await firestore().collection('boards').doc(bid).update({ meta: meta })
revalidatePath(`/edit/${bid}`)
}
60 changes: 60 additions & 0 deletions next-tavla/app/(admin)/edit/[id]/components/MetaSettings/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use client'
import classes from './styles.module.css'
import { Button } from '@entur/button'
import { ChoiceChip, ChoiceChipGroup } from '@entur/chip'
import { TextField } from '@entur/form'
import { Heading4 } from '@entur/typography'
import { DEFAULT_BOARD_NAME } from 'Admin/utils/constants'
import { useState } from 'react'
import { TFontSize, TMeta } from 'types/meta'
import { saveMeta } from './actions'
import { TBoardID } from 'types/settings'

function MetaSettings({ bid, meta }: { bid: TBoardID; meta: TMeta }) {
const [font, setFont] = useState('medium')
return (
<form
action={(data: FormData) => {
const name = data.get('name') as string
const font = data.get('font') as TFontSize
saveMeta(bid, {
...meta,
title: name,
fontSize: font,
dateModified: Date.now(),
})
}}
className={classes.meta}
>
<div className="flexRow justifyBetween alignCenter p-2">
<div className="w-100">
<Heading4>Navn på tavlen</Heading4>
<TextField
name="name"
className="w-30"
defaultValue={meta.title ?? DEFAULT_BOARD_NAME}
label="Navn på tavlen"
/>
</div>
<div className="flexColumn g-1">
<Heading4>Velg tekststørrelse: </Heading4>
<ChoiceChipGroup
className="flexRow"
name="font"
value={font}
onChange={(e) => setFont(e.target.value)}
>
<ChoiceChip value="small">Liten</ChoiceChip>
<ChoiceChip value="medium">Medium</ChoiceChip>
<ChoiceChip value="large">Stor</ChoiceChip>
</ChoiceChipGroup>
</div>
</div>
<Button className="m-2" variant="secondary" type="submit">
Lagre navn og tekststørrelse
</Button>
</form>
)
}

export { MetaSettings }
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.meta {
border-radius: 0.5em;
background: var(--secondary-background-color);
padding: 1em;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Checkbox } from '@entur/form'
import { TTransportMode } from 'types/graphql-schema'
import { TTile } from 'types/tile'
import classes from './styles.module.css'
import { TLineFragment } from './types'

function LineCheckbox({
tile,
line,
transportMode,
}: {
tile: TTile
line: TLineFragment
transportMode: TTransportMode | null
}) {
return (
<Checkbox
name={`${tile.uuid}-${transportMode}`}
defaultChecked={
!tile.whitelistedLines ||
tile.whitelistedLines.length === 0 ||
tile.whitelistedLines.includes(line.id)
}
key={line.id}
value={line.id}
className="pl-3"
>
<div className="flexRow alignCenter g-1">
<PublicCode publicCode={line.publicCode} />
{line.name}
</div>
</Checkbox>
)
}
function PublicCode({ publicCode }: { publicCode: string | null }) {
return <div className={classes.publicCode}>{publicCode}</div>
}
export { LineCheckbox }
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { TTransportMode } from 'types/graphql-schema'
import { TTile } from 'types/tile'
import { TransportIcon } from 'components/TransportIcon'
import { transportModeNames } from './utils'
import { Checkbox } from '@entur/form'

function TransportModeCheckbox({
tile,
transportMode,
}: {
tile: TTile
transportMode: TTransportMode | null
}) {
return (
<div>
<div className="flexRow g-2 alignCenter justifyStart">
<TransportIcon
transportMode={transportMode}
className="w-4 h-4"
/>
{transportModeNames(transportMode)}
</div>
<div className="flexRow alignCenter">
<Checkbox
defaultChecked={
!tile.whitelistedLines ||
tile.whitelistedLines.length === 0
}
onChange={(e) => {
document
.getElementsByName(`${tile.uuid}-${transportMode}`)
.forEach((input) => {
if (input instanceof HTMLInputElement)
input.checked = e.currentTarget.checked
})
}}
>
Velg alle
</Checkbox>
</div>
</div>
)
}

export { TransportModeCheckbox }
35 changes: 35 additions & 0 deletions next-tavla/app/(admin)/edit/[id]/components/TileCard/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use server'
import { firestore } from 'firebase-admin'
import { initializeAdminApp } from 'Admin/utils/firebase'
import { TBoard, TBoardID } from 'types/settings'
import { TTile } from 'types/tile'
import { revalidatePath } from 'next/cache'

initializeAdminApp()

export async function deleteTile(bid: TBoardID, tile: TTile) {
await firestore()
.collection('boards')
.doc(bid)
.update({
tiles: firestore.FieldValue.arrayRemove(tile),
'meta.dateModified': Date.now(),
})
revalidatePath(`/edit/${bid}`)
}

export async function saveTile(bid: TBoardID, tile: TTile) {
const docRef = firestore().collection('boards').doc(bid)
const doc = (await docRef.get()).data() as TBoard
const oldTile = doc.tiles.find((t) => t.uuid === tile.uuid)
if (!oldTile)
return docRef.update({
tiles: firestore.FieldValue.arrayUnion(tile),
'meta.dateModified': Date.now(),
})
const index = doc.tiles.indexOf(oldTile)
doc.tiles[index] = tile
docRef.update({ tiles: doc.tiles, 'meta.dateModified': Date.now() })

revalidatePath(`/edit/${bid}`)
}
144 changes: 144 additions & 0 deletions next-tavla/app/(admin)/edit/[id]/components/TileCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
'use client'
import { BaseExpand } from '@entur/expand'
import classes from './styles.module.css'
import { TTile } from 'types/tile'
import { Button, SecondarySquareButton } from '@entur/button'
import { DeleteIcon, EditIcon, CloseIcon } from '@entur/icons'
import { useState } from 'react'
import { TBoardID } from 'types/settings'
import { Heading3, Heading4, SubParagraph } from '@entur/typography'
import { isArray, uniqBy } from 'lodash'
import { TransportIcon } from 'components/TransportIcon'
import { Columns } from 'types/column'
import { FilterChip } from '@entur/chip'
import { TColumn } from 'types/column'
import { useLines } from './useLines'
import { sortLineByPublicCode } from './utils'
import { deleteTile, saveTile } from './actions'
import { TransportModeCheckbox } from './TransportModeCheckbox'
import { LineCheckbox } from './LineCheckbox'
import { HiddenInput } from 'components/Form/HiddenInput'

function TileCard({ bid, tile }: { bid: TBoardID; tile: TTile }) {
const [isOpen, setIsOpen] = useState(false)
const lines = useLines(tile)

if (!lines) return <div className={classes.card}>Laster..</div>

const transportModes = uniqBy(lines, 'transportMode')
.map((l) => l.transportMode)
.sort()

const linesByModeSorted = transportModes
.map((transportMode) => ({
transportMode,
lines: lines
.filter((line) => line.transportMode === transportMode)
.sort(sortLineByPublicCode),
}))
.sort((a, b) => b.lines.length - a.lines.length)

return (
<div>
<div className={classes.card}>
<div className="flexRow g-2 alignCenter">
<div className="flexRow g-2 h-4">
{transportModes.map((tm) => (
<TransportIcon transportMode={tm} key={tm} />
))}
</div>
{tile.name}
</div>
<div className="flexRow g-2">
<SecondarySquareButton
onClick={async () => {
await deleteTile(bid, tile)
}}
>
<DeleteIcon />
</SecondarySquareButton>
<SecondarySquareButton onClick={() => setIsOpen(!isOpen)}>
{isOpen ? <CloseIcon /> : <EditIcon />}
</SecondarySquareButton>
</div>
</div>
<BaseExpand open={isOpen}>
<form
action={(data: FormData) => {
const columns = data.getAll('columns') as TColumn[]
data.delete('columns')
const count = data.get('count') as number | null
data.delete('count')

let lines: string[] = []
for (const line of data.values()) {
lines.push(line as string)
}
// If the length of lines equals all the lines, we don't want to include any
lines = lines.length == count ? [] : lines

saveTile(bid, {
...tile,
columns: columns,
whitelistedLines: lines,
})
}}
onSubmit={() => setIsOpen(false)}
>
<Heading3>Rediger stoppested: {tile.name}</Heading3>
<Heading4>Tabellen</Heading4>
<SubParagraph>
Her bestemmer du hvilke kolonner som skal vises i
tavlen.
</SubParagraph>
<div className="flexRow g-2">
{Object.entries(Columns).map(([key, value]) => {
return (
<FilterChip
name="columns"
key={key}
value={key}
defaultChecked={
isArray(tile.columns)
? tile.columns?.includes(
key as TColumn,
)
: false
}
>
{value}
</FilterChip>
)
})}
</div>
<Heading4>Velg transportmidler og linjer</Heading4>
<div className="flexRow g-2">
{linesByModeSorted.map(({ transportMode, lines }) => (
<div key={transportMode}>
<TransportModeCheckbox
tile={tile}
transportMode={transportMode}
/>
{lines.map((line) => (
<LineCheckbox
key={line.id}
tile={tile}
line={line}
transportMode={transportMode}
/>
))}
</div>
))}
</div>
<HiddenInput id="count" value={lines.length.toString()} />
<div className="flexRow justifyEnd mt-2 mr-2 mb-4">
<Button variant="primary" type="submit">
Lagre endringer
</Button>
</div>
</form>
</BaseExpand>
</div>
)
}
export { TileCard }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.card {
display: flex;
justify-content: space-between;
align-items: center;
background: var(--secondary-background-color);
border-radius: 0.5em;
padding: 1em;
}

.publicCode {
background-color: var(--tertiary-background-color);
padding: 0.35em 0.5em;
border-radius: 0.5em;
}
Loading

0 comments on commit aacd957

Please sign in to comment.