Skip to content

Commit 7c55051

Browse files
committed
Added "Export to file" buttons on resources table
1 parent 8d8cf3a commit 7c55051

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-6
lines changed

src/routes/v2/admin/resources/+page.svelte

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
<script>
2+
import { downloadBlob } from '$lib/common/component_utilities';
23
import { displayStandardErrorAlert, getAlertErrorFromResponse } from '$lib/common/errors';
34
import ConfirmActionButton from '$lib/components/common/ConfirmActionButton.svelte';
45
import { onMount } from 'svelte';
56
67
/** @type {Array<import('fractal-components/types/api').Resource>} */
78
let resources = $state([]);
89
/** @type {import('$lib/components/common/StandardErrorAlert.svelte').default|undefined} */
9-
let searchErrorAlert;
10+
let resourcesErrorAlert;
1011
1112
async function loadResources() {
12-
searchErrorAlert?.hide();
13+
resourcesErrorAlert?.hide();
1314
const url = new URL('/api/admin/v2/resource', window.location.origin);
1415
const response = await fetch(url);
1516
if (!response.ok) {
16-
searchErrorAlert = displayStandardErrorAlert(
17+
resourcesErrorAlert = displayStandardErrorAlert(
1718
await getAlertErrorFromResponse(response),
18-
'searchError'
19+
'resourcesError'
1920
);
2021
return;
2122
}
@@ -37,6 +38,23 @@
3738
}
3839
}
3940
41+
/**
42+
* @param {number} resourceId
43+
*/
44+
async function exportToFile(resourceId) {
45+
const response = await fetch(`/api/admin/v2/resource/${resourceId}`);
46+
if (response.ok) {
47+
/** @type {import('fractal-components/types/api').Resource} */
48+
const resource = await response.json();
49+
downloadBlob(JSON.stringify(resource, null, 2), `${resource.name}.json`, 'application/json');
50+
} else {
51+
resourcesErrorAlert = displayStandardErrorAlert(
52+
await getAlertErrorFromResponse(response),
53+
'resourcesError'
54+
);
55+
}
56+
}
57+
4058
onMount(async () => {
4159
await loadResources();
4260
});
@@ -78,6 +96,13 @@
7896
<a href="/v2/admin/resources/{resource.id}/edit" class="btn btn-primary">
7997
<i class="bi bi-pencil"></i> Edit
8098
</a>
99+
<button
100+
type="button"
101+
class="btn btn-outline-primary"
102+
onclick={() => exportToFile(resource.id)}
103+
>
104+
<i class="bi bi-download"></i> Export to file
105+
</button>
81106
<ConfirmActionButton
82107
modalId={'confirmDeleteResource' + resource.id}
83108
style="danger"
@@ -93,6 +118,6 @@
93118
</tbody>
94119
</table>
95120
96-
<div id="searchError" class="mt-3"></div>
121+
<div id="resourcesError" class="mt-3"></div>
97122
</div>
98123
</div>

tests/v2/resource_crud.spec.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { expect, test } from '@playwright/test';
22
import { setUploadFile, waitModal, waitModalClosed, waitPageLoading } from '../utils.js';
33
import path from 'path';
44
import { fileURLToPath } from 'url';
5+
import fs from 'fs';
6+
import os from 'os';
57

68
const __filename = fileURLToPath(import.meta.url);
79
const __dirname = path.dirname(__filename);
@@ -58,9 +60,23 @@ test('Create, update and delete a resource', async ({ page }) => {
5860
await expect(page.getByText('Resource updated')).toBeVisible();
5961
});
6062

61-
await test.step('Delete the resource', async () => {
63+
await test.step('Export the resource to file', async () => {
6264
await page.getByRole('link', { name: 'Resources' }).click();
6365
await waitPageLoading(page);
66+
const downloadPromise = page.waitForEvent('download');
67+
await page
68+
.getByRole('row', { name: `${randomResourceName}-renamed` })
69+
.getByRole('button', { name: 'Export to file' })
70+
.click();
71+
const download = await downloadPromise;
72+
const file = path.join(os.tmpdir(), download.suggestedFilename());
73+
await download.saveAs(file);
74+
const data = JSON.parse(fs.readFileSync(file).toString());
75+
expect(data.name).toEqual(`${randomResourceName}-renamed`);
76+
expect(data.type).toEqual('local');
77+
});
78+
79+
await test.step('Delete the resource', async () => {
6480
await page
6581
.getByRole('row', { name: `${randomResourceName}-renamed` })
6682
.getByRole('button', { name: 'Delete' })

0 commit comments

Comments
 (0)