Skip to content

Commit 3f93162

Browse files
authored
Merge pull request #233 from fractal-analytics-platform/display-task-list-with-richer-structure
Improve management of tasks within the client
2 parents e569257 + fa6ca40 commit 3f93162

File tree

10 files changed

+403
-14
lines changed

10 files changed

+403
-14
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { it, expect } from 'vitest';
2+
import { orderTasksByOwnerThenByNameThenByVersion } from '$lib/common/component_utilities.js';
3+
4+
it('should order tasks by owner, then by name, then by version', () => {
5+
const tasks = [
6+
{ name: 'task1', owner: 'owner1', version: '0.0.1' },
7+
{ name: 'task2', owner: 'owner1', version: '0.0.1' },
8+
{ name: 'task3', owner: 'owner1', version: '0.0.2' },
9+
{ name: 'task4', owner: 'owner2', version: '0.0.1' },
10+
{ name: 'task5', owner: 'owner2', version: '0.0.2' },
11+
{ name: 'task6', owner: 'owner2', version: '0.0.2' },
12+
{ name: 'task7', owner: 'admin', version: '0.0.1' },
13+
{ name: 'task8', owner: 'owner3', version: '0.0.2' },
14+
{ name: 'task9', owner: 'owner3', version: '0.0.2' }
15+
];
16+
17+
const sortedTasks = orderTasksByOwnerThenByNameThenByVersion(tasks);
18+
19+
expect(sortedTasks).toEqual([
20+
{ name: 'task7', owner: 'admin', version: '0.0.1' },
21+
{ name: 'task1', owner: 'owner1', version: '0.0.1' },
22+
{ name: 'task2', owner: 'owner1', version: '0.0.1' },
23+
{ name: 'task3', owner: 'owner1', version: '0.0.2' },
24+
{ name: 'task4', owner: 'owner2', version: '0.0.1' },
25+
{ name: 'task5', owner: 'owner2', version: '0.0.2' },
26+
{ name: 'task6', owner: 'owner2', version: '0.0.2' },
27+
{ name: 'task8', owner: 'owner3', version: '0.0.2' },
28+
{ name: 'task9', owner: 'owner3', version: '0.0.2' }
29+
]);
30+
31+
const sortedTasks2 = orderTasksByOwnerThenByNameThenByVersion(tasks, 'owner2');
32+
33+
expect(sortedTasks2).toEqual([
34+
{ name: 'task4', owner: 'owner2', version: '0.0.1' },
35+
{ name: 'task5', owner: 'owner2', version: '0.0.2' },
36+
{ name: 'task6', owner: 'owner2', version: '0.0.2' },
37+
{ name: 'task7', owner: 'admin', version: '0.0.1' },
38+
{ name: 'task1', owner: 'owner1', version: '0.0.1' },
39+
{ name: 'task2', owner: 'owner1', version: '0.0.1' },
40+
{ name: 'task3', owner: 'owner1', version: '0.0.2' },
41+
{ name: 'task8', owner: 'owner3', version: '0.0.2' },
42+
{ name: 'task9', owner: 'owner3', version: '0.0.2' }
43+
]);
44+
});

__tests__/semver.test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { it, expect } from 'vitest';
2+
import semver from 'semver';
3+
import { greatestVersionAsc } from '../src/lib/common/component_utilities';
4+
5+
it('should be able to loosely validate versions', () => {
6+
const cases = [
7+
['0.10.0a0', '0.10.0-a0'],
8+
['0.10.0a2', '0.10.0-a2'],
9+
['0.10.0alpha0', '0.10.0-alpha0'],
10+
['0.10.0alpha3', '0.10.0-alpha3'],
11+
['0.10.0b4', '0.10.0-b4'],
12+
['0.10.0beta5', '0.10.0-beta5'],
13+
['0.10.0c0', '0.10.0-c0'],
14+
['1.0.0rc4.dev7', '1.0.0-rc4.dev7']
15+
];
16+
17+
const validationOptions = {
18+
loose: true,
19+
includePrerelease: true
20+
};
21+
22+
cases.forEach(([input, expected]) => {
23+
const validatedVersion = semver.valid(input, validationOptions);
24+
expect(validatedVersion).toBe(expected);
25+
});
26+
});
27+
28+
it('should sort versions correctly', () => {
29+
30+
const versions = [
31+
{ version: '0.10.0c0' },
32+
{ version: '0.10.0b4' },
33+
{ version: '0.10.0' },
34+
{ version: '0.10.0alpha3' },
35+
{ version: '0.10.0a2' },
36+
{ version: '1.0.0' },
37+
{ version: '0.10.0a0' },
38+
{ version: '1.0.0rc4.dev7' },
39+
{ version: '0.10.0beta5' },
40+
{ version: '0.10.0alpha0' }
41+
];
42+
43+
const sortedVersions = versions.sort(greatestVersionAsc);
44+
45+
expect(sortedVersions).toEqual([
46+
{ version: '0.10.0a0' },
47+
{ version: '0.10.0a2' },
48+
{ version: '0.10.0alpha0' },
49+
{ version: '0.10.0alpha3' },
50+
{ version: '0.10.0b4' },
51+
{ version: '0.10.0beta5' },
52+
{ version: '0.10.0c0' },
53+
{ version: '0.10.0' },
54+
{ version: '1.0.0rc4.dev7' },
55+
{ version: '1.0.0' }
56+
]);
57+
});

package-lock.json

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"dependencies": {
3535
"@vincjo/datatables": "^1.6.0",
3636
"ajv": "^8.12.0",
37-
"jose": "^4.14.4"
37+
"jose": "^4.14.4",
38+
"slim-select": "^2.5.1"
3839
}
3940
}

src/app.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css"
1818
/>
1919
<title>Fractal Web Client</title>
20+
<link rel='stylesheet' href='%sveltekit.assets%/slimselect.css'>
2021
<link rel='stylesheet' href='%sveltekit.assets%/global.css'>
2122
</head>
2223
<body>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import semver from 'semver';
2+
3+
export function greatestVersionAsc(t1, t2) {
4+
const semverValidationOptions = {
5+
loose: true,
6+
includePrerelease: true
7+
};
8+
try {
9+
const t1Version = semver.valid(t1.version, semverValidationOptions);
10+
const t2Version = semver.valid(t2.version, semverValidationOptions);
11+
const t1VersionLt = semver.lte(t1Version, t2Version);
12+
if (t1VersionLt) return -1;
13+
if (!t1VersionLt) return 1;
14+
} catch {
15+
return 0;
16+
}
17+
}
18+
19+
export function greatestVersionDesc(t1, t2) {
20+
const semverValidationOptions = {
21+
loose: true,
22+
includePrerelease: true
23+
};
24+
try {
25+
const t1Version = semver.valid(t1.version, semverValidationOptions);
26+
const t2Version = semver.valid(t2.version, semverValidationOptions);
27+
const t1VersionGt = semver.gte(t1Version, t2Version);
28+
if (t1VersionGt) return -1;
29+
if (!t1VersionGt) return 1;
30+
} catch {
31+
return 0;
32+
}
33+
}
34+
35+
function compareTaskNameAndVersion(t1, t2) {
36+
if (t1.name < t2.name) return -1;
37+
if (t1.name > t2.name) return 1;
38+
// Names are equal, sort by version
39+
return greatestVersionAsc(t1, t2);
40+
}
41+
42+
export function orderTasksByOwnerThenByNameThenByVersion(tasks, ownerName = null) {
43+
// Sort tasks by owner, by name and by version
44+
return tasks.sort((t1, t2) => {
45+
// If ownerName is not null, filter tasks by owner
46+
if (ownerName !== null) {
47+
// If t1 owner is same as ownerName, t1 should go before t2
48+
if (t1.owner === ownerName) {
49+
if (t2.owner !== ownerName) return -1;
50+
// Both owners are same, sort by name and version
51+
return compareTaskNameAndVersion(t1, t2);
52+
} else {
53+
// t1 owner is not same as ownerName, t2 should go before t1
54+
if (t2.owner === ownerName) return 1;
55+
}
56+
}
57+
if (t1.owner === null) {
58+
// If t1 owner is null, t1 should go before t2 if t2 owner is null
59+
// Should check if t2 owner is null too
60+
if (t2.owner === null) {
61+
// Both owners are null, sort by name
62+
return compareTaskNameAndVersion(t1, t2);
63+
} else {
64+
// t1 owner is null, t2 owner is not null, t1 should go before t2
65+
return -1;
66+
}
67+
} else {
68+
// t1 owner is not null, t2 owner is null, t2 should go before t1
69+
if (t2.owner === null) return 1;
70+
// Both owners are not null, sort by owner
71+
if (t1.owner < t2.owner) return -1;
72+
if (t1.owner > t2.owner) return 1;
73+
// Owners are equal, sort by name
74+
return compareTaskNameAndVersion(t1, t2);
75+
}
76+
});
77+
}

0 commit comments

Comments
 (0)