@opencxh/domain 1.171.0 → 1.172.2
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/dist/entities/activity/blocks.d.ts +30 -30
- package/dist/entities/activity/catalog.d.ts +63 -86
- package/dist/entities/activity/descriptor.d.ts +31 -32
- package/dist/entities/activity/index.d.ts +2 -2
- package/dist/entities/activity/preview.d.ts +7 -7
- package/dist/entities/activity/resolve.d.ts +37 -38
- package/dist/entities/activity/types.d.ts +34 -34
- package/dist/entities/ai-account/types.d.ts +3 -3
- package/dist/entities/ai-account/vendors.d.ts +25 -32
- package/dist/entities/ai-budget/types.d.ts +12 -8
- package/dist/entities/ai-conversation/types.d.ts +12 -12
- package/dist/entities/ai-profile/types.d.ts +21 -26
- package/dist/entities/ai-settings/types.d.ts +12 -13
- package/dist/entities/analytics/dashboard.d.ts +1 -1
- package/dist/entities/analytics/index.d.ts +4 -4
- package/dist/entities/analytics/source.d.ts +1 -1
- package/dist/entities/analytics/usage.d.ts +23 -28
- package/dist/entities/artifact/blocks.d.ts +54 -69
- package/dist/entities/artifact/markdown.d.ts +2 -3
- package/dist/entities/artifact/types.d.ts +47 -55
- package/dist/entities/assignment/types.d.ts +74 -88
- package/dist/entities/calendar-event/types.d.ts +1 -1
- package/dist/entities/channel/index.d.ts +1 -1
- package/dist/entities/channel/signature.d.ts +8 -9
- package/dist/entities/communication/index.d.ts +1 -1
- package/dist/entities/communication/message-template.d.ts +1 -1
- package/dist/entities/company/types.d.ts +1 -1
- package/dist/entities/contact/index.d.ts +1 -1
- package/dist/entities/contact/provider.d.ts +2 -1
- package/dist/entities/contact/types.d.ts +1 -10
- package/dist/entities/custom-field-def/types.d.ts +14 -14
- package/dist/entities/external-identity/types.d.ts +45 -50
- package/dist/entities/interaction/index.d.ts +1 -1
- package/dist/entities/interaction/types.d.ts +44 -47
- package/dist/entities/live-lens/types.d.ts +39 -44
- package/dist/entities/mcp/types.d.ts +53 -57
- package/dist/entities/memory/alias.d.ts +7 -7
- package/dist/entities/memory/browse.d.ts +21 -21
- package/dist/entities/memory/ingest.d.ts +26 -17
- package/dist/entities/memory/item.d.ts +53 -58
- package/dist/entities/memory/kind.d.ts +26 -26
- package/dist/entities/memory/query.d.ts +49 -51
- package/dist/entities/playbook/actor.d.ts +20 -19
- package/dist/entities/playbook/assignment.d.ts +54 -63
- package/dist/entities/playbook/index.d.ts +2 -2
- package/dist/entities/playbook/labels.d.ts +4 -4
- package/dist/entities/playbook/trigger-vars.d.ts +14 -15
- package/dist/entities/playbook/types.d.ts +173 -303
- package/dist/entities/resource-reminder/types.d.ts +8 -9
- package/dist/entities/scope/actor.d.ts +16 -0
- package/dist/entities/{note → scope}/index.d.ts +1 -0
- package/dist/entities/scope/scope.test.d.ts +1 -0
- package/dist/entities/scope/types.d.ts +53 -0
- package/dist/entities/task/types.d.ts +1 -1
- package/dist/entities/time-entry/duration.d.ts +11 -11
- package/dist/entities/time-entry/types.d.ts +60 -60
- package/dist/entities/time-entry/work-type.d.ts +21 -24
- package/dist/entities/topic/examples.d.ts +26 -27
- package/dist/entities/topic/scope.d.ts +11 -12
- package/dist/entities/topic/types.d.ts +28 -28
- package/dist/entities/user/types.d.ts +24 -28
- package/dist/entities/webhook/types.d.ts +6 -6
- package/dist/entities/work/keys.d.ts +25 -26
- package/dist/entities/work/ladder.d.ts +48 -52
- package/dist/entities/work/types.d.ts +109 -115
- package/dist/index.cjs +6 -6
- package/dist/index.d.ts +29 -33
- package/dist/index.js +1391 -1254
- package/dist/platform/account.d.ts +34 -38
- package/dist/platform/ai-tools.d.ts +48 -52
- package/dist/platform/api.d.ts +9 -8
- package/dist/platform/author.d.ts +7 -9
- package/dist/platform/capabilities.d.ts +3 -3
- package/dist/platform/communication.d.ts +163 -204
- package/dist/platform/context.d.ts +8 -8
- package/dist/platform/identity.d.ts +16 -19
- package/dist/platform/kernel.d.ts +1 -1
- package/dist/platform/manifest.d.ts +2 -2
- package/dist/platform/media.d.ts +13 -15
- package/dist/platform/permission.d.ts +39 -0
- package/dist/platform/permission.test.d.ts +1 -0
- package/dist/platform/presence.d.ts +30 -30
- package/dist/platform/provider.d.ts +14 -76
- package/dist/platform/resource-source.d.ts +43 -45
- package/dist/platform/resource.d.ts +17 -23
- package/dist/platform/scope.d.ts +36 -62
- package/dist/platform/services.d.ts +4 -4
- package/dist/platform/settings.d.ts +0 -14
- package/dist/platform/storage.d.ts +2 -2
- package/dist/platform/sync-source.d.ts +196 -216
- package/dist/platform/transcript-cadence.d.ts +37 -41
- package/dist/platform/ui.d.ts +6 -6
- package/dist/text/endpoint.d.ts +11 -27
- package/package.json +3 -5
- package/dist/entities/note/types.d.ts +0 -14
- package/dist/entities/shopify/index.d.ts +0 -1
- package/dist/entities/shopify/types.d.ts +0 -79
- package/dist/platform/lifecycle.d.ts +0 -13
- package/dist/platform/reactive.d.ts +0 -13
- package/dist/platform/sdk.d.ts +0 -182
- package/dist/platform/service-registry.d.ts +0 -13
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* gebruikt.
|
|
2
|
+
* A personal "come back to this later" marker on a resource, per (resourceType, resourceId,
|
|
3
|
+
* userId) - the same ResourceRef shape read-state uses.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Unlike a snooze this does not touch the resource itself: the team keeps seeing the conversation
|
|
6
|
+
* in its list, only your attention stack skips it.
|
|
8
7
|
*/
|
|
9
8
|
export interface ResourceReminder {
|
|
10
9
|
id: string;
|
|
@@ -12,12 +11,12 @@ export interface ResourceReminder {
|
|
|
12
11
|
resourceId: string;
|
|
13
12
|
userId: string;
|
|
14
13
|
organizationId: string;
|
|
15
|
-
/** Epoch
|
|
14
|
+
/** Epoch ms at which the item may come back into view. */
|
|
16
15
|
remindAt: number;
|
|
17
16
|
/**
|
|
18
|
-
* Epoch
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* Epoch ms at which it was parked. Decides whether later activity lifts the reminder - an
|
|
18
|
+
* explicit field and not the framework `updatedAt`, so the expiry rule stays deterministic and
|
|
19
|
+
* testable.
|
|
21
20
|
*/
|
|
22
21
|
parkedAt: number;
|
|
23
22
|
createdAt?: number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ActingIdentity } from '../../platform/identity';
|
|
2
|
+
import { OwnerScope } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* An owner scope as the identity that acts for it.
|
|
5
|
+
*
|
|
6
|
+
* Storage calls the personal rung `personal`, execution calls it `user`, and `org`
|
|
7
|
+
* carries an id on one side and not on the other. This is that translation, and it is
|
|
8
|
+
* meant to be the only one: a hand-rolled `kind === "personal" ? "user" : …` at a call
|
|
9
|
+
* site is how two vocabularies for the same three rungs drift apart.
|
|
10
|
+
*
|
|
11
|
+
* It arrived as `playbookActor` in `entities/playbook/`, whose own docstring already
|
|
12
|
+
* claimed to be "the only place this belongs" — correct, but it read as
|
|
13
|
+
* playbook-specific while every scoped entity needs the same conversion. `playbookActor`
|
|
14
|
+
* stays as a name so the playbook call sites and their tests keep working.
|
|
15
|
+
*/
|
|
16
|
+
export declare function toActingIdentity(scope: OwnerScope): ActingIdentity;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One vocabulary for "whose is this".
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is its own module
|
|
5
|
+
*
|
|
6
|
+
* `OwnerScope` lived in `entities/contact/types.ts`, and nineteen other entities —
|
|
7
|
+
* work, task, topic, playbook, artifact, calendar-event, time-entry, memory, … —
|
|
8
|
+
* imported the platform's scoping primitive *from the contact entity*. Nothing was
|
|
9
|
+
* broken by that; it just meant the answer to "which type do I use to say who owns
|
|
10
|
+
* this" was somewhere nobody would look, and a new entity picked whatever it found
|
|
11
|
+
* first. The audit counted six different spellings in use.
|
|
12
|
+
*
|
|
13
|
+
* ## The field-name convention
|
|
14
|
+
*
|
|
15
|
+
* **The field is always called `ownerScope`.** `scope` is reserved for OAuth scopes,
|
|
16
|
+
* which are an unrelated concept that happens to share the word — `AiBudget.scope` is
|
|
17
|
+
* the one place that still deviates.
|
|
18
|
+
*
|
|
19
|
+
* ## The three rungs, and the fourth spelling
|
|
20
|
+
*
|
|
21
|
+
* Storage says `personal` / `team` / `org`. The execution side —
|
|
22
|
+
* {@link ActingIdentity} in `platform/identity.ts` — says `user` / `team` / `org`, and
|
|
23
|
+
* its `org` carries no id because the tenant is already in context there. Those are
|
|
24
|
+
* genuinely two vocabularies for two layers, not an accident, and the bridge between
|
|
25
|
+
* them is `toActingIdentity` next door. That function is the **only** place the
|
|
26
|
+
* translation belongs; a hand-rolled `kind === "personal" ? "user" : …` at a call site
|
|
27
|
+
* is how the two drift apart.
|
|
28
|
+
*/
|
|
29
|
+
export type OwnerScope = {
|
|
30
|
+
kind: "personal";
|
|
31
|
+
userId: string;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "team";
|
|
34
|
+
teamId: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "org";
|
|
37
|
+
organizationId: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* A scope that cannot be personal.
|
|
41
|
+
*
|
|
42
|
+
* For records that are shared by definition: a topic one person can see cannot be read
|
|
43
|
+
* by the colleague who takes over the conversation, and the same goes for an AI profile
|
|
44
|
+
* or a work type. Those rules exist today as prose in the entity's docstring, with the
|
|
45
|
+
* type still permitting `personal` — which means nothing enforces them.
|
|
46
|
+
*
|
|
47
|
+
* Applying this to an existing entity is a type *tightening*, so it can surface rows
|
|
48
|
+
* that already violate the rule. Query for personal-scoped rows first; that is why it
|
|
49
|
+
* is offered here rather than applied everywhere at once.
|
|
50
|
+
*/
|
|
51
|
+
export type OrgTeamScope = Exclude<OwnerScope, {
|
|
52
|
+
kind: "personal";
|
|
53
|
+
}>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OwnerScope } from '../
|
|
1
|
+
import { OwnerScope } from '../scope/types';
|
|
2
2
|
export type TaskStatus = "open" | "in_progress" | "done" | "cancelled";
|
|
3
3
|
export type TaskPriority = "low" | "normal" | "high" | "urgent";
|
|
4
4
|
export type TaskInitiator = "user" | "system";
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { TimeEntry, TimeRounding } from './types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* The elapsed time of an entry, in seconds.
|
|
4
4
|
*
|
|
5
|
-
* In `domain`
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* In `domain` and not in the app server, because client and server have to give the same answer:
|
|
6
|
+
* the server stamps the duration on stop, the panel counts it up every second in the meantime. If
|
|
7
|
+
* the panel counted from a zero point of its own, the visible clock would drift from what is
|
|
8
|
+
* eventually booked as soon as a tab sleeps.
|
|
9
9
|
*/
|
|
10
10
|
export declare function elapsedSeconds(entry: Pick<TimeEntry, "accumulatedSeconds" | "runningSince">, now: number): number;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* The booked duration belonging to a measured one.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Rounded up and with a minimum of one unit: whoever picks "per 15 minutes" wants a three-minute
|
|
15
|
+
* job to become a quarter of an hour and not zero. With `exact` the floor is one minute, for the
|
|
16
|
+
* same reason — an entry of zero seconds is not an entry.
|
|
17
17
|
*/
|
|
18
18
|
export declare function applyRounding(rawSeconds: number, rounding: TimeRounding | undefined): number;
|
|
19
|
-
/** `0:45` / `2:05` —
|
|
19
|
+
/** `0:45` / `2:05` — hours and minutes, like the day totals in the panel. */
|
|
20
20
|
export declare function formatHm(totalSeconds: number): string;
|
|
21
|
-
/** `12:34`
|
|
21
|
+
/** `12:34` under the hour, `1:02:03` above it — the running clock. */
|
|
22
22
|
export declare function formatClock(totalSeconds: number): string;
|
|
@@ -1,70 +1,71 @@
|
|
|
1
|
-
import { OwnerScope } from '../
|
|
1
|
+
import { OwnerScope } from '../scope/types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Where a time entry stands in its life.
|
|
4
4
|
*
|
|
5
|
-
* `review` is
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* `review` is deliberately a **server** state and not something living only in the panel: the
|
|
6
|
+
* design lets you round off, pick a work type and update a note after stopping, before the entry
|
|
7
|
+
* counts. If that intermediate step lived only in the client, a closed tab would be exactly the
|
|
8
|
+
* moment the worked hour evaporates. Now the panel simply finds the entry again on the next visit,
|
|
9
|
+
* review card and all.
|
|
10
10
|
*
|
|
11
|
-
* `logged` is
|
|
11
|
+
* `logged` is the only state counting towards totals and reporting.
|
|
12
12
|
*/
|
|
13
13
|
export type TimeEntryStatus = "running" | "paused" | "review" | "logged";
|
|
14
|
-
/**
|
|
14
|
+
/** How measured time is translated into a bookable duration. */
|
|
15
15
|
export type TimeRounding = "exact" | "15" | "30";
|
|
16
|
-
/**
|
|
16
|
+
/** Entered by hand or actually clocked — visible in the reporting. */
|
|
17
17
|
export type TimeEntrySource = "timer" | "manual";
|
|
18
18
|
export interface TimeEntry {
|
|
19
19
|
id: string;
|
|
20
20
|
organizationId: string;
|
|
21
|
-
/**
|
|
21
|
+
/** Who wrote the time. An entry always belongs to exactly one person. */
|
|
22
22
|
userId: string;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* What the time is booked on: `"interaction:abc"`, `"task:42"`, `"company:xyz"`. Absent =
|
|
25
|
+
* general work without an object.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Deliberately an opaque key and not an `interactionId`: the app owning the kind answers the
|
|
28
|
+
* access question (`assertScopeAccess`), so time tracking needs to know no other app at all to
|
|
29
|
+
* be able to write time against it.
|
|
30
30
|
*/
|
|
31
31
|
scopeKey?: string;
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
32
|
+
/** Human label at write time ("Gesprek · RE: EYLO"), so a list stays readable without
|
|
33
|
+
* querying the source app per row. */
|
|
34
34
|
scopeLabel?: string;
|
|
35
|
-
/**
|
|
35
|
+
/** File keys of the resource, flattened at write time — see the `keys` convention. */
|
|
36
36
|
keys?: string[];
|
|
37
|
-
/**
|
|
37
|
+
/** Ownership as the source app sees it; decides who sees the entry in a team view. */
|
|
38
38
|
ownerScope?: OwnerScope;
|
|
39
39
|
status: TimeEntryStatus;
|
|
40
|
-
/** Epoch ms,
|
|
40
|
+
/** Epoch ms, set by the server. The client only works out what time it is now. */
|
|
41
41
|
startedAt: number;
|
|
42
|
-
/** Epoch ms;
|
|
42
|
+
/** Epoch ms; set as soon as the timer stops. Absent while it runs or is paused. */
|
|
43
43
|
endedAt?: number;
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
45
|
+
* Seconds already banked before the current segment.
|
|
46
46
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
47
|
+
* Pausing must not produce a second row — the design promises one entry per job, with a pause
|
|
48
|
+
* button in it. So on every pause the server adds the elapsed segment here and releases
|
|
49
|
+
* `runningSince`; resuming only sets `runningSince` again. Elapsed time is therefore always
|
|
50
|
+
* `accumulatedSeconds + (runningSince ? now - runningSince : 0)`, and that answer is the same on
|
|
51
|
+
* every device.
|
|
52
52
|
*/
|
|
53
53
|
accumulatedSeconds: number;
|
|
54
|
-
/** Epoch ms
|
|
54
|
+
/** Epoch ms the running segment started at. Absent = paused or stopped. */
|
|
55
55
|
runningSince?: number;
|
|
56
|
-
/**
|
|
56
|
+
/** What the clock actually measured. Stays when rounding pulls the booked duration up. */
|
|
57
57
|
rawSeconds?: number;
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
59
|
+
* The booked duration in seconds — *this* is what counts in totals and invoicing.
|
|
60
60
|
*
|
|
61
|
-
*
|
|
62
|
-
* later
|
|
61
|
+
* Separate from `rawSeconds` because rounding is a user's choice and not a measurement: whoever
|
|
62
|
+
* asks later "where does that quarter of an hour come from" must still be able to see the two
|
|
63
|
+
* minutes that were measured.
|
|
63
64
|
*/
|
|
64
65
|
durationSeconds?: number;
|
|
65
66
|
rounding?: TimeRounding;
|
|
66
67
|
billable: boolean;
|
|
67
|
-
/**
|
|
68
|
+
/** Refers to `WorkType.key`. */
|
|
68
69
|
workType?: string;
|
|
69
70
|
note?: string;
|
|
70
71
|
source: TimeEntrySource;
|
|
@@ -72,35 +73,34 @@ export interface TimeEntry {
|
|
|
72
73
|
updatedAt?: number;
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
76
|
+
* A manageable work type instead of a list in code: which types an organisation knows differs per
|
|
77
|
+
* company, and the type decides whether something is billable by default.
|
|
77
78
|
*/
|
|
78
79
|
export interface WorkType {
|
|
79
80
|
id: string;
|
|
80
81
|
organizationId: string;
|
|
81
82
|
/**
|
|
82
|
-
*
|
|
83
|
+
* Stable key that lands on the time entry.
|
|
83
84
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* twee betekenissen erin.
|
|
85
|
+
* Unique within the organisation and not within the scope, even though a type can belong to a
|
|
86
|
+
* team: `TimeEntry.workType` stores only this key and the reporting groups on it. Two teams each
|
|
87
|
+
* with their own `installatie` would come out as one row there, carrying two meanings.
|
|
88
88
|
*/
|
|
89
89
|
key: string;
|
|
90
90
|
label: string;
|
|
91
|
-
/**
|
|
91
|
+
/** Default for `TimeEntry.billable`; overridable per entry. */
|
|
92
92
|
defaultBillable: boolean;
|
|
93
|
-
/**
|
|
93
|
+
/** Included as a dimension in the reporting. */
|
|
94
94
|
reportable?: boolean;
|
|
95
95
|
order?: number;
|
|
96
96
|
archived?: boolean;
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
98
|
+
* Whose type this is: the whole organisation or one team. Decides who sees it in their time
|
|
99
|
+
* panel and who may manage it.
|
|
100
100
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
101
|
+
* No `personal`, for the same reason as with `Topic`: a type only one person knows makes the
|
|
102
|
+
* booked hour unreadable for the colleague taking over the week. Absent counts as org-wide —
|
|
103
|
+
* which is what every type effectively was before this field.
|
|
104
104
|
*/
|
|
105
105
|
ownerScope?: Extract<OwnerScope, {
|
|
106
106
|
kind: "org";
|
|
@@ -111,10 +111,10 @@ export interface WorkType {
|
|
|
111
111
|
updatedAt?: number;
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
-
*
|
|
114
|
+
* What the admin screen sends to the server.
|
|
115
115
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
116
|
+
* Without an `id` it is a new type. `key` is deliberately absent: the server derives it from the
|
|
117
|
+
* name and it is fixed afterwards, because booked hours refer to it.
|
|
118
118
|
*/
|
|
119
119
|
export interface SaveWorkTypeRequest {
|
|
120
120
|
id?: string;
|
|
@@ -125,13 +125,13 @@ export interface SaveWorkTypeRequest {
|
|
|
125
125
|
archived?: boolean;
|
|
126
126
|
ownerScope?: WorkType["ownerScope"];
|
|
127
127
|
}
|
|
128
|
-
/**
|
|
128
|
+
/** A candidate the user can clock on, offered in the panel's empty state. */
|
|
129
129
|
export interface TimeTarget {
|
|
130
130
|
scopeKey?: string;
|
|
131
|
-
/**
|
|
131
|
+
/** Kind label above the title ("GESPREK OP JE SCHERM", "TAAK"). */
|
|
132
132
|
kindLabel: string;
|
|
133
133
|
title: string;
|
|
134
|
-
/** Lucide
|
|
134
|
+
/** Lucide icon name. */
|
|
135
135
|
icon: string;
|
|
136
136
|
keys?: string[];
|
|
137
137
|
}
|
|
@@ -142,14 +142,14 @@ export interface StartTimerRequest {
|
|
|
142
142
|
workType?: string;
|
|
143
143
|
note?: string;
|
|
144
144
|
}
|
|
145
|
-
/**
|
|
145
|
+
/** What can still be adjusted when saving an entry. */
|
|
146
146
|
export interface SaveTimeEntryRequest {
|
|
147
147
|
id: string;
|
|
148
148
|
rounding?: TimeRounding;
|
|
149
149
|
workType?: string;
|
|
150
150
|
billable?: boolean;
|
|
151
151
|
note?: string;
|
|
152
|
-
/**
|
|
152
|
+
/** Book against a different object than the timer started on. */
|
|
153
153
|
scopeKey?: string;
|
|
154
154
|
scopeLabel?: string;
|
|
155
155
|
keys?: string[];
|
|
@@ -158,18 +158,18 @@ export interface ManualTimeEntryRequest {
|
|
|
158
158
|
scopeKey?: string;
|
|
159
159
|
scopeLabel?: string;
|
|
160
160
|
keys?: string[];
|
|
161
|
-
/** Epoch ms;
|
|
161
|
+
/** Epoch ms; the day the entry falls on. */
|
|
162
162
|
startedAt: number;
|
|
163
163
|
durationSeconds: number;
|
|
164
164
|
workType?: string;
|
|
165
165
|
billable?: boolean;
|
|
166
166
|
note?: string;
|
|
167
167
|
}
|
|
168
|
-
/**
|
|
168
|
+
/** What the panel fetches in one go: the running timer plus the day around it. */
|
|
169
169
|
export interface TimeDaySummary {
|
|
170
|
-
/**
|
|
170
|
+
/** The entry running, paused or awaiting review right now — there is at most one. */
|
|
171
171
|
active?: TimeEntry;
|
|
172
|
-
/**
|
|
172
|
+
/** Finished entries of the requested day, ascending on start time. */
|
|
173
173
|
entries: TimeEntry[];
|
|
174
174
|
totalSeconds: number;
|
|
175
175
|
billableSeconds: number;
|
|
@@ -1,44 +1,41 @@
|
|
|
1
1
|
import { WorkType } from './types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* The key belonging to a name: lowercase, hyphens, no accents.
|
|
4
4
|
*
|
|
5
|
-
* In `domain`
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* rapportage.
|
|
5
|
+
* In `domain` and not in the app server because the admin screen shows it while you type — what
|
|
6
|
+
* the user sees there has to be exactly what the server writes later, or the form promises
|
|
7
|
+
* `installatie-op-locatie` and the reporting says `installatieoplocatie`.
|
|
9
8
|
*
|
|
10
|
-
* `normalize("NFD")` plus
|
|
11
|
-
*
|
|
9
|
+
* `normalize("NFD")` plus stripping the combining marks, so "Béta" becomes `beta` and not `b-ta`:
|
|
10
|
+
* a readable key is half the point.
|
|
12
11
|
*/
|
|
13
12
|
export declare function workTypeKey(label: string): string;
|
|
14
13
|
/**
|
|
15
|
-
*
|
|
14
|
+
* The same key, but guaranteed still free within the organisation.
|
|
16
15
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* een sleutel die hij nooit heeft gezien.
|
|
16
|
+
* Collisions are real: the key is org-unique while types are managed per team, so two teams both
|
|
17
|
+
* creating "Installatie" end up here. The second gets `installatie-2` instead of an error — an
|
|
18
|
+
* admin picking a name should not trip over a key they never saw.
|
|
21
19
|
*
|
|
22
|
-
* `taken`
|
|
23
|
-
*
|
|
20
|
+
* `taken` also holds archived keys: those still sit on booked hours, so reusing one would silently
|
|
21
|
+
* shift old work under a new type.
|
|
24
22
|
*/
|
|
25
23
|
export declare function uniqueWorkTypeKey(label: string, taken: Iterable<string>): string;
|
|
26
24
|
/**
|
|
27
|
-
*
|
|
25
|
+
* Does this user see this type?
|
|
28
26
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
27
|
+
* A type without a scope is org-wide: rows are older than the field, and filtering them out would
|
|
28
|
+
* cost an existing organisation its whole list.
|
|
31
29
|
*/
|
|
32
30
|
export declare function isWorkTypeVisible(workType: Pick<WorkType, "ownerScope">, teamIds: string[]): boolean;
|
|
33
31
|
/**
|
|
34
|
-
*
|
|
32
|
+
* What the panel shows: not archived, in the right order.
|
|
35
33
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* heeft goedgekeurd.
|
|
34
|
+
* Without a team argument, deliberately: the server already cut team visibility off before the
|
|
35
|
+
* list reaches the client (that is a permission question). If the client filtered on `teamIds`
|
|
36
|
+
* again here it would have to know the user's memberships — and with an empty list, as it has one
|
|
37
|
+
* now, it would filter out precisely the team types the server just approved.
|
|
41
38
|
*/
|
|
42
39
|
export declare function activeWorkTypes(workTypes: WorkType[]): WorkType[];
|
|
43
|
-
/**
|
|
40
|
+
/** The order of the chips: `order` first, ties alphabetical so it stays stable. */
|
|
44
41
|
export declare function compareWorkTypes(a: WorkType, b: WorkType): number;
|
|
@@ -1,46 +1,45 @@
|
|
|
1
1
|
import { Topic, TopicExampleCandidate } from './types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* The shape in which two examples are "the same".
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* For comparing only, never for storing: what the admin sees is the original text. Ten copies of
|
|
6
|
+
* "Ik wil mijn factuur" teach the classifier nothing while crowding out an example that would, so
|
|
7
|
+
* capitals, punctuation and double spaces must not hide that.
|
|
8
8
|
*/
|
|
9
9
|
export declare function normalizeExample(text: string): string;
|
|
10
|
-
/**
|
|
10
|
+
/** Truncate on a whole sentence where possible, otherwise hard at the limit. */
|
|
11
11
|
export declare function truncateExample(text: string): string;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Add a suggestion. Returns the new candidate list, or `null` when there was nothing to change —
|
|
14
|
+
* the caller then stores no write.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* 1.
|
|
18
|
-
* 2.
|
|
19
|
-
*
|
|
20
|
-
* 3.
|
|
21
|
-
*
|
|
16
|
+
* Three reasons to return `null`, all three ordinary:
|
|
17
|
+
* 1. Empty text. A conversation without an inbound message yields no example.
|
|
18
|
+
* 2. It is already in `examples`. The admin already judged this; suggesting it again is noise, and
|
|
19
|
+
* after one rejection it would come back on every next assignment.
|
|
20
|
+
* 3. It is already among the candidates. It *is* **updated** when the new report is a correction:
|
|
21
|
+
* that is the stronger signal and belongs at the top.
|
|
22
22
|
*/
|
|
23
23
|
export declare function addExampleCandidate(topic: Pick<Topic, "examples" | "exampleCandidates">, candidate: TopicExampleCandidate): TopicExampleCandidate[] | null;
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* Accept a suggestion: from the candidate list into `examples`.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Runs in the **editor**, not on the server — accepting is an edit of the topic and travels with
|
|
28
|
+
* the normal save. That saves an endpoint and keeps "what the admin sees" and "what gets stored"
|
|
29
|
+
* one thing.
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* in de UI vermeld.
|
|
31
|
+
* On a full `examples` the **oldest** falls out (first in the list). Not unavoidably right — the
|
|
32
|
+
* first examples are often typed with care — but the alternative is putting a choice to the admin
|
|
33
|
+
* while they are doing something else. The cap is stated in the UI.
|
|
35
34
|
*/
|
|
36
35
|
export declare function acceptExampleCandidate(topic: Pick<Topic, "examples" | "exampleCandidates">, text: string): Pick<Topic, "examples" | "exampleCandidates">;
|
|
37
36
|
/**
|
|
38
|
-
*
|
|
37
|
+
* Throw a suggestion away.
|
|
39
38
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
39
|
+
* Rejecting is **not** remembered. If the exact same sentence shows up again on a manual
|
|
40
|
+
* assignment, it is back. Deliberately no third list of rejected texts: that is twice the state
|
|
41
|
+
* for a case that only occurs with a generic opening line ("Hallo, ik heb een vraag") — and then
|
|
42
|
+
* coming back is not strange either. If it still bothers you, the real fix is to accept it, after
|
|
43
|
+
* which it is never suggested again.
|
|
45
44
|
*/
|
|
46
45
|
export declare function rejectExampleCandidate(topic: Pick<Topic, "exampleCandidates">, text: string): TopicExampleCandidate[];
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { Topic } from './types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* May this topic be offered on a conversation in an inbox of this team?
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `apps/communication/server/src/utils/scope.ts`),
|
|
8
|
-
*
|
|
9
|
-
* niet door te scrollen.
|
|
5
|
+
* Mind the word *offered*. This is a **picker filter**, not authorization: the read right is
|
|
6
|
+
* already handled by the server (`buildScopeQuery` in
|
|
7
|
+
* `apps/communication/server/src/utils/scope.ts`), so whatever arrives here the user may see
|
|
8
|
+
* anyway. What this filter adds is relevance — Sales need not scroll past Support's topics.
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* So it must **never** be used to look up an already assigned topic. A conversation that changes
|
|
11
|
+
* team keeps its label; looking that up in the filtered list would leave the label empty while a
|
|
12
|
+
* topic is very much set.
|
|
14
13
|
*
|
|
15
|
-
* `personal`
|
|
16
|
-
*
|
|
14
|
+
* `personal` always drops out: that scope is refused at write time, and if a row ever does arrive
|
|
15
|
+
* that way, it does not belong in a shared picker.
|
|
17
16
|
*/
|
|
18
17
|
export declare function topicOfferedForTeam(topic: Topic, teamId: string | undefined): boolean;
|
|
19
|
-
/** {@link topicOfferedForTeam} over
|
|
18
|
+
/** {@link topicOfferedForTeam} over a list. */
|
|
20
19
|
export declare function topicsForTeam(topics: Topic[], teamId: string | undefined): Topic[];
|