Skip to content

Commit 10787c8

Browse files
authored
feat(dashboard): refactor location list UI to use data table (medusajs#13571)
* wip: convert location list to a table * chore: changeset * fix: rm search bluring on loading change * feat: translations and palceholders, cleanup, make content more compact * fix: delete message * chore: optimise use memo * fix: update toast * feat: make stock location address searchable * fix: search input blur on load finish
1 parent 6e80694 commit 10787c8

File tree

15 files changed

+373
-134
lines changed

15 files changed

+373
-134
lines changed

.changeset/weak-candles-melt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@medusajs/dashboard": patch
3+
---
4+
5+
feat(dashboard): refactor location list UI to use data table

packages/admin/dashboard/src/components/data-table/data-table.tsx

Lines changed: 64 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,26 @@ type DataTableActionProps = {
3131
label: string
3232
disabled?: boolean
3333
} & (
34-
| {
34+
| {
3535
to: string
3636
}
37-
| {
37+
| {
3838
onClick: () => void
3939
}
40-
)
40+
)
4141

4242
type DataTableActionMenuActionProps = {
4343
label: string
4444
icon: ReactNode
4545
disabled?: boolean
4646
} & (
47-
| {
47+
| {
4848
to: string
4949
}
50-
| {
50+
| {
5151
onClick: () => void
5252
}
53-
)
53+
)
5454

5555
type DataTableActionMenuGroupProps = {
5656
actions: DataTableActionMenuActionProps[]
@@ -138,36 +138,45 @@ export const DataTable = <TData,>({
138138
const isViewConfigEnabled = useFeatureFlag("view_configurations")
139139

140140
// If view config is disabled, don't use column visibility features
141-
const effectiveEnableColumnVisibility = isViewConfigEnabled && enableColumnVisibility
141+
const effectiveEnableColumnVisibility =
142+
isViewConfigEnabled && enableColumnVisibility
142143
const effectiveEnableViewSelector = isViewConfigEnabled && enableViewSelector
143144

144145
const enableFiltering = filters && filters.length > 0
145-
const showFilterMenu = enableFilterMenu !== undefined ? enableFilterMenu : enableFiltering
146+
const showFilterMenu =
147+
enableFilterMenu !== undefined ? enableFilterMenu : enableFiltering
146148
const enableCommands = commands && commands.length > 0
147149
const enableSorting = columns.some((column) => column.enableSorting)
148150

149-
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>(initialColumnVisibility)
151+
const [columnVisibility, setColumnVisibility] =
152+
React.useState<VisibilityState>(initialColumnVisibility)
150153

151154
// Update column visibility when initial visibility changes
152155
React.useEffect(() => {
153156
// Deep compare to check if the visibility has actually changed
154157
const currentKeys = Object.keys(columnVisibility).sort()
155158
const newKeys = Object.keys(initialColumnVisibility).sort()
156159

157-
const hasChanged = currentKeys.length !== newKeys.length ||
160+
const hasChanged =
161+
currentKeys.length !== newKeys.length ||
158162
currentKeys.some((key, index) => key !== newKeys[index]) ||
159-
Object.entries(initialColumnVisibility).some(([key, value]) => columnVisibility[key] !== value)
163+
Object.entries(initialColumnVisibility).some(
164+
([key, value]) => columnVisibility[key] !== value
165+
)
160166

161167
if (hasChanged) {
162168
setColumnVisibility(initialColumnVisibility)
163169
}
164170
}, [initialColumnVisibility])
165171

166172
// Wrapper function to handle column visibility changes
167-
const handleColumnVisibilityChange = React.useCallback((visibility: VisibilityState) => {
168-
setColumnVisibility(visibility)
169-
onColumnVisibilityChange?.(visibility)
170-
}, [onColumnVisibilityChange])
173+
const handleColumnVisibilityChange = React.useCallback(
174+
(visibility: VisibilityState) => {
175+
setColumnVisibility(visibility)
176+
onColumnVisibilityChange?.(visibility)
177+
},
178+
[onColumnVisibilityChange]
179+
)
171180

172181
// Extract filter IDs for query param management
173182
const filterIds = useMemo(() => filters?.map((f) => f.id) ?? [], [filters])
@@ -231,7 +240,7 @@ export const DataTable = <TData,>({
231240
Array.from(prev.keys()).forEach((key) => {
232241
if (prefixedFilterIds.includes(key)) {
233242
// Extract the unprefixed key
234-
const unprefixedKey = prefix ? key.replace(`${prefix}_`, '') : key
243+
const unprefixedKey = prefix ? key.replace(`${prefix}_`, "") : key
235244
if (!(unprefixedKey in value)) {
236245
prev.delete(key)
237246
}
@@ -257,11 +266,14 @@ export const DataTable = <TData,>({
257266
}, [order])
258267

259268
// Memoize current configuration to prevent infinite loops
260-
const currentConfiguration = useMemo(() => ({
261-
filters: filtering,
262-
sorting: sorting,
263-
search: search,
264-
}), [filtering, sorting, search])
269+
const currentConfiguration = useMemo(
270+
() => ({
271+
filters: filtering,
272+
sorting: sorting,
273+
search: search,
274+
}),
275+
[filtering, sorting, search]
276+
)
265277

266278
const handleSortingChange = (value: DataTableSortingState) => {
267279
setSearchParams((prev) => {
@@ -315,50 +327,53 @@ export const DataTable = <TData,>({
315327
onRowClick: rowHref ? onRowClick : undefined,
316328
pagination: enablePagination
317329
? {
318-
state: pagination,
319-
onPaginationChange: handlePaginationChange,
320-
}
330+
state: pagination,
331+
onPaginationChange: handlePaginationChange,
332+
}
321333
: undefined,
322334
filtering: enableFiltering
323335
? {
324-
state: filtering,
325-
onFilteringChange: handleFilteringChange,
326-
}
336+
state: filtering,
337+
onFilteringChange: handleFilteringChange,
338+
}
327339
: undefined,
328340
sorting: enableSorting
329341
? {
330-
state: sorting,
331-
onSortingChange: handleSortingChange,
332-
}
342+
state: sorting,
343+
onSortingChange: handleSortingChange,
344+
}
333345
: undefined,
334346
search: enableSearch
335347
? {
336-
state: search,
337-
onSearchChange: handleSearchChange,
338-
}
348+
state: search,
349+
onSearchChange: handleSearchChange,
350+
}
339351
: undefined,
340352
rowSelection,
341353
isLoading,
342354
columnVisibility: effectiveEnableColumnVisibility
343355
? {
344-
state: columnVisibility,
345-
onColumnVisibilityChange: handleColumnVisibilityChange,
346-
}
347-
: undefined,
348-
columnOrder: effectiveEnableColumnVisibility && columnOrder && onColumnOrderChange
349-
? {
350-
state: columnOrder,
351-
onColumnOrderChange: onColumnOrderChange,
352-
}
356+
state: columnVisibility,
357+
onColumnVisibilityChange: handleColumnVisibilityChange,
358+
}
353359
: undefined,
360+
columnOrder:
361+
effectiveEnableColumnVisibility && columnOrder && onColumnOrderChange
362+
? {
363+
state: columnOrder,
364+
onColumnOrderChange: onColumnOrderChange,
365+
}
366+
: undefined,
354367
})
355368

356369
const shouldRenderHeading = heading || subHeading
357370

358371
return (
359372
<UiDataTable
360373
instance={instance}
361-
className={layout === "fill" ? "h-full [&_tr]:last-of-type:!border-b" : undefined}
374+
className={
375+
layout === "fill" ? "h-full [&_tr]:last-of-type:!border-b" : undefined
376+
}
362377
>
363378
<UiDataTable.Toolbar
364379
className="flex flex-col items-start justify-between gap-2 md:flex-row md:items-center"
@@ -397,7 +412,9 @@ export const DataTable = <TData,>({
397412
</div>
398413
)}
399414
{actionMenu && <ActionMenu variant="primary" {...actionMenu} />}
400-
{actions && actions.length > 0 && <DataTableActions actions={actions} />}
415+
{actions && actions.length > 0 && (
416+
<DataTableActions actions={actions} />
417+
)}
401418
{!actions && action && <DataTableAction {...action} />}
402419
</div>
403420
</div>
@@ -407,7 +424,9 @@ export const DataTable = <TData,>({
407424
<UiDataTable.Pagination translations={paginationTranslations} />
408425
)}
409426
{enableCommands && (
410-
<UiDataTable.CommandBar selectedLabel={(count) => `${count} selected`} />
427+
<UiDataTable.CommandBar
428+
selectedLabel={(count) => `${count} selected`}
429+
/>
411430
)}
412431
</UiDataTable>
413432
)
@@ -520,4 +539,3 @@ const DataTableActions = ({ actions }: { actions: DataTableActionProps[] }) => {
520539
</div>
521540
)
522541
}
523-

packages/admin/dashboard/src/i18n/translations/$schema.json

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5834,9 +5834,23 @@
58345834
"properties": {
58355835
"description": {
58365836
"type": "string"
5837+
},
5838+
"noRecordsMessage": {
5839+
"type": "string"
5840+
},
5841+
"noRecordsMessageEmpty": {
5842+
"type": "string"
5843+
},
5844+
"noRecordsMessageFiltered": {
5845+
"type": "string"
58375846
}
58385847
},
5839-
"required": ["description"],
5848+
"required": [
5849+
"description",
5850+
"noRecordsMessage",
5851+
"noRecordsMessageEmpty",
5852+
"noRecordsMessageFiltered"
5853+
],
58405854
"additionalProperties": false
58415855
},
58425856
"create": {
@@ -5876,9 +5890,12 @@
58765890
"properties": {
58775891
"confirmation": {
58785892
"type": "string"
5893+
},
5894+
"successToast": {
5895+
"type": "string"
58795896
}
58805897
},
5881-
"required": ["confirmation"],
5898+
"required": ["confirmation", "successToast"],
58825899
"additionalProperties": false
58835900
},
58845901
"fulfillmentProviders": {

packages/admin/dashboard/src/i18n/translations/en.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,7 +1535,10 @@
15351535
"stockLocations": {
15361536
"domain": "Locations & Shipping",
15371537
"list": {
1538-
"description": "Manage your store's stock locations and shipping options."
1538+
"description": "Manage your store's stock locations and shipping options.",
1539+
"noRecordsMessage": "No records",
1540+
"noRecordsMessageEmpty": "No locations found",
1541+
"noRecordsMessageFiltered": "No locations found matching the filters"
15391542
},
15401543
"create": {
15411544
"header": "Create Stock Location",
@@ -1548,7 +1551,8 @@
15481551
"successToast": "Location {{name}} was successfully updated."
15491552
},
15501553
"delete": {
1551-
"confirmation": "You are about to delete the stock location \"{{name}}\". This action cannot be undone."
1554+
"confirmation": "You are about to delete the stock location \"{{name}}\". This action cannot be undone.",
1555+
"successToast": "Location \"{{name}}\" was successfully deleted."
15521556
},
15531557
"fulfillmentProviders": {
15541558
"header": "Fulfillment Providers",

packages/admin/dashboard/src/routes/locations/location-edit/components/edit-location-form/edit-location-form.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const EditLocationForm = ({ location }: EditLocationFormProps) => {
6262
},
6363
{
6464
onSuccess: () => {
65-
toast.success(t("stockLocations.edit.successToast"))
65+
toast.success(t("stockLocations.edit.successToast", { name: name }))
6666
handleSuccess()
6767
},
6868
onError: (e) => {

packages/admin/dashboard/src/routes/locations/location-list/components/location-list-header/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/admin/dashboard/src/routes/locations/location-list/components/location-list-header/location-list-header.tsx

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
export { shippingListLoader as loader } from "./loader"
21
export { LocationList as Component } from "./location-list"

packages/admin/dashboard/src/routes/locations/location-list/loader.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)