Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
c78a482
initial setup
GermanJablo Sep 3, 2025
966fed9
fix importMap
GermanJablo Sep 19, 2025
aef7732
add components in config
GermanJablo Sep 19, 2025
996f2a4
save
GermanJablo Sep 19, 2025
0c2f539
add importMap
GermanJablo Sep 22, 2025
fafe0fc
export type
GermanJablo Sep 22, 2025
3f1de98
install react-grid-layout
GermanJablo Sep 22, 2025
1cc6369
add modular dashboard components
GermanJablo Sep 22, 2025
e479f1f
the problem was `position: relative` missing
GermanJablo Sep 22, 2025
78d9d44
make responsive
GermanJablo Sep 22, 2025
66d9204
render widgets as RSC
GermanJablo Sep 23, 2025
aa7a1c7
add examples, default size
GermanJablo Sep 24, 2025
1313bd5
force specific widths and heights
GermanJablo Sep 24, 2025
ad59602
revert, allow arbitrary widths and heights, just set max and min limits
GermanJablo Sep 24, 2025
456b962
improve styles
GermanJablo Sep 24, 2025
388235b
save
GermanJablo Sep 24, 2025
d9173c8
add dropdown menu in StepNav Breadcrumb
GermanJablo Sep 25, 2025
d86d045
use almost final API
GermanJablo Sep 25, 2025
0d47a80
fix multiple instances same widget and remove preventCollision
GermanJablo Sep 25, 2025
445613f
fix styles when not editing dashboard
GermanJablo Sep 25, 2025
804dad7
remove compactType null
GermanJablo Sep 25, 2025
3f8c53f
add preferences
GermanJablo Sep 25, 2025
1afe461
fix cursor grab state
GermanJablo Sep 25, 2025
91f4e4c
save
GermanJablo Sep 26, 2025
c678b68
refactor dashboard layout management to use WidgetInstanceClient type…
GermanJablo Sep 26, 2025
47d0e1f
improve naming
GermanJablo Sep 26, 2025
20c8eed
remove comment
GermanJablo Sep 29, 2025
cd57892
use webpack because HMR doesn't work with turbopack
GermanJablo Sep 29, 2025
7369307
refactor, move handleLayoutChange to custom hook
GermanJablo Sep 29, 2025
306520a
refactor, rename, simplify
GermanJablo Sep 29, 2025
60df73c
fix createClientLayout
GermanJablo Sep 29, 2025
d3c2209
add temporal "typescript.experimental.useTsgo": true
GermanJablo Sep 29, 2025
83ee6ec
type safe setLayoutPreference
GermanJablo Sep 29, 2025
0ff20e6
fix server component
GermanJablo Sep 29, 2025
327353d
add comment
GermanJablo Sep 29, 2025
3d873f6
add widget functionality
GermanJablo Sep 29, 2025
d92f735
Merge remote-tracking branch 'origin/main' into feat/modular-dashboards
GermanJablo Sep 30, 2025
54cc18a
render widgets on demand
GermanJablo Sep 30, 2025
843844b
delete widget functionality
GermanJablo Sep 30, 2025
1c25eef
fix styles
GermanJablo Sep 30, 2025
20df8e2
improve styles. Follow design intent
GermanJablo Sep 30, 2025
f39078b
move WidgetWrapper to client.tsx
GermanJablo Sep 30, 2025
a034046
move DashboardBreadcrumbDropdown to client.tsx
GermanJablo Sep 30, 2025
88910ba
move server action to folder
GermanJablo Sep 30, 2025
c10261f
simplify client component
GermanJablo Sep 30, 2025
884fa41
revert commit
GermanJablo Sep 30, 2025
ff6d675
add all resize handles
GermanJablo Sep 30, 2025
e852f2a
improve style select
GermanJablo Sep 30, 2025
109666c
fix(next): update border and background styles to use theme variables
GermanJablo Oct 1, 2025
15e398e
fix(next): add error handling and user feedback for layout saving
GermanJablo Oct 1, 2025
cc6d0e7
fix(next): update tooltip and select styles to use theme variables
GermanJablo Oct 1, 2025
90bf22e
defaultLayout accepts a function with req argument
GermanJablo Oct 1, 2025
f8ddc6e
add max and min height and width. Doesn't work for added widgets yet
GermanJablo Oct 3, 2025
8212e6d
fix initial size widget when inserting
GermanJablo Oct 3, 2025
b89ce09
simplify mobile breakpoint
GermanJablo Oct 3, 2025
8550ff5
fix widget background
GermanJablo Oct 3, 2025
e156f45
fix revenue styles
GermanJablo Oct 3, 2025
d07d38e
extract CollectionCards as a separate component
GermanJablo Oct 3, 2025
e72c5ab
extract collection cards to separate component
GermanJablo Oct 3, 2025
df5187c
nest css
GermanJablo Oct 3, 2025
2dd2057
don't style widgets by default - leverage card class
GermanJablo Oct 3, 2025
587ad19
decouple CollectionCards widget from default component
GermanJablo Oct 6, 2025
f005e51
move GridLayout inside Default folder
GermanJablo Oct 6, 2025
7208550
BIG COMMIT: default widget collectionCards
GermanJablo Oct 6, 2025
c474abf
Merge remote-tracking branch 'origin/main' into HEAD
GermanJablo Oct 7, 2025
c8203f2
amend
GermanJablo Oct 7, 2025
eb430dc
amend
GermanJablo Oct 7, 2025
ae28a45
BIG COMMIT: WidgetServerProps
GermanJablo Oct 7, 2025
38e30f7
ammend
GermanJablo Oct 7, 2025
2316f15
chore(test): update dashboard config to include commented-out collect…
GermanJablo Oct 7, 2025
de0d974
add collection-cards to defaultLayout if defaultLayout is undefined
GermanJablo Oct 7, 2025
2527ce2
fix(payload): update Widget type to support additional dimensions for…
GermanJablo Oct 7, 2025
028c6d3
reverte change in dev.ts, using turbopack again
GermanJablo Oct 8, 2025
8c73209
fix css
GermanJablo Oct 8, 2025
6f42e7a
docs WIP
GermanJablo Oct 8, 2025
a29beed
better error handling
GermanJablo Oct 8, 2025
f7278f6
fix(ui): improve default context for StepNav with warning for missing…
GermanJablo Oct 8, 2025
0a2f353
improve test
GermanJablo Oct 8, 2025
9da9b3a
Merge remote-tracking branch 'origin/main' into feat/modular-dashboards2
GermanJablo Oct 21, 2025
430aa3a
remove react-grid-layout
GermanJablo Oct 21, 2025
cdaf057
version using rows
GermanJablo Oct 22, 2025
a8d618f
use flat layout, simple flex
GermanJablo Oct 22, 2025
ab29c62
drag to reorder
GermanJablo Oct 22, 2025
1dcc640
use drag over instead of drag end
GermanJablo Oct 22, 2025
960864e
fixes
GermanJablo Oct 22, 2025
f0c42ca
remove-dnd-kit
GermanJablo Oct 31, 2025
2d46bb1
red dots for debugability
GermanJablo Oct 31, 2025
927fa66
use dnd-kit core (no list)
GermanJablo Nov 3, 2025
a7a5179
drag overlay works correctly
GermanJablo Nov 3, 2025
c0291ed
drag overlay works correctly
GermanJablo Nov 3, 2025
3792fc5
fixed dragOverlay
GermanJablo Nov 4, 2025
2a31c0d
fix cursor
GermanJablo Nov 4, 2025
8d22a91
save WIP
GermanJablo Nov 4, 2025
3482ed7
refactor: move dashboardStepNav to separate file
GermanJablo Nov 4, 2025
fcb05fd
move widget
GermanJablo Nov 5, 2025
fb3b0b9
dropTargetWidget
GermanJablo Nov 5, 2025
98fb2ee
refactor
GermanJablo Nov 5, 2025
129b87a
fix DragOverlay
GermanJablo Nov 5, 2025
8a1d77b
refactor
GermanJablo Nov 5, 2025
ca6cade
remove activeWidth
GermanJablo Nov 5, 2025
4711590
simplify dropTargetWidget
GermanJablo Nov 5, 2025
27a06b1
fix reordering
GermanJablo Nov 5, 2025
200f12f
fix broken close button
GermanJablo Nov 5, 2025
71636de
resizing
GermanJablo Nov 5, 2025
cba3281
resizing part 2
GermanJablo Nov 5, 2025
3c1e85c
remove widgetWidth as numbers from 1 to 12
GermanJablo Nov 5, 2025
0d481bf
fix bugs
GermanJablo Nov 5, 2025
02cd4d7
refactor move to collisionDetection
GermanJablo Nov 6, 2025
d32b76e
refactor move sensers
GermanJablo Nov 6, 2025
8fb326a
fix widget
GermanJablo Nov 6, 2025
a8b1948
save WIP
GermanJablo Nov 6, 2025
8bbb295
replace gap with padding
GermanJablo Nov 7, 2025
77a6a72
save
GermanJablo Nov 7, 2025
75b998c
fix periodic percentages
GermanJablo Nov 7, 2025
74bc088
fix dragoverlay
GermanJablo Nov 7, 2025
c663d4b
droppableTarget
GermanJablo Nov 7, 2025
edb9342
fix collision detection! NEW approach works! Much simpler
GermanJablo Nov 7, 2025
5110c87
fix collisionDetector to support keyboard
GermanJablo Nov 7, 2025
59120d3
remove keyboard sensor and use the default. Is not worth it the compl…
GermanJablo Nov 7, 2025
d6b1f59
Revert "remove keyboard sensor and use the default. Is not worth it t…
GermanJablo Nov 7, 2025
9740fee
customize only pointer senser, leave keyboard as default
GermanJablo Nov 7, 2025
736ff9c
fix onDragEnd
GermanJablo Nov 7, 2025
f358556
fix dropIndicator
GermanJablo Nov 7, 2025
0ecaf2a
fixed percentage
GermanJablo Nov 7, 2025
86d33e1
fix styles stepNav
GermanJablo Nov 7, 2025
88610a9
round droppable indicator
GermanJablo Nov 7, 2025
5563dec
responsive: mobile breakpoint
GermanJablo Nov 7, 2025
8c2ced7
change gap size
GermanJablo Nov 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ jobs:
- auth
- auth-basic
- bulk-edit
- dashboard
- joins
- field-error-states
- fields-relationship
Expand Down Expand Up @@ -404,6 +405,7 @@ jobs:
- auth
- auth-basic
- bulk-edit
- dashboard
- joins
- field-error-states
- fields-relationship
Expand Down
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"playwright.env": {
"NODE_OPTIONS": "--no-deprecation --no-experimental-strip-types"
},
"typescript.experimental.useTsgo": true,
"jest.virtualFolders": [
{
"name": "root",
Expand Down
5 changes: 5 additions & 0 deletions docs/custom-components/custom-views.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ const config = buildConfig({
})
```

<Banner type="success">
**Note:** The dashboard is a special case, where in addition to replacing the
default view, [you can add widgets modularly](../custom-components/dashboard).
</Banner>

For more granular control, pass a configuration object instead. Payload exposes the following properties for each view:

| Property | Description |
Expand Down
9 changes: 9 additions & 0 deletions docs/custom-components/dashboard.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
WIP:

## Dashboard Widgets

- `card` class.

## Dashboard View

Just a link to [Customizing Views](./custom-views) or describe here?
14 changes: 13 additions & 1 deletion docs/custom-components/root-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ The following options are available:
_For details on how to build Custom Components, see [Building Custom Components](./overview#building-custom-components)._

<Banner type="success">
**Note:** You can also use set [Collection
**Note:** You can also set [Collection
Components](../configuration/collections#custom-components) and [Global
Components](../configuration/globals#custom-components) in their respective
configs.
Expand Down Expand Up @@ -127,6 +127,12 @@ export default function MyBeforeDashboardComponent() {
}
```

<Banner type="success">
**Note:** You can also set [Dashboard Widgets](../custom-components/dashboard)
in the `admin.dashboard` property, or replace the entire [Dashboard
View](../custom-components/dashboard) with your own.
</Banner>

### afterDashboard

Similar to `beforeDashboard`, the `afterDashboard` property allows you to inject Custom Components into the built-in Dashboard, _after_ the default dashboard contents.
Expand Down Expand Up @@ -156,6 +162,12 @@ export default function MyAfterDashboardComponent() {
}
```

<Banner type="success">
**Note:** You can also set [Dashboard Widgets](../custom-components/dashboard)
in the `admin.dashboard` property, or replace the entire [Dashboard
View](../custom-components/dashboard) with your own.
</Banner>

### beforeLogin

The `beforeLogin` property allows you to inject Custom Components into the built-in Login view, _before_ the default login form.
Expand Down
2 changes: 2 additions & 0 deletions packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@
},
"dependencies": {
"@dnd-kit/core": "6.0.8",
"@dnd-kit/modifiers": "9.0.0",
"@dnd-kit/sortable": "7.0.2",
"@payloadcms/graphql": "workspace:*",
"@payloadcms/translations": "workspace:*",
"@payloadcms/ui": "workspace:*",
Expand Down
18 changes: 0 additions & 18 deletions packages/next/src/utilities/getVisibleEntities.ts

This file was deleted.

2 changes: 2 additions & 0 deletions packages/next/src/utilities/handleServerFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { buildTableStateHandler } from '@payloadcms/ui/utilities/buildTableState
import { getFolderResultsComponentAndDataHandler } from '@payloadcms/ui/utilities/getFolderResultsComponentAndData'
import { schedulePublishHandler } from '@payloadcms/ui/utilities/schedulePublishHandler'

import { renderWidgetHandler } from '../views/Dashboard/Default/GridLayout/renderWidget/renderWidgetServerFn.js'
import { renderDocumentHandler } from '../views/Document/handleServerFunction.js'
import { renderDocumentSlotsHandler } from '../views/Document/renderDocumentSlots.js'
import { renderListHandler } from '../views/List/handleServerFunction.js'
Expand All @@ -19,6 +20,7 @@ const baseServerFunctions: Record<string, ServerFunction<any, any>> = {
'render-document-slots': renderDocumentSlotsHandler,
'render-field': _internal_renderFieldHandler,
'render-list': renderListHandler,
'render-widget': renderWidgetHandler,
'schedule-publish': schedulePublishHandler,
'table-state': buildTableStateHandler,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import type { Widget } from 'payload'

import { useStepNav } from '@payloadcms/ui'
import { Button } from '@payloadcms/ui/elements/Button'
import { DrawerToggler } from '@payloadcms/ui/elements/Drawer'
import { ItemsDrawer } from '@payloadcms/ui/elements/ItemsDrawer'
import { type Option, ReactSelect } from '@payloadcms/ui/elements/ReactSelect'
import { useEffect, useId } from 'react'

export function DashboardStepNav({
addWidget,
cancel,
isEditing,
resetLayout,
saveLayout,
setIsEditing,
widgets,
}: {
addWidget: (slug: string) => void
cancel: () => void
isEditing: boolean
resetLayout: () => Promise<void>
saveLayout: () => Promise<void>
setIsEditing: (isEditing: boolean) => void
widgets: Widget[]
}) {
const { setStepNav } = useStepNav()
const uuid = useId()
const drawerSlug = `widgets-drawer-${uuid}`

// Set step nav directly with minimal dependencies to avoid infinite loops
useEffect(() => {
setStepNav([
{
label: (
<DashboardBreadcrumbDropdown
isEditing={isEditing}
onCancel={cancel}
onEditClick={() => setIsEditing(true)}
onResetLayout={resetLayout}
onSaveChanges={saveLayout}
widgetsDrawerSlug={drawerSlug}
/>
),
},
])
// TODO: useEffectEvent
// Only depend on isEditing and drawerSlug - the functions are stable enough
// eslint-disable-next-line react-compiler/react-compiler
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEditing, drawerSlug])

return (
<>
{isEditing && (
<ItemsDrawer
drawerSlug={drawerSlug}
items={widgets}
onItemClick={(widget) => addWidget(widget.slug)}
searchPlaceholder="Search widgets..."
title="Add Widget"
/>
)}
</>
)
}

export function DashboardBreadcrumbDropdown(props: {
isEditing: boolean
onCancel: () => void
onEditClick: () => void
onResetLayout: () => void
onSaveChanges: () => void
widgetsDrawerSlug: string
}) {
const { isEditing, onCancel, onEditClick, onResetLayout, onSaveChanges, widgetsDrawerSlug } =
props
if (isEditing) {
return (
<div className="dashboard-breadcrumb-dropdown__editing">
<span>Editing Dashboard</span>
<div className="dashboard-breadcrumb-dropdown__actions">
<DrawerToggler className="drawer-toggler--unstyled" slug={widgetsDrawerSlug}>
<Button buttonStyle="pill" el="span" size="small">
Add +
</Button>
</DrawerToggler>
<Button buttonStyle="pill" onClick={onSaveChanges} size="small">
Save Changes
</Button>
<Button buttonStyle="pill" onClick={onCancel} size="small">
Cancel
</Button>
</div>
</div>
)
}

const options = [
{ label: 'Edit Dashboard', value: 'edit' },
{ label: 'Reset Layout', value: 'reset' },
]

const handleChange = (selectedOption: Option | Option[]) => {
// Since isMulti is false, we expect a single Option
const option = Array.isArray(selectedOption) ? selectedOption[0] : selectedOption

if (option?.value === 'edit') {
onEditClick()
} else if (option?.value === 'reset') {
onResetLayout()
}
}

return (
<ReactSelect
className="dashboard-breadcrumb-select"
isClearable={false}
isSearchable={false}
menuIsOpen={undefined} // Let ReactSelect handle open/close
onChange={handleChange}
options={options}
placeholder="Dashboard"
value={{ label: 'Dashboard', value: 'dashboard' }}
/>
)
}
Loading