diff --git a/@xen-orchestra/backups-cli/package.json b/@xen-orchestra/backups-cli/package.json
index 7610e16a83a..f968afbf124 100644
--- a/@xen-orchestra/backups-cli/package.json
+++ b/@xen-orchestra/backups-cli/package.json
@@ -7,7 +7,7 @@
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"dependencies": {
"@xen-orchestra/async-map": "^0.1.2",
- "@xen-orchestra/backups": "^0.53.0",
+ "@xen-orchestra/backups": "^0.53.1",
"@xen-orchestra/fs": "^4.1.7",
"filenamify": "^6.0.0",
"getopts": "^2.2.5",
diff --git a/@xen-orchestra/backups/_runners/_vmRunners/_AbstractXapi.mjs b/@xen-orchestra/backups/_runners/_vmRunners/_AbstractXapi.mjs
index 8151cd251ce..a7dcc6340a0 100644
--- a/@xen-orchestra/backups/_runners/_vmRunners/_AbstractXapi.mjs
+++ b/@xen-orchestra/backups/_runners/_vmRunners/_AbstractXapi.mjs
@@ -290,7 +290,7 @@ export const AbstractXapi = class AbstractXapiVmBackupRunner extends Abstract {
for (const vdiRef of vdiRefs) {
try {
// data_destroy will fail with a VDI_NO_CBT_METADATA error if CBT is not enabled on this VDI
- await this._xapi.call('VDI.data_destroy', vdiRef)
+ await this._xapi.VDI_dataDestroy(vdiRef)
Task.info(`Snapshot data has been deleted`, { vdiRef })
} catch (error) {
Task.warning(`Couldn't deleted snapshot data`, { error, vdiRef })
diff --git a/@xen-orchestra/backups/package.json b/@xen-orchestra/backups/package.json
index 12794202d63..3dad129a9b6 100644
--- a/@xen-orchestra/backups/package.json
+++ b/@xen-orchestra/backups/package.json
@@ -8,7 +8,7 @@
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
- "version": "0.53.0",
+ "version": "0.53.1",
"engines": {
"node": ">=14.18"
},
@@ -59,7 +59,7 @@
"tmp": "^0.2.1"
},
"peerDependencies": {
- "@xen-orchestra/xapi": "^7.3.0"
+ "@xen-orchestra/xapi": "^7.4.0"
},
"license": "AGPL-3.0-or-later",
"author": {
diff --git a/@xen-orchestra/immutable-backups/package.json b/@xen-orchestra/immutable-backups/package.json
index b41e05f2016..884643944c1 100644
--- a/@xen-orchestra/immutable-backups/package.json
+++ b/@xen-orchestra/immutable-backups/package.json
@@ -23,7 +23,7 @@
},
"dependencies": {
"@vates/async-each": "^1.0.0",
- "@xen-orchestra/backups": "^0.53.0",
+ "@xen-orchestra/backups": "^0.53.1",
"@xen-orchestra/log": "^0.6.0",
"app-conf": "^3.0.0",
"chokidar": "^3.5.3",
diff --git a/@xen-orchestra/lite/src/stories/web-core/state-hero/no-data-hero.story.vue b/@xen-orchestra/lite/src/stories/web-core/state-hero/no-data-hero.story.vue
new file mode 100644
index 00000000000..fff487c0574
--- /dev/null
+++ b/@xen-orchestra/lite/src/stories/web-core/state-hero/no-data-hero.story.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/@xen-orchestra/proxy/package.json b/@xen-orchestra/proxy/package.json
index 04ee06bd052..3d28923c1c3 100644
--- a/@xen-orchestra/proxy/package.json
+++ b/@xen-orchestra/proxy/package.json
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@xen-orchestra/proxy",
- "version": "0.28.13",
+ "version": "0.28.14",
"license": "AGPL-3.0-or-later",
"description": "XO Proxy used to remotely execute backup jobs",
"keywords": [
@@ -33,13 +33,13 @@
"@vates/disposable": "^0.1.5",
"@vates/task": "^0.4.0",
"@xen-orchestra/async-map": "^0.1.2",
- "@xen-orchestra/backups": "^0.53.0",
+ "@xen-orchestra/backups": "^0.53.1",
"@xen-orchestra/fs": "^4.1.7",
"@xen-orchestra/log": "^0.6.0",
"@xen-orchestra/mixin": "^0.1.0",
"@xen-orchestra/mixins": "^0.16.0",
"@xen-orchestra/self-signed": "^0.2.1",
- "@xen-orchestra/xapi": "^7.3.0",
+ "@xen-orchestra/xapi": "^7.4.0",
"ajv": "^8.0.3",
"app-conf": "^3.0.0",
"async-iterator-to-stream": "^1.1.0",
diff --git a/@xen-orchestra/web-core/docs/composables/table.composable.md b/@xen-orchestra/web-core/docs/composables/table.composable.md
new file mode 100644
index 00000000000..01ff3756697
--- /dev/null
+++ b/@xen-orchestra/web-core/docs/composables/table.composable.md
@@ -0,0 +1,159 @@
+# `useTable` composable
+
+## Usage
+
+```ts
+const { columns, visibleColumns, rows, columnsById } = useTable('', records, {
+ rowId: record => record.id,
+ columns: define => [
+ define('', { label: 'Column 1' }),
+ define('', { label: 'Column 2' }),
+ define('', { label: 'Column 3' }),
+ ],
+})
+```
+
+## `useTable` options
+
+| Name | Type | Required | Description |
+| --------- | ---------------------------------------------- | :------: | ------------------------------------------------- |
+| `rowId` | `(record: TRecord) => string` | ✓ | A function that define the id of a row. |
+| `columns` | `(define: DefineColumn) => ColumnDefinition[]` | ✓ | A function that defines the columns of the table. |
+
+## Defining a column
+
+```ts
+define('', options) // TValue will be TRecord[TColumnId]
+define('', ``, options) // TValue will be TRecord[TProperty]
+define('', (record: TRecord) => '', options) // TValue will be the result of the function
+```
+
+### Column options
+
+| Name | Type | Required | Default | Description |
+| ------------ | ---------------------------------- | :------: | ------- | -------------------------------------- |
+| `label` | `string` | ✓ | | The column label. |
+| `isHideable` | `boolean` | | `true` | Indicates if the column can be hidden. |
+| `compareFn` | `(a: TValue, b: TValue) => number` | | | A function used to compare the values. |
+
+## `columns`
+
+An array containing all columns defined in the table.
+
+### Properties of a column
+
+| Name | Type | Description |
+| ------------ | ----------------------------- | ------------------------------------------------ |
+| `id` | `string` | The column id. |
+| `label` | `string` | The column label. |
+| `isVisible` | `boolean` | Indicates if the column is visible. |
+| `getter` | `(record: TRecord) => TValue` | A function that returns the value of the column. |
+| `isSortable` | `boolean` | Indicates if the column is sortable. |
+| `isHideable` | `boolean` | Indicates if the column is hideable. |
+
+#### If `isSortable` is `true`
+
+| Name | Type | Description |
+| --------------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------- |
+| `compareFn` | `(a: TValue, b: TValue) => number` | The compare function defined in the column options. |
+| `isSorted` | `boolean` | Indicates if the column is sorted. |
+| `isSortedAsc` | `boolean` | Indicates if the column is sorted in ascending order. |
+| `isSortedDesc` | `boolean` | Indicates if the column is sorted in descending order. |
+| `sort` | `(direction, toggleOffIfSameDirection?) => void` | A function that sorts the rows based on the column values. |
+| ⤷ `direction` | `'asc' \| 'desc' \| false` | The sort direction. If `false`, the column is unsorted. |
+| ⤷ `toggleOffIfSameDirection` | `boolean` | Indicates if the column should be unsorted if it is already sorted in the same direction. |
+| `sortAsc` | `(toggleOffIfSameDirection?) => void` | A function that sorts the rows based on the column values in ascending order. |
+| ⤷ `toggleOffIfSameDirection` | `boolean` | Indicates if the column should be unsorted if it is already sorted in ascending order. |
+| `sortDesc` | `(toggleOffIfSameDirection?) => void` | A function that sorts the rows based on the column values in descending order. |
+| ⤷ `toggleOffIfSameDirection` | `boolean` | Indicates if the column should be unsorted if it is already sorted in descending order. |
+
+#### If `isHideable` is `true`
+
+| Name | Type | Description |
+| -------------------- | --------------------------- | ------------------------------------------------------------------------------- |
+| `hide` | `() => void` | A function that hides the column. |
+| `show` | `() => void` | A function that shows the column. |
+| `toggle` | `(value?: boolean) => void` | A function that toggles the visibility of the column. |
+| ⤷ `value` | `boolean \| undefined` | If undefined, the visibility will be toggled. Else it will be set to the value. |
+
+## `visibleColumns`
+
+Same as `columns` but only contains the visible columns.
+
+## `rows`
+
+An array containing all rows of the table.
+
+### Properties of a row
+
+| Name | Type | Description |
+| ---------------- | ---------- | ------------------------------- |
+| `id` | `string` | The row id. |
+| `value` | `TRecord` | The record of the row. |
+| `visibleColumns` | `Column[]` | The visible columns of the row. |
+
+#### `visibleColumns`
+
+An array containing the visible columns of the row.
+
+##### Properties of a row column
+
+| Name | Type | Description |
+| ------- | -------- | ------------------------ |
+| `id` | `string` | The column id. |
+| `value` | `TValue` | The value of the column. |
+
+## `columnsById`
+
+An object containing all columns defined in the table indexed by their id.
+
+## Example
+
+```vue
+
+