@jskit-ai/users-core 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.descriptor.mjs +9 -5
- package/package.json +1 -1
- package/src/server/common/formatters/workspaceFormatter.js +16 -3
- package/src/server/common/repositories/workspacesRepository.js +20 -2
- package/src/server/common/support/deepFreeze.js +1 -17
- package/src/server/common/support/resolveActionUser.js +1 -3
- package/src/shared/index.js +34 -1
- package/src/shared/resources/consoleSettingsResource.js +4 -15
- package/src/shared/resources/userSettingsResource.js +2 -2
- package/src/shared/resources/workspaceSettingsResource.js +2 -2
- package/src/shared/settings.js +123 -6
- package/templates/migrations/users_core_console_owner.cjs +0 -2
- package/templates/migrations/users_core_initial.cjs +5 -4
- package/templates/migrations/users_core_profile_username.cjs +0 -2
- package/templates/packages/main/src/shared/resources/workspaceSettingsFields.js +90 -3
- package/test/workspaceSettingsRepository.test.js +33 -4
- package/test/workspaceSettingsResource.test.js +16 -0
- package/test/workspaceSettingsService.test.js +18 -2
- package/src/shared/resources/normalizeSettingsFieldInput.js +0 -19
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/users-core",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.13",
|
|
5
5
|
description: "Users/workspace domain runtime plus HTTP routes for workspace, account, and console features.",
|
|
6
6
|
dependsOn: [
|
|
7
7
|
"@jskit-ai/auth-core",
|
|
@@ -214,7 +214,14 @@ export default Object.freeze({
|
|
|
214
214
|
dev: {}
|
|
215
215
|
},
|
|
216
216
|
packageJson: {
|
|
217
|
-
scripts: {
|
|
217
|
+
scripts: {
|
|
218
|
+
"server:app": "SERVER_SURFACE=app node ./bin/server.js",
|
|
219
|
+
"server:admin": "SERVER_SURFACE=admin node ./bin/server.js",
|
|
220
|
+
"dev:app": "VITE_SURFACE=app vite",
|
|
221
|
+
"dev:admin": "VITE_SURFACE=admin vite",
|
|
222
|
+
"build:app": "VITE_SURFACE=app vite build",
|
|
223
|
+
"build:admin": "VITE_SURFACE=admin vite build"
|
|
224
|
+
}
|
|
218
225
|
},
|
|
219
226
|
procfile: {},
|
|
220
227
|
files: [
|
|
@@ -222,7 +229,6 @@ export default Object.freeze({
|
|
|
222
229
|
op: "install-migration",
|
|
223
230
|
from: "templates/migrations/users_core_initial.cjs",
|
|
224
231
|
toDir: "migrations",
|
|
225
|
-
slug: "users_core_initial",
|
|
226
232
|
extension: ".cjs",
|
|
227
233
|
reason: "Install users/workspace core schema migration.",
|
|
228
234
|
category: "migration",
|
|
@@ -232,7 +238,6 @@ export default Object.freeze({
|
|
|
232
238
|
op: "install-migration",
|
|
233
239
|
from: "templates/migrations/users_core_profile_username.cjs",
|
|
234
240
|
toDir: "migrations",
|
|
235
|
-
slug: "users_core_profile_username",
|
|
236
241
|
extension: ".cjs",
|
|
237
242
|
reason: "Install users profile username migration.",
|
|
238
243
|
category: "migration",
|
|
@@ -242,7 +247,6 @@ export default Object.freeze({
|
|
|
242
247
|
op: "install-migration",
|
|
243
248
|
from: "templates/migrations/users_core_console_owner.cjs",
|
|
244
249
|
toDir: "migrations",
|
|
245
|
-
slug: "users_core_console_owner",
|
|
246
250
|
extension: ".cjs",
|
|
247
251
|
reason: "Install users/workspace console owner migration.",
|
|
248
252
|
category: "migration",
|
package/package.json
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
coerceWorkspaceColor,
|
|
3
|
+
coerceWorkspaceSecondaryColor,
|
|
4
|
+
coerceWorkspaceSurfaceColor,
|
|
5
|
+
coerceWorkspaceSurfaceVariantColor
|
|
6
|
+
} from "../../../shared/settings.js";
|
|
2
7
|
import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
3
8
|
|
|
4
9
|
function mapWorkspaceSummary(workspace, membership) {
|
|
10
|
+
const color = coerceWorkspaceColor(workspace.color);
|
|
5
11
|
return {
|
|
6
12
|
id: Number(workspace.id),
|
|
7
13
|
slug: normalizeText(workspace.slug),
|
|
8
14
|
name: normalizeText(workspace.name),
|
|
9
|
-
color
|
|
15
|
+
color,
|
|
16
|
+
secondaryColor: coerceWorkspaceSecondaryColor(workspace.secondaryColor, { color }),
|
|
17
|
+
surfaceColor: coerceWorkspaceSurfaceColor(workspace.surfaceColor, { color }),
|
|
18
|
+
surfaceVariantColor: coerceWorkspaceSurfaceVariantColor(workspace.surfaceVariantColor, { color }),
|
|
10
19
|
avatarUrl: normalizeText(workspace.avatarUrl),
|
|
11
20
|
roleId: normalizeLowerText(membership?.roleId || "member") || "member",
|
|
12
21
|
isAccessible: normalizeLowerText(membership?.status || "active") === "active"
|
|
@@ -17,9 +26,13 @@ function mapWorkspaceSettingsPublic(workspaceSettings, { workspaceInvitationsEna
|
|
|
17
26
|
const source = workspaceSettings && typeof workspaceSettings === "object" ? workspaceSettings : {};
|
|
18
27
|
const invitesAvailable = workspaceInvitationsEnabled === true;
|
|
19
28
|
const invitesEnabled = invitesAvailable && source.invitesEnabled !== false;
|
|
29
|
+
const color = coerceWorkspaceColor(source.color);
|
|
20
30
|
return {
|
|
21
31
|
name: normalizeText(source.name),
|
|
22
|
-
color
|
|
32
|
+
color,
|
|
33
|
+
secondaryColor: coerceWorkspaceSecondaryColor(source.secondaryColor, { color }),
|
|
34
|
+
surfaceColor: coerceWorkspaceSurfaceColor(source.surfaceColor, { color }),
|
|
35
|
+
surfaceVariantColor: coerceWorkspaceSurfaceVariantColor(source.surfaceVariantColor, { color }),
|
|
23
36
|
avatarUrl: normalizeText(source.avatarUrl),
|
|
24
37
|
invitesEnabled,
|
|
25
38
|
invitesAvailable,
|
|
@@ -6,12 +6,18 @@ import {
|
|
|
6
6
|
nowDb,
|
|
7
7
|
isDuplicateEntryError
|
|
8
8
|
} from "./repositoryUtils.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
coerceWorkspaceColor,
|
|
11
|
+
coerceWorkspaceSecondaryColor,
|
|
12
|
+
coerceWorkspaceSurfaceColor,
|
|
13
|
+
coerceWorkspaceSurfaceVariantColor
|
|
14
|
+
} from "../../../shared/settings.js";
|
|
10
15
|
|
|
11
16
|
function mapRow(row) {
|
|
12
17
|
if (!row) {
|
|
13
18
|
return null;
|
|
14
19
|
}
|
|
20
|
+
const color = coerceWorkspaceColor(row.color);
|
|
15
21
|
|
|
16
22
|
return {
|
|
17
23
|
id: Number(row.id),
|
|
@@ -20,7 +26,16 @@ function mapRow(row) {
|
|
|
20
26
|
ownerUserId: Number(row.owner_user_id),
|
|
21
27
|
isPersonal: Boolean(row.is_personal),
|
|
22
28
|
avatarUrl: row.avatar_url ? normalizeText(row.avatar_url) : "",
|
|
23
|
-
color
|
|
29
|
+
color,
|
|
30
|
+
secondaryColor: coerceWorkspaceSecondaryColor(row.secondary_color, {
|
|
31
|
+
color
|
|
32
|
+
}),
|
|
33
|
+
surfaceColor: coerceWorkspaceSurfaceColor(row.surface_color, {
|
|
34
|
+
color
|
|
35
|
+
}),
|
|
36
|
+
surfaceVariantColor: coerceWorkspaceSurfaceVariantColor(row.surface_variant_color, {
|
|
37
|
+
color
|
|
38
|
+
}),
|
|
24
39
|
createdAt: toIsoString(row.created_at),
|
|
25
40
|
updatedAt: toIsoString(row.updated_at),
|
|
26
41
|
deletedAt: toNullableIso(row.deleted_at)
|
|
@@ -53,6 +68,9 @@ function createRepository(knex) {
|
|
|
53
68
|
"w.is_personal",
|
|
54
69
|
client.raw("COALESCE(ws.avatar_url, w.avatar_url) as avatar_url"),
|
|
55
70
|
client.raw("COALESCE(ws.color, w.color) as color"),
|
|
71
|
+
client.raw("COALESCE(ws.secondary_color, ws.color, w.color) as secondary_color"),
|
|
72
|
+
client.raw("COALESCE(ws.surface_color, ws.color, w.color) as surface_color"),
|
|
73
|
+
client.raw("COALESCE(ws.surface_variant_color, ws.color, w.color) as surface_variant_color"),
|
|
56
74
|
"w.created_at",
|
|
57
75
|
"w.updated_at",
|
|
58
76
|
"w.deleted_at"
|
|
@@ -1,17 +1 @@
|
|
|
1
|
-
|
|
2
|
-
if (!value || typeof value !== "object") {
|
|
3
|
-
return value;
|
|
4
|
-
}
|
|
5
|
-
if (seen.has(value)) {
|
|
6
|
-
return value;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
seen.add(value);
|
|
10
|
-
for (const key of Object.keys(value)) {
|
|
11
|
-
deepFreeze(value[key], seen);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return Object.freeze(value);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export { deepFreeze };
|
|
1
|
+
export { deepFreeze } from "@jskit-ai/kernel/shared/support/deepFreeze";
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
3
|
-
}
|
|
1
|
+
import { normalizeObject } from "@jskit-ai/kernel/shared/support/normalize";
|
|
4
2
|
|
|
5
3
|
function resolveActionUser(context, input) {
|
|
6
4
|
const payload = normalizeObject(input);
|
package/src/shared/index.js
CHANGED
|
@@ -8,9 +8,20 @@ import {
|
|
|
8
8
|
} from "./roles.js";
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
+
coerceWorkspaceBackgroundColor,
|
|
11
12
|
DEFAULT_WORKSPACE_COLOR,
|
|
12
13
|
DEFAULT_USER_SETTINGS,
|
|
13
|
-
coerceWorkspaceColor
|
|
14
|
+
coerceWorkspaceColor,
|
|
15
|
+
coerceWorkspaceSecondaryColor,
|
|
16
|
+
coerceWorkspaceSurfaceColor,
|
|
17
|
+
coerceWorkspaceSurfaceVariantColor,
|
|
18
|
+
deriveWorkspaceBackgroundColor,
|
|
19
|
+
deriveWorkspaceSecondaryColor,
|
|
20
|
+
deriveWorkspaceSurfaceColor,
|
|
21
|
+
deriveWorkspaceSurfaceVariantColor,
|
|
22
|
+
mixHexColors,
|
|
23
|
+
normalizeWorkspaceHexColor,
|
|
24
|
+
resolveWorkspaceThemePalette
|
|
14
25
|
} from "./settings.js";
|
|
15
26
|
import {
|
|
16
27
|
TENANCY_MODE_NONE,
|
|
@@ -43,6 +54,17 @@ const USERS_SHARED_API = Object.freeze({
|
|
|
43
54
|
DEFAULT_WORKSPACE_COLOR,
|
|
44
55
|
DEFAULT_USER_SETTINGS,
|
|
45
56
|
coerceWorkspaceColor,
|
|
57
|
+
coerceWorkspaceSecondaryColor,
|
|
58
|
+
coerceWorkspaceSurfaceColor,
|
|
59
|
+
coerceWorkspaceSurfaceVariantColor,
|
|
60
|
+
coerceWorkspaceBackgroundColor,
|
|
61
|
+
deriveWorkspaceSecondaryColor,
|
|
62
|
+
deriveWorkspaceSurfaceColor,
|
|
63
|
+
deriveWorkspaceSurfaceVariantColor,
|
|
64
|
+
deriveWorkspaceBackgroundColor,
|
|
65
|
+
normalizeWorkspaceHexColor,
|
|
66
|
+
mixHexColors,
|
|
67
|
+
resolveWorkspaceThemePalette,
|
|
46
68
|
TENANCY_MODE_NONE,
|
|
47
69
|
TENANCY_MODE_PERSONAL,
|
|
48
70
|
TENANCY_MODE_WORKSPACE,
|
|
@@ -71,6 +93,17 @@ export {
|
|
|
71
93
|
DEFAULT_WORKSPACE_COLOR,
|
|
72
94
|
DEFAULT_USER_SETTINGS,
|
|
73
95
|
coerceWorkspaceColor,
|
|
96
|
+
coerceWorkspaceSecondaryColor,
|
|
97
|
+
coerceWorkspaceSurfaceColor,
|
|
98
|
+
coerceWorkspaceSurfaceVariantColor,
|
|
99
|
+
coerceWorkspaceBackgroundColor,
|
|
100
|
+
deriveWorkspaceSecondaryColor,
|
|
101
|
+
deriveWorkspaceSurfaceColor,
|
|
102
|
+
deriveWorkspaceSurfaceVariantColor,
|
|
103
|
+
deriveWorkspaceBackgroundColor,
|
|
104
|
+
normalizeWorkspaceHexColor,
|
|
105
|
+
mixHexColors,
|
|
106
|
+
resolveWorkspaceThemePalette,
|
|
74
107
|
TENANCY_MODE_NONE,
|
|
75
108
|
TENANCY_MODE_PERSONAL,
|
|
76
109
|
TENANCY_MODE_WORKSPACE,
|
|
@@ -2,9 +2,10 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { createOperationMessages } from "../operationMessages.js";
|
|
3
3
|
import {
|
|
4
4
|
createCursorListValidator,
|
|
5
|
-
normalizeObjectInput
|
|
5
|
+
normalizeObjectInput,
|
|
6
|
+
normalizeSettingsFieldInput,
|
|
7
|
+
normalizeSettingsFieldOutput
|
|
6
8
|
} from "@jskit-ai/kernel/shared/validators";
|
|
7
|
-
import { normalizeSettingsFieldInput } from "./normalizeSettingsFieldInput.js";
|
|
8
9
|
import { consoleSettingsFields } from "./consoleSettingsFields.js";
|
|
9
10
|
|
|
10
11
|
function buildCreateSchema() {
|
|
@@ -57,21 +58,9 @@ const consoleSettingsOutputValidator = Object.freeze({
|
|
|
57
58
|
normalize(payload = {}) {
|
|
58
59
|
const source = normalizeObjectInput(payload);
|
|
59
60
|
const settingsSource = normalizeObjectInput(source.settings);
|
|
60
|
-
const settings = {};
|
|
61
|
-
|
|
62
|
-
for (const field of consoleSettingsFields) {
|
|
63
|
-
const rawValue = Object.hasOwn(settingsSource, field.key)
|
|
64
|
-
? settingsSource[field.key]
|
|
65
|
-
: field.resolveDefault({
|
|
66
|
-
settings: settingsSource
|
|
67
|
-
});
|
|
68
|
-
settings[field.key] = field.normalizeOutput(rawValue, {
|
|
69
|
-
settings: settingsSource
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
61
|
|
|
73
62
|
return {
|
|
74
|
-
settings
|
|
63
|
+
settings: normalizeSettingsFieldOutput(settingsSource, consoleSettingsFields)
|
|
75
64
|
};
|
|
76
65
|
}
|
|
77
66
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import {
|
|
3
3
|
createCursorListValidator,
|
|
4
|
-
normalizeObjectInput
|
|
4
|
+
normalizeObjectInput,
|
|
5
|
+
normalizeSettingsFieldInput
|
|
5
6
|
} from "@jskit-ai/kernel/shared/validators";
|
|
6
7
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
7
8
|
import { createOperationMessages } from "../operationMessages.js";
|
|
8
|
-
import { normalizeSettingsFieldInput } from "./normalizeSettingsFieldInput.js";
|
|
9
9
|
import { userProfileResource } from "./userProfileResource.js";
|
|
10
10
|
import {
|
|
11
11
|
USER_SETTINGS_SECTIONS,
|
|
@@ -2,9 +2,9 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
3
3
|
import {
|
|
4
4
|
normalizeObjectInput,
|
|
5
|
-
createCursorListValidator
|
|
5
|
+
createCursorListValidator,
|
|
6
|
+
normalizeSettingsFieldInput
|
|
6
7
|
} from "@jskit-ai/kernel/shared/validators";
|
|
7
|
-
import { normalizeSettingsFieldInput } from "./normalizeSettingsFieldInput.js";
|
|
8
8
|
import { workspaceSettingsFields } from "./workspaceSettingsFields.js";
|
|
9
9
|
import { createWorkspaceRoleCatalog } from "../roles.js";
|
|
10
10
|
|
package/src/shared/settings.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
const DEFAULT_WORKSPACE_COLOR = "#
|
|
1
|
+
const DEFAULT_WORKSPACE_COLOR = "#2F5D9E";
|
|
2
|
+
const HEX_COLOR_PATTERN = /^#[0-9A-Fa-f]{6}$/;
|
|
2
3
|
|
|
3
4
|
const DEFAULT_USER_SETTINGS = Object.freeze({
|
|
4
5
|
theme: "system",
|
|
@@ -16,16 +17,132 @@ const DEFAULT_USER_SETTINGS = Object.freeze({
|
|
|
16
17
|
lastActiveWorkspaceId: null
|
|
17
18
|
});
|
|
18
19
|
|
|
19
|
-
function
|
|
20
|
+
function normalizeWorkspaceHexColor(value) {
|
|
20
21
|
const normalized = String(value || "").trim();
|
|
21
|
-
if (
|
|
22
|
-
return
|
|
22
|
+
if (!HEX_COLOR_PATTERN.test(normalized)) {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
return normalized.toUpperCase();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function clampChannel(value) {
|
|
29
|
+
if (!Number.isFinite(value)) {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
if (value < 0) {
|
|
33
|
+
return 0;
|
|
23
34
|
}
|
|
24
|
-
|
|
35
|
+
if (value > 255) {
|
|
36
|
+
return 255;
|
|
37
|
+
}
|
|
38
|
+
return Math.round(value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function toHexChannel(value) {
|
|
42
|
+
return clampChannel(value).toString(16).padStart(2, "0").toUpperCase();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseHexColor(value) {
|
|
46
|
+
const normalized = normalizeWorkspaceHexColor(value);
|
|
47
|
+
if (!normalized) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
red: Number.parseInt(normalized.slice(1, 3), 16),
|
|
52
|
+
green: Number.parseInt(normalized.slice(3, 5), 16),
|
|
53
|
+
blue: Number.parseInt(normalized.slice(5, 7), 16)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function mixHexColors(baseColor, mixColor, mixRatio = 0) {
|
|
58
|
+
const base = parseHexColor(baseColor);
|
|
59
|
+
const mixed = parseHexColor(mixColor);
|
|
60
|
+
if (!base || !mixed) {
|
|
61
|
+
return "";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const ratio = Math.min(1, Math.max(0, Number(mixRatio) || 0));
|
|
65
|
+
const keepRatio = 1 - ratio;
|
|
66
|
+
return `#${toHexChannel(base.red * keepRatio + mixed.red * ratio)}${toHexChannel(
|
|
67
|
+
base.green * keepRatio + mixed.green * ratio
|
|
68
|
+
)}${toHexChannel(base.blue * keepRatio + mixed.blue * ratio)}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function coerceWorkspaceColor(value) {
|
|
72
|
+
return normalizeWorkspaceHexColor(value) || DEFAULT_WORKSPACE_COLOR;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function deriveWorkspaceSecondaryColor(workspaceColor = DEFAULT_WORKSPACE_COLOR) {
|
|
76
|
+
return mixHexColors(coerceWorkspaceColor(workspaceColor), "#000000", 0.28) || "#224372";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function deriveWorkspaceSurfaceColor(workspaceColor = DEFAULT_WORKSPACE_COLOR) {
|
|
80
|
+
return mixHexColors(coerceWorkspaceColor(workspaceColor), "#FFFFFF", 0.93) || "#F0F4F8";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function deriveWorkspaceSurfaceVariantColor(workspaceColor = DEFAULT_WORKSPACE_COLOR) {
|
|
84
|
+
return mixHexColors(coerceWorkspaceColor(workspaceColor), "#FFFFFF", 0.86) || "#E2E8F1";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function deriveWorkspaceBackgroundColor(workspaceColor = DEFAULT_WORKSPACE_COLOR) {
|
|
88
|
+
const surfaceColor = deriveWorkspaceSurfaceColor(workspaceColor);
|
|
89
|
+
return mixHexColors(surfaceColor, "#FFFFFF", 0.45) || "#F4FAF8";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function coerceWorkspaceSecondaryColor(value, { color = DEFAULT_WORKSPACE_COLOR } = {}) {
|
|
93
|
+
return normalizeWorkspaceHexColor(value) || deriveWorkspaceSecondaryColor(color);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function coerceWorkspaceSurfaceColor(value, { color = DEFAULT_WORKSPACE_COLOR } = {}) {
|
|
97
|
+
return normalizeWorkspaceHexColor(value) || deriveWorkspaceSurfaceColor(color);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function coerceWorkspaceSurfaceVariantColor(value, { color = DEFAULT_WORKSPACE_COLOR } = {}) {
|
|
101
|
+
return normalizeWorkspaceHexColor(value) || deriveWorkspaceSurfaceVariantColor(color);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function coerceWorkspaceBackgroundColor(value, { color = DEFAULT_WORKSPACE_COLOR } = {}) {
|
|
105
|
+
return normalizeWorkspaceHexColor(value) || deriveWorkspaceBackgroundColor(color);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function resolveWorkspaceThemePalette(input = {}) {
|
|
109
|
+
const source = input && typeof input === "object" ? input : {};
|
|
110
|
+
const color = coerceWorkspaceColor(source.color);
|
|
111
|
+
const secondaryColor = coerceWorkspaceSecondaryColor(source.secondaryColor, {
|
|
112
|
+
color
|
|
113
|
+
});
|
|
114
|
+
const surfaceColor = coerceWorkspaceSurfaceColor(source.surfaceColor, {
|
|
115
|
+
color
|
|
116
|
+
});
|
|
117
|
+
const surfaceVariantColor = coerceWorkspaceSurfaceVariantColor(source.surfaceVariantColor, {
|
|
118
|
+
color
|
|
119
|
+
});
|
|
120
|
+
const backgroundColor = coerceWorkspaceBackgroundColor(source.backgroundColor, {
|
|
121
|
+
color
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return Object.freeze({
|
|
125
|
+
color,
|
|
126
|
+
secondaryColor,
|
|
127
|
+
surfaceColor,
|
|
128
|
+
surfaceVariantColor,
|
|
129
|
+
backgroundColor
|
|
130
|
+
});
|
|
25
131
|
}
|
|
26
132
|
|
|
27
133
|
export {
|
|
28
134
|
DEFAULT_WORKSPACE_COLOR,
|
|
29
135
|
DEFAULT_USER_SETTINGS,
|
|
30
|
-
|
|
136
|
+
coerceWorkspaceBackgroundColor,
|
|
137
|
+
coerceWorkspaceColor,
|
|
138
|
+
coerceWorkspaceSecondaryColor,
|
|
139
|
+
coerceWorkspaceSurfaceColor,
|
|
140
|
+
coerceWorkspaceSurfaceVariantColor,
|
|
141
|
+
deriveWorkspaceBackgroundColor,
|
|
142
|
+
deriveWorkspaceSecondaryColor,
|
|
143
|
+
deriveWorkspaceSurfaceColor,
|
|
144
|
+
deriveWorkspaceSurfaceVariantColor,
|
|
145
|
+
mixHexColors,
|
|
146
|
+
normalizeWorkspaceHexColor,
|
|
147
|
+
resolveWorkspaceThemePalette
|
|
31
148
|
};
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// JSKIT_MIGRATION_ID: users-core-initial-schema
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* @param {import('knex').Knex} knex
|
|
5
3
|
*/
|
|
@@ -27,7 +25,7 @@ exports.up = async function up(knex) {
|
|
|
27
25
|
table.integer("owner_user_id").unsigned().notNullable().references("id").inTable("user_profiles").onDelete("CASCADE");
|
|
28
26
|
table.boolean("is_personal").notNullable().defaultTo(true);
|
|
29
27
|
table.string("avatar_url", 512).notNullable().defaultTo("");
|
|
30
|
-
table.string("color", 7).notNullable().defaultTo("#
|
|
28
|
+
table.string("color", 7).notNullable().defaultTo("#2F5D9E");
|
|
31
29
|
table.timestamp("created_at", { useTz: false }).notNullable().defaultTo(knex.fn.now());
|
|
32
30
|
table.timestamp("updated_at", { useTz: false }).notNullable().defaultTo(knex.fn.now());
|
|
33
31
|
table.timestamp("deleted_at", { useTz: false }).nullable();
|
|
@@ -48,7 +46,10 @@ exports.up = async function up(knex) {
|
|
|
48
46
|
table.integer("workspace_id").unsigned().primary().references("id").inTable("workspaces").onDelete("CASCADE");
|
|
49
47
|
table.string("name", 160).notNullable().defaultTo("Workspace");
|
|
50
48
|
table.string("avatar_url", 512).notNullable().defaultTo("");
|
|
51
|
-
table.string("color", 7).notNullable().defaultTo("#
|
|
49
|
+
table.string("color", 7).notNullable().defaultTo("#2F5D9E");
|
|
50
|
+
table.string("secondary_color", 7).notNullable().defaultTo("#224372");
|
|
51
|
+
table.string("surface_color", 7).notNullable().defaultTo("#F0F4F8");
|
|
52
|
+
table.string("surface_variant_color", 7).notNullable().defaultTo("#E2E8F1");
|
|
52
53
|
table.boolean("invites_enabled").notNullable().defaultTo(true);
|
|
53
54
|
table.timestamp("created_at", { useTz: false }).notNullable().defaultTo(knex.fn.now());
|
|
54
55
|
table.timestamp("updated_at", { useTz: false }).notNullable().defaultTo(knex.fn.now());
|
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
import { Type } from "typebox";
|
|
5
5
|
import { normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
coerceWorkspaceColor,
|
|
8
|
+
coerceWorkspaceSecondaryColor,
|
|
9
|
+
coerceWorkspaceSurfaceColor,
|
|
10
|
+
coerceWorkspaceSurfaceVariantColor
|
|
11
|
+
} from "@jskit-ai/users-core/shared/settings";
|
|
7
12
|
import {
|
|
8
13
|
defineField,
|
|
9
14
|
resetWorkspaceSettingsFields
|
|
@@ -29,6 +34,10 @@ function normalizeHexColor(value) {
|
|
|
29
34
|
return /^#[0-9A-Fa-f]{6}$/.test(color) ? color.toUpperCase() : null;
|
|
30
35
|
}
|
|
31
36
|
|
|
37
|
+
function resolveThemeBaseColor({ workspace = {}, settings = {} } = {}) {
|
|
38
|
+
return normalizeText(settings.color || workspace.color);
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
resetWorkspaceSettingsFields();
|
|
33
42
|
|
|
34
43
|
defineField({
|
|
@@ -78,8 +87,8 @@ defineField({
|
|
|
78
87
|
pattern: "^#[0-9A-Fa-f]{6}$",
|
|
79
88
|
messages: {
|
|
80
89
|
required: "Workspace color is required.",
|
|
81
|
-
pattern: "Workspace color must be a hex color like #
|
|
82
|
-
default: "Workspace color must be a hex color like #
|
|
90
|
+
pattern: "Workspace color must be a hex color like #2F5D9E.",
|
|
91
|
+
default: "Workspace color must be a hex color like #2F5D9E."
|
|
83
92
|
}
|
|
84
93
|
}),
|
|
85
94
|
outputSchema: Type.String({ minLength: 7, maxLength: 7, pattern: "^#[0-9A-Fa-f]{6}$" }),
|
|
@@ -88,6 +97,84 @@ defineField({
|
|
|
88
97
|
resolveDefault: ({ workspace = {} } = {}) => coerceWorkspaceColor(workspace.color)
|
|
89
98
|
});
|
|
90
99
|
|
|
100
|
+
defineField({
|
|
101
|
+
key: "secondaryColor",
|
|
102
|
+
dbColumn: "secondary_color",
|
|
103
|
+
required: true,
|
|
104
|
+
inputSchema: Type.String({
|
|
105
|
+
minLength: 7,
|
|
106
|
+
maxLength: 7,
|
|
107
|
+
pattern: "^#[0-9A-Fa-f]{6}$",
|
|
108
|
+
messages: {
|
|
109
|
+
required: "Secondary color is required.",
|
|
110
|
+
pattern: "Secondary color must be a hex color like #224372.",
|
|
111
|
+
default: "Secondary color must be a hex color like #224372."
|
|
112
|
+
}
|
|
113
|
+
}),
|
|
114
|
+
outputSchema: Type.String({ minLength: 7, maxLength: 7, pattern: "^#[0-9A-Fa-f]{6}$" }),
|
|
115
|
+
normalizeInput: normalizeHexColor,
|
|
116
|
+
normalizeOutput: (value, { workspace = {}, settings = {} } = {}) =>
|
|
117
|
+
coerceWorkspaceSecondaryColor(value, {
|
|
118
|
+
color: resolveThemeBaseColor({ workspace, settings })
|
|
119
|
+
}),
|
|
120
|
+
resolveDefault: ({ workspace = {}, settings = {} } = {}) =>
|
|
121
|
+
coerceWorkspaceSecondaryColor(workspace.secondaryColor, {
|
|
122
|
+
color: resolveThemeBaseColor({ workspace, settings })
|
|
123
|
+
})
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
defineField({
|
|
127
|
+
key: "surfaceColor",
|
|
128
|
+
dbColumn: "surface_color",
|
|
129
|
+
required: true,
|
|
130
|
+
inputSchema: Type.String({
|
|
131
|
+
minLength: 7,
|
|
132
|
+
maxLength: 7,
|
|
133
|
+
pattern: "^#[0-9A-Fa-f]{6}$",
|
|
134
|
+
messages: {
|
|
135
|
+
required: "Surface color is required.",
|
|
136
|
+
pattern: "Surface color must be a hex color like #F0F4F8.",
|
|
137
|
+
default: "Surface color must be a hex color like #F0F4F8."
|
|
138
|
+
}
|
|
139
|
+
}),
|
|
140
|
+
outputSchema: Type.String({ minLength: 7, maxLength: 7, pattern: "^#[0-9A-Fa-f]{6}$" }),
|
|
141
|
+
normalizeInput: normalizeHexColor,
|
|
142
|
+
normalizeOutput: (value, { workspace = {}, settings = {} } = {}) =>
|
|
143
|
+
coerceWorkspaceSurfaceColor(value, {
|
|
144
|
+
color: resolveThemeBaseColor({ workspace, settings })
|
|
145
|
+
}),
|
|
146
|
+
resolveDefault: ({ workspace = {}, settings = {} } = {}) =>
|
|
147
|
+
coerceWorkspaceSurfaceColor(workspace.surfaceColor, {
|
|
148
|
+
color: resolveThemeBaseColor({ workspace, settings })
|
|
149
|
+
})
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
defineField({
|
|
153
|
+
key: "surfaceVariantColor",
|
|
154
|
+
dbColumn: "surface_variant_color",
|
|
155
|
+
required: true,
|
|
156
|
+
inputSchema: Type.String({
|
|
157
|
+
minLength: 7,
|
|
158
|
+
maxLength: 7,
|
|
159
|
+
pattern: "^#[0-9A-Fa-f]{6}$",
|
|
160
|
+
messages: {
|
|
161
|
+
required: "Surface variant color is required.",
|
|
162
|
+
pattern: "Surface variant color must be a hex color like #E2E8F1.",
|
|
163
|
+
default: "Surface variant color must be a hex color like #E2E8F1."
|
|
164
|
+
}
|
|
165
|
+
}),
|
|
166
|
+
outputSchema: Type.String({ minLength: 7, maxLength: 7, pattern: "^#[0-9A-Fa-f]{6}$" }),
|
|
167
|
+
normalizeInput: normalizeHexColor,
|
|
168
|
+
normalizeOutput: (value, { workspace = {}, settings = {} } = {}) =>
|
|
169
|
+
coerceWorkspaceSurfaceVariantColor(value, {
|
|
170
|
+
color: resolveThemeBaseColor({ workspace, settings })
|
|
171
|
+
}),
|
|
172
|
+
resolveDefault: ({ workspace = {}, settings = {} } = {}) =>
|
|
173
|
+
coerceWorkspaceSurfaceVariantColor(workspace.surfaceVariantColor, {
|
|
174
|
+
color: resolveThemeBaseColor({ workspace, settings })
|
|
175
|
+
})
|
|
176
|
+
});
|
|
177
|
+
|
|
91
178
|
defineField({
|
|
92
179
|
key: "invitesEnabled",
|
|
93
180
|
dbColumn: "invites_enabled",
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import "../test-support/registerDefaultSettingsFields.js";
|
|
4
|
+
import { resolveWorkspaceThemePalette } from "../src/shared/settings.js";
|
|
4
5
|
import { createRepository } from "../src/server/workspaceSettings/workspaceSettingsRepository.js";
|
|
5
6
|
|
|
6
7
|
function createDefaultWorkspaceSettings() {
|
|
7
8
|
return true;
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
const DEFAULT_WORKSPACE_THEME = resolveWorkspaceThemePalette({
|
|
12
|
+
color: "#2F5D9E"
|
|
13
|
+
});
|
|
14
|
+
|
|
10
15
|
function createKnexStub(rowOverrides = {}) {
|
|
11
16
|
const state = {
|
|
12
17
|
insertedRow: null,
|
|
@@ -15,7 +20,10 @@ function createKnexStub(rowOverrides = {}) {
|
|
|
15
20
|
workspace_id: 1,
|
|
16
21
|
name: "Workspace",
|
|
17
22
|
avatar_url: "",
|
|
18
|
-
color: "#
|
|
23
|
+
color: "#2F5D9E",
|
|
24
|
+
secondary_color: DEFAULT_WORKSPACE_THEME.secondaryColor,
|
|
25
|
+
surface_color: DEFAULT_WORKSPACE_THEME.surfaceColor,
|
|
26
|
+
surface_variant_color: DEFAULT_WORKSPACE_THEME.surfaceVariantColor,
|
|
19
27
|
invites_enabled: 1,
|
|
20
28
|
created_at: "2026-03-09 00:26:35.710",
|
|
21
29
|
updated_at: "2026-03-09 00:26:35.710",
|
|
@@ -34,6 +42,9 @@ function createKnexStub(rowOverrides = {}) {
|
|
|
34
42
|
name: payload.name,
|
|
35
43
|
avatar_url: payload.avatar_url,
|
|
36
44
|
color: payload.color,
|
|
45
|
+
secondary_color: payload.secondary_color,
|
|
46
|
+
surface_color: payload.surface_color,
|
|
47
|
+
surface_variant_color: payload.surface_variant_color,
|
|
37
48
|
invites_enabled: payload.invites_enabled,
|
|
38
49
|
created_at: "2026-03-10 00:00:00.000",
|
|
39
50
|
updated_at: "2026-03-10 00:00:00.000"
|
|
@@ -61,6 +72,15 @@ function createKnexStub(rowOverrides = {}) {
|
|
|
61
72
|
if (Object.hasOwn(payload, "color")) {
|
|
62
73
|
state.row.color = payload.color;
|
|
63
74
|
}
|
|
75
|
+
if (Object.hasOwn(payload, "secondary_color")) {
|
|
76
|
+
state.row.secondary_color = payload.secondary_color;
|
|
77
|
+
}
|
|
78
|
+
if (Object.hasOwn(payload, "surface_color")) {
|
|
79
|
+
state.row.surface_color = payload.surface_color;
|
|
80
|
+
}
|
|
81
|
+
if (Object.hasOwn(payload, "surface_variant_color")) {
|
|
82
|
+
state.row.surface_variant_color = payload.surface_variant_color;
|
|
83
|
+
}
|
|
64
84
|
if (Object.hasOwn(payload, "updated_at")) {
|
|
65
85
|
state.row.updated_at = payload.updated_at;
|
|
66
86
|
}
|
|
@@ -86,7 +106,10 @@ test("workspaceSettingsRepository.findByWorkspaceId maps the stored row", async
|
|
|
86
106
|
workspaceId: 1,
|
|
87
107
|
name: "Workspace",
|
|
88
108
|
avatarUrl: "",
|
|
89
|
-
color: "#
|
|
109
|
+
color: "#2F5D9E",
|
|
110
|
+
secondaryColor: DEFAULT_WORKSPACE_THEME.secondaryColor,
|
|
111
|
+
surfaceColor: DEFAULT_WORKSPACE_THEME.surfaceColor,
|
|
112
|
+
surfaceVariantColor: DEFAULT_WORKSPACE_THEME.surfaceVariantColor,
|
|
90
113
|
invitesEnabled: true,
|
|
91
114
|
createdAt: "2026-03-08T16:26:35.710Z",
|
|
92
115
|
updatedAt: "2026-03-08T16:26:35.710Z"
|
|
@@ -119,11 +142,17 @@ test("workspaceSettingsRepository.ensureForWorkspaceId inserts the injected defa
|
|
|
119
142
|
assert.equal(state.insertedRow.workspace_id, 5);
|
|
120
143
|
assert.equal(state.insertedRow.name, "Workspace");
|
|
121
144
|
assert.equal(state.insertedRow.avatar_url, "");
|
|
122
|
-
assert.equal(state.insertedRow.color, "#
|
|
145
|
+
assert.equal(state.insertedRow.color, "#2F5D9E");
|
|
146
|
+
assert.equal(state.insertedRow.secondary_color, DEFAULT_WORKSPACE_THEME.secondaryColor);
|
|
147
|
+
assert.equal(state.insertedRow.surface_color, DEFAULT_WORKSPACE_THEME.surfaceColor);
|
|
148
|
+
assert.equal(state.insertedRow.surface_variant_color, DEFAULT_WORKSPACE_THEME.surfaceVariantColor);
|
|
123
149
|
assert.equal(state.insertedRow.invites_enabled, false);
|
|
124
150
|
assert.equal(record.name, "Workspace");
|
|
125
151
|
assert.equal(record.avatarUrl, "");
|
|
126
|
-
assert.equal(record.color, "#
|
|
152
|
+
assert.equal(record.color, "#2F5D9E");
|
|
153
|
+
assert.equal(record.secondaryColor, DEFAULT_WORKSPACE_THEME.secondaryColor);
|
|
154
|
+
assert.equal(record.surfaceColor, DEFAULT_WORKSPACE_THEME.surfaceColor);
|
|
155
|
+
assert.equal(record.surfaceVariantColor, DEFAULT_WORKSPACE_THEME.surfaceVariantColor);
|
|
127
156
|
assert.equal(record.invitesEnabled, false);
|
|
128
157
|
});
|
|
129
158
|
|
|
@@ -2,6 +2,7 @@ import test from "node:test";
|
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { validateOperationSection } from "@jskit-ai/http-runtime/shared/validators/operationValidation";
|
|
4
4
|
import "../test-support/registerDefaultSettingsFields.js";
|
|
5
|
+
import { resolveWorkspaceThemePalette } from "../src/shared/settings.js";
|
|
5
6
|
import { workspaceSettingsResource } from "../src/shared/resources/workspaceSettingsResource.js";
|
|
6
7
|
import { createWorkspaceRoleCatalog } from "../src/shared/roles.js";
|
|
7
8
|
|
|
@@ -48,6 +49,9 @@ test("workspace settings patch body normalizes valid payload before validation",
|
|
|
48
49
|
name: " Team Mercury ",
|
|
49
50
|
avatarUrl: "https://example.com/avatar.png",
|
|
50
51
|
color: "#0f6b54",
|
|
52
|
+
secondaryColor: "#0b4d3c",
|
|
53
|
+
surfaceColor: "#eef5f3",
|
|
54
|
+
surfaceVariantColor: "#ddeae7",
|
|
51
55
|
invitesEnabled: false
|
|
52
56
|
});
|
|
53
57
|
|
|
@@ -57,6 +61,9 @@ test("workspace settings patch body normalizes valid payload before validation",
|
|
|
57
61
|
name: "Team Mercury",
|
|
58
62
|
avatarUrl: "https://example.com/avatar.png",
|
|
59
63
|
color: "#0F6B54",
|
|
64
|
+
secondaryColor: "#0B4D3C",
|
|
65
|
+
surfaceColor: "#EEF5F3",
|
|
66
|
+
surfaceVariantColor: "#DDEAE7",
|
|
60
67
|
invitesEnabled: false
|
|
61
68
|
});
|
|
62
69
|
});
|
|
@@ -89,10 +96,16 @@ test("workspace settings create body requires full-write fields", () => {
|
|
|
89
96
|
|
|
90
97
|
assert.equal(parsed.ok, false);
|
|
91
98
|
assert.equal(parsed.fieldErrors.color, "Workspace color is required.");
|
|
99
|
+
assert.equal(parsed.fieldErrors.secondaryColor, "Secondary color is required.");
|
|
100
|
+
assert.equal(parsed.fieldErrors.surfaceColor, "Surface color is required.");
|
|
101
|
+
assert.equal(parsed.fieldErrors.surfaceVariantColor, "Surface variant color is required.");
|
|
92
102
|
assert.equal(parsed.fieldErrors.invitesEnabled, "invitesEnabled is required.");
|
|
93
103
|
});
|
|
94
104
|
|
|
95
105
|
test("workspace settings output normalizes raw service payloads", () => {
|
|
106
|
+
const expectedTheme = resolveWorkspaceThemePalette({
|
|
107
|
+
color: "#0F6B54"
|
|
108
|
+
});
|
|
96
109
|
const normalized = workspaceSettingsResource.operations.view.outputValidator.normalize({
|
|
97
110
|
workspace: {
|
|
98
111
|
id: "7",
|
|
@@ -118,6 +131,9 @@ test("workspace settings output normalizes raw service payloads", () => {
|
|
|
118
131
|
name: "Mercury Workspace",
|
|
119
132
|
avatarUrl: "https://example.com/avatar.png",
|
|
120
133
|
color: "#0F6B54",
|
|
134
|
+
secondaryColor: expectedTheme.secondaryColor,
|
|
135
|
+
surfaceColor: expectedTheme.surfaceColor,
|
|
136
|
+
surfaceVariantColor: expectedTheme.surfaceVariantColor,
|
|
121
137
|
invitesEnabled: false,
|
|
122
138
|
invitesAvailable: true,
|
|
123
139
|
invitesEffective: false
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import "../test-support/registerDefaultSettingsFields.js";
|
|
4
|
+
import { resolveWorkspaceThemePalette } from "../src/shared/settings.js";
|
|
4
5
|
import { createService } from "../src/server/workspaceSettings/workspaceSettingsService.js";
|
|
5
6
|
|
|
6
7
|
function authorizedOptions(permissions = []) {
|
|
@@ -15,6 +16,9 @@ function authorizedOptions(permissions = []) {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
function createFixture({ workspaceInvitationsEnabled = true } = {}) {
|
|
19
|
+
const defaultTheme = resolveWorkspaceThemePalette({
|
|
20
|
+
color: "#0F6B54"
|
|
21
|
+
});
|
|
18
22
|
const state = {
|
|
19
23
|
settingsPatch: null,
|
|
20
24
|
workspace: {
|
|
@@ -23,12 +27,15 @@ function createFixture({ workspaceInvitationsEnabled = true } = {}) {
|
|
|
23
27
|
name: "TonyMobily3",
|
|
24
28
|
ownerUserId: 9,
|
|
25
29
|
avatarUrl: "",
|
|
26
|
-
color:
|
|
30
|
+
color: defaultTheme.color
|
|
27
31
|
},
|
|
28
32
|
settings: {
|
|
29
33
|
name: "TonyMobily3",
|
|
30
34
|
avatarUrl: "",
|
|
31
|
-
color:
|
|
35
|
+
color: defaultTheme.color,
|
|
36
|
+
secondaryColor: defaultTheme.secondaryColor,
|
|
37
|
+
surfaceColor: defaultTheme.surfaceColor,
|
|
38
|
+
surfaceVariantColor: defaultTheme.surfaceVariantColor,
|
|
32
39
|
invitesEnabled: true
|
|
33
40
|
}
|
|
34
41
|
};
|
|
@@ -67,6 +74,9 @@ test("workspaceSettingsService.getWorkspaceSettings returns the stored invitesEn
|
|
|
67
74
|
name: "TonyMobily3",
|
|
68
75
|
avatarUrl: "",
|
|
69
76
|
color: "#0F6B54",
|
|
77
|
+
secondaryColor: "#0B4D3C",
|
|
78
|
+
surfaceColor: "#EEF5F3",
|
|
79
|
+
surfaceVariantColor: "#DDEAE7",
|
|
70
80
|
invitesEnabled: true,
|
|
71
81
|
invitesAvailable: true,
|
|
72
82
|
invitesEffective: true
|
|
@@ -93,6 +103,9 @@ test("workspaceSettingsService.updateWorkspaceSettings writes editable fields th
|
|
|
93
103
|
name: "New Name",
|
|
94
104
|
avatarUrl: "",
|
|
95
105
|
color: "#0F6B54",
|
|
106
|
+
secondaryColor: "#0B4D3C",
|
|
107
|
+
surfaceColor: "#EEF5F3",
|
|
108
|
+
surfaceVariantColor: "#DDEAE7",
|
|
96
109
|
invitesEnabled: false,
|
|
97
110
|
invitesAvailable: true,
|
|
98
111
|
invitesEffective: false
|
|
@@ -113,6 +126,9 @@ test("workspaceSettingsService disables invite settings in output when app polic
|
|
|
113
126
|
name: "TonyMobily3",
|
|
114
127
|
avatarUrl: "",
|
|
115
128
|
color: "#0F6B54",
|
|
129
|
+
secondaryColor: "#0B4D3C",
|
|
130
|
+
surfaceColor: "#EEF5F3",
|
|
131
|
+
surfaceVariantColor: "#DDEAE7",
|
|
116
132
|
invitesEnabled: false,
|
|
117
133
|
invitesAvailable: false,
|
|
118
134
|
invitesEffective: false
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { normalizeObjectInput } from "@jskit-ai/kernel/shared/validators";
|
|
2
|
-
|
|
3
|
-
function normalizeSettingsFieldInput(payload = {}, fields = []) {
|
|
4
|
-
const source = normalizeObjectInput(payload);
|
|
5
|
-
const normalized = {};
|
|
6
|
-
|
|
7
|
-
for (const field of fields) {
|
|
8
|
-
if (!Object.hasOwn(source, field.key)) {
|
|
9
|
-
continue;
|
|
10
|
-
}
|
|
11
|
-
normalized[field.key] = field.normalizeInput(source[field.key], {
|
|
12
|
-
payload: source
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return normalized;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export { normalizeSettingsFieldInput };
|