diff --git a/next-tavla/src/Admin/scenarios/Boards/components/Column/Actions.tsx b/next-tavla/src/Admin/scenarios/Boards/components/Column/Actions.tsx
index 0b7b8c475..7bf61fedf 100644
--- a/next-tavla/src/Admin/scenarios/Boards/components/Column/Actions.tsx
+++ b/next-tavla/src/Admin/scenarios/Boards/components/Column/Actions.tsx
@@ -7,16 +7,19 @@ import { useLink } from '../../hooks/useLink'
import { useToast } from '@entur/alert'
import classes from './styles.module.css'
import { DeleteBoardButton } from '../Delete'
+import { SortableColumn } from './SortableColumn'
function Actions({ board }: { board: TBoard }) {
const link = useLink(board.id)
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
)
}
diff --git a/next-tavla/src/Admin/scenarios/Boards/components/Column/LastModified.tsx b/next-tavla/src/Admin/scenarios/Boards/components/Column/LastModified.tsx
index 3d737c180..b70374ca8 100644
--- a/next-tavla/src/Admin/scenarios/Boards/components/Column/LastModified.tsx
+++ b/next-tavla/src/Admin/scenarios/Boards/components/Column/LastModified.tsx
@@ -1,7 +1,12 @@
import { formatTimestamp } from 'Admin/utils/time'
+import { SortableColumn } from './SortableColumn'
function LastModified({ timestamp }: { timestamp?: number }) {
- return {formatTimestamp(timestamp)}
+ return (
+
+ {formatTimestamp(timestamp)}
+
+ )
}
export { LastModified }
diff --git a/next-tavla/src/Admin/scenarios/Boards/components/Column/Link.tsx b/next-tavla/src/Admin/scenarios/Boards/components/Column/Link.tsx
index 81f05e4d7..5d82b8e83 100644
--- a/next-tavla/src/Admin/scenarios/Boards/components/Column/Link.tsx
+++ b/next-tavla/src/Admin/scenarios/Boards/components/Column/Link.tsx
@@ -1,8 +1,9 @@
import { useLink } from '../../hooks/useLink'
+import { SortableColumn } from './SortableColumn'
function Link({ bid }: { bid?: string }) {
const link = useLink(bid)
- return {link}
+ return {link}
}
export { Link }
diff --git a/next-tavla/src/Admin/scenarios/Boards/components/Column/Name.tsx b/next-tavla/src/Admin/scenarios/Boards/components/Column/Name.tsx
index 782da7fb2..1319447a3 100644
--- a/next-tavla/src/Admin/scenarios/Boards/components/Column/Name.tsx
+++ b/next-tavla/src/Admin/scenarios/Boards/components/Column/Name.tsx
@@ -1,7 +1,8 @@
import { DEFAULT_BOARD_NAME } from 'Admin/utils/constants'
+import { SortableColumn } from './SortableColumn'
function Name({ name = DEFAULT_BOARD_NAME }: { name?: string }) {
- return {name}
+ return {name}
}
export { Name }
diff --git a/next-tavla/src/Admin/scenarios/Boards/components/Column/SortableColumn.tsx b/next-tavla/src/Admin/scenarios/Boards/components/Column/SortableColumn.tsx
new file mode 100644
index 000000000..b98100524
--- /dev/null
+++ b/next-tavla/src/Admin/scenarios/Boards/components/Column/SortableColumn.tsx
@@ -0,0 +1,26 @@
+import { TBoardsColumn } from 'Admin/types/boards'
+import classes from './styles.module.css'
+import { useSortableColumnAttributes } from '../../hooks/useSortableColumnAttributes'
+
+function SortableColumn({
+ column,
+ children,
+}: {
+ column: TBoardsColumn
+ children: React.ReactNode
+}) {
+ const { setNodeRef, style } = useSortableColumnAttributes(column)
+
+ return (
+
+ {children}
+
+ )
+}
+
+export { SortableColumn }
diff --git a/next-tavla/src/Admin/scenarios/Boards/components/Column/styles.module.css b/next-tavla/src/Admin/scenarios/Boards/components/Column/styles.module.css
index 1f5ac3373..6318160d5 100644
--- a/next-tavla/src/Admin/scenarios/Boards/components/Column/styles.module.css
+++ b/next-tavla/src/Admin/scenarios/Boards/components/Column/styles.module.css
@@ -2,3 +2,10 @@
display: flex;
gap: 0.5em;
}
+
+.column {
+ padding-left: 0.25em;
+ min-height: 2.25rem;
+ display: flex;
+ align-items: center;
+}
diff --git a/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/ColumnHeader.tsx b/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/ColumnHeader.tsx
new file mode 100644
index 000000000..3d8452e2a
--- /dev/null
+++ b/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/ColumnHeader.tsx
@@ -0,0 +1,33 @@
+import { BoardsColumns, TBoardsColumn } from 'Admin/types/boards'
+import classes from './styles.module.css'
+import { Sort } from '../Sort'
+import { Tooltip } from '@entur/tooltip'
+import { useSortableColumnAttributes } from '../../hooks/useSortableColumnAttributes'
+
+function ColumnHeader({ column }: { column: TBoardsColumn }) {
+ const { attributes, listeners, setNodeRef, style } =
+ useSortableColumnAttributes(column)
+
+ return (
+
+
+
+ {BoardsColumns[column]}
+
+
+
+
+ )
+}
+
+export { ColumnHeader }
diff --git a/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/index.tsx b/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/index.tsx
index 770fed810..76c641043 100644
--- a/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/index.tsx
+++ b/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/index.tsx
@@ -1,31 +1,11 @@
-import { useCallback } from 'react'
-import classes from './styles.module.css'
import { TBoardsColumn } from 'Admin/types/boards'
-import { Sort } from '../Sort'
+import { ColumnHeader } from './ColumnHeader'
function TableHeader({ columns }: { columns: TBoardsColumn[] }) {
- const title = useCallback((column: TBoardsColumn) => {
- switch (column) {
- case 'name':
- return 'Navn på tavle'
- case 'url':
- return 'Lenke'
- case 'actions':
- return 'Handlinger'
- case 'lastModified':
- return 'Sist oppdatert'
- default:
- return 'Ukjent kolonne'
- }
- }, [])
-
return (
<>
- {columns.map((column) => (
-
+ {columns.map((column: TBoardsColumn) => (
+
))}
>
)
diff --git a/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/styles.module.css b/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/styles.module.css
index 22fdedaa0..3b1d32330 100644
--- a/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/styles.module.css
+++ b/next-tavla/src/Admin/scenarios/Boards/components/TableHeader/styles.module.css
@@ -3,12 +3,27 @@
align-items: center;
gap: 0.5em;
border-bottom: var(--colors-blues-blue20) solid;
- padding-bottom: 0.5em;
margin-bottom: 1em;
height: 3em;
}
.title {
+ display: flex;
+ height: 3em;
+ align-items: center;
font-weight: 500;
- color: var(--colors-brand-lavender);
+ color: var(--border-color);
+ padding: 0 0.25rem;
+ border-top-right-radius: 0.25em;
+ border-top-left-radius: 0.25em;
+}
+
+.title:hover {
+ cursor: grab;
+ background-color: var(--tertiary-background-color);
+}
+
+.title:active {
+ cursor: grabbing;
+ background-color: var(--tertiary-background-color);
}
diff --git a/next-tavla/src/Admin/scenarios/Boards/components/TableRows/index.tsx b/next-tavla/src/Admin/scenarios/Boards/components/TableRows/index.tsx
index 6fbfbc7c7..4130d932d 100644
--- a/next-tavla/src/Admin/scenarios/Boards/components/TableRows/index.tsx
+++ b/next-tavla/src/Admin/scenarios/Boards/components/TableRows/index.tsx
@@ -1,26 +1,26 @@
-import { TBoardsColumn } from 'Admin/types/boards'
import { useBoardsSettings } from '../../utils/context'
import { useSortBoardFunction } from '../../hooks/useSortBoardFunction'
import { DEFAULT_BOARD_NAME } from 'Admin/utils/constants'
import { Column } from '../Column'
import { Fragment } from 'react'
import { TBoard } from 'types/settings'
+import { TBoardsColumn } from 'Admin/types/boards'
function TableRows() {
- const settings = useBoardsSettings()
+ const { boards, columns, search } = useBoardsSettings()
const sortFunction = useSortBoardFunction()
- const filter = new RegExp(settings.search, 'i')
+ const filter = new RegExp(search, 'i')
return (
<>
- {settings.boards
+ {boards
.filter((board: TBoard) =>
filter.test(board?.meta?.title ?? DEFAULT_BOARD_NAME),
)
.sort(sortFunction)
.map((board: TBoard) => (
- {settings.columns.map((column: TBoardsColumn) => (
+ {columns.map((column: TBoardsColumn) => (
+
+
+
+
+
+
+
+
+
+ Velg kolonner
+
+
+
+
+
+
+
+
+ {Object.entries(BoardsColumns).map(([column]) => (
+
+ dispatch({
+ type: 'toggleColumn',
+ column: column as TBoardsColumn,
+ })
+ }
+ >
+ {BoardsColumns[column as TBoardsColumn]}
+
+ ))}
+
+
+
+
+ )
+}
+
+export { ToggleBoardsColumns }
diff --git a/next-tavla/src/Admin/scenarios/Boards/components/ToggleBoardsColumns/styles.module.css b/next-tavla/src/Admin/scenarios/Boards/components/ToggleBoardsColumns/styles.module.css
new file mode 100644
index 000000000..edaa20709
--- /dev/null
+++ b/next-tavla/src/Admin/scenarios/Boards/components/ToggleBoardsColumns/styles.module.css
@@ -0,0 +1,16 @@
+.boardListOptions {
+ margin-left: auto;
+}
+
+.popoverHeading {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.contentList {
+ padding: 0.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
diff --git a/next-tavla/src/Admin/scenarios/Boards/hooks/useSortableColumnAttributes.ts b/next-tavla/src/Admin/scenarios/Boards/hooks/useSortableColumnAttributes.ts
new file mode 100644
index 000000000..f8f01a4c3
--- /dev/null
+++ b/next-tavla/src/Admin/scenarios/Boards/hooks/useSortableColumnAttributes.ts
@@ -0,0 +1,37 @@
+import { CSSProperties } from 'react'
+import { useSortable } from '@dnd-kit/sortable'
+import { CSS } from '@dnd-kit/utilities'
+
+export function useSortableColumnAttributes(column: string) {
+ const { attributes, listeners, transform, transition, active, setNodeRef } =
+ useSortable({ id: column })
+
+ const otherColumnActive = active && active.id !== column
+ const thisColumnActive = active && active.id === column
+
+ const activeStyle =
+ thisColumnActive &&
+ ({
+ backgroundColor: 'var(--main-background-color)',
+ } as CSSProperties)
+
+ const style = {
+ transform: CSS.Translate.toString(transform),
+ transition: transition,
+ zIndex: thisColumnActive ? 10 : 0,
+ opacity: otherColumnActive ? 0.5 : 1,
+ ...activeStyle,
+ }
+
+ if (thisColumnActive) style.backgroundColor = 'var(--main-background-color)'
+
+ return {
+ style,
+ attributes,
+ listeners,
+ transform,
+ transition,
+ active,
+ setNodeRef,
+ }
+}
diff --git a/next-tavla/src/Admin/scenarios/Boards/index.tsx b/next-tavla/src/Admin/scenarios/Boards/index.tsx
index d610f350a..1d8674f71 100644
--- a/next-tavla/src/Admin/scenarios/Boards/index.tsx
+++ b/next-tavla/src/Admin/scenarios/Boards/index.tsx
@@ -8,6 +8,7 @@ import {
SettingsContext,
SettingsDispatchContext,
useBoardsSettings,
+ useBoardsSettingsDispatch,
} from './utils/context'
import { Search } from './components/Search'
import { isEmpty } from 'lodash'
@@ -15,6 +16,27 @@ import { IllustratedInfo } from 'Admin/components/IllustratedInfo'
import { TableHeader } from './components/TableHeader'
import { TableRows } from './components/TableRows'
import { CreateBoard } from 'Admin/components/CreateBoard'
+import { ToggleBoardsColumns } from './components/ToggleBoardsColumns'
+import { TBoardsColumn } from 'Admin/types/boards'
+import {
+ useSensors,
+ useSensor,
+ PointerSensor,
+ KeyboardSensor,
+ DragEndEvent,
+ DndContext,
+ closestCenter,
+} from '@dnd-kit/core'
+import {
+ sortableKeyboardCoordinates,
+ arrayMove,
+ SortableContext,
+ horizontalListSortingStrategy,
+} from '@dnd-kit/sortable'
+import {
+ restrictToHorizontalAxis,
+ restrictToWindowEdges,
+} from '@dnd-kit/modifiers'
function Boards({ boards }: { boards: TBoard[] }) {
const [settings, dispatch] = useReducer(settingsReducer, {
@@ -32,7 +54,10 @@ function Boards({ boards }: { boards: TBoard[] }) {
Mine Tavler
-
+
+
+
+
@@ -41,9 +66,17 @@ function Boards({ boards }: { boards: TBoard[] }) {
}
function BoardTable() {
- const settings = useBoardsSettings()
+ const { boards, columns } = useBoardsSettings()
+ const dispatch = useBoardsSettingsDispatch()
- if (isEmpty(settings.boards))
+ const sensors = useSensors(
+ useSensor(PointerSensor),
+ useSensor(KeyboardSensor, {
+ coordinateGetter: sortableKeyboardCoordinates,
+ }),
+ )
+
+ if (isEmpty(boards))
return (
)
+ const handleDragEnd = (event: DragEndEvent) => {
+ const { active, over } = event
+
+ if (active.id !== over?.id) {
+ const oldIndex = columns.indexOf(active.id as TBoardsColumn)
+ const newIndex = columns.indexOf(over?.id as TBoardsColumn)
+ const newOrder = arrayMove(columns, oldIndex, newIndex)
+ dispatch({ type: 'setColumns', columns: newOrder })
+ }
+ }
+
return (
)
}
diff --git a/next-tavla/src/Admin/scenarios/Boards/styles.module.css b/next-tavla/src/Admin/scenarios/Boards/styles.module.css
index 7a2f69628..1a1d6c1ee 100644
--- a/next-tavla/src/Admin/scenarios/Boards/styles.module.css
+++ b/next-tavla/src/Admin/scenarios/Boards/styles.module.css
@@ -14,3 +14,10 @@
display: grid;
align-items: center;
}
+
+.actionRow {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1em;
+}
diff --git a/next-tavla/src/Admin/scenarios/Boards/utils/reducer.ts b/next-tavla/src/Admin/scenarios/Boards/utils/reducer.ts
index 79cd14c87..7761a6672 100644
--- a/next-tavla/src/Admin/scenarios/Boards/utils/reducer.ts
+++ b/next-tavla/src/Admin/scenarios/Boards/utils/reducer.ts
@@ -1,9 +1,12 @@
import { TBoards, TBoardsColumn, TSort } from 'Admin/types/boards'
+import { xor } from 'lodash'
export type Action =
| { type: 'setSearch'; search: string }
| { type: 'setSort'; sort: { type: TSort; column: TBoardsColumn } }
| { type: 'deleteBoard'; bid: string }
+ | { type: 'toggleColumn'; column: TBoardsColumn }
+ | { type: 'setColumns'; columns: TBoardsColumn[] }
export function settingsReducer(board: TBoards, action: Action): TBoards {
switch (action.type) {
@@ -16,5 +19,15 @@ export function settingsReducer(board: TBoards, action: Action): TBoards {
...board,
boards: board.boards.filter((b) => b.id !== action.bid),
}
+ case 'toggleColumn':
+ return {
+ ...board,
+ columns: xor(board.columns, [action.column]),
+ }
+ case 'setColumns':
+ return {
+ ...board,
+ columns: action.columns,
+ }
}
}
diff --git a/next-tavla/src/Admin/types/boards.ts b/next-tavla/src/Admin/types/boards.ts
index 321e8e157..045119718 100644
--- a/next-tavla/src/Admin/types/boards.ts
+++ b/next-tavla/src/Admin/types/boards.ts
@@ -2,7 +2,14 @@ import { TBoard } from 'types/settings'
export type TSort = 'none' | 'ascending' | 'descending'
-export type TBoardsColumn = 'name' | 'url' | 'actions' | 'lastModified'
+export const BoardsColumns = {
+ name: 'Tavlenavn',
+ url: 'Lenke',
+ actions: 'Handlinger',
+ lastModified: 'Sist oppdatert',
+} as const
+
+export type TBoardsColumn = keyof typeof BoardsColumns
export const SortableColumns = ['name', 'lastModified'] as const
diff --git a/next-tavla/src/Shared/styles/global.css b/next-tavla/src/Shared/styles/global.css
index 3f5a40d3f..1f7b7703b 100644
--- a/next-tavla/src/Shared/styles/global.css
+++ b/next-tavla/src/Shared/styles/global.css
@@ -41,3 +41,9 @@ h3 {
flex-direction: column;
gap: 1rem;
}
+
+.flexRow {
+ display: flex;
+ flex-direction: row;
+ gap: 1rem;
+}
diff --git a/next-tavla/src/Shared/styles/spacing.css b/next-tavla/src/Shared/styles/spacing.css
index 6922d103d..83e02d705 100644
--- a/next-tavla/src/Shared/styles/spacing.css
+++ b/next-tavla/src/Shared/styles/spacing.css
@@ -1,3 +1,83 @@
+.m-1 {
+ margin: 0.5em;
+}
+
+.m-2 {
+ margin: 1em;
+}
+
+.m-3 {
+ margin: 1.5em;
+}
+
+.m-4 {
+ margin: 2em;
+}
+
+.mt-1 {
+ margin-top: 0.5em;
+}
+
+.mt-2 {
+ margin-top: 1em;
+}
+
+.mt-3 {
+ margin-top: 1.5em;
+}
+
+.mt-4 {
+ margin-top: 2em;
+}
+
+.ml-1 {
+ margin-left: 0.5em;
+}
+
+.ml-2 {
+ margin-left: 1em;
+}
+
+.ml-3 {
+ margin-left: 1.5em;
+}
+
+.ml-4 {
+ margin-left: 2em;
+}
+
+.mr-1 {
+ margin-right: 0.5em;
+}
+
+.mr-2 {
+ margin-right: 1em;
+}
+
+.mr-3 {
+ margin-right: 1.5em;
+}
+
+.mr-4 {
+ margin-right: 2em;
+}
+
+.mb-1 {
+ margin-bottom: 0.5em;
+}
+
+.mb-2 {
+ margin-bottom: 1em;
+}
+
+.mb-3 {
+ margin-bottom: 1.5em;
+}
+
+.mb-4 {
+ margin-bottom: 2em;
+}
+
.p-1 {
padding: 0.5em;
}