@mj-biz-apps/common-ng 5.43.0 → 5.44.0
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/lib/components/related-chips/__tests__/related-links.test.d.ts +2 -0
- package/dist/lib/components/related-chips/__tests__/related-links.test.d.ts.map +1 -0
- package/dist/lib/components/related-chips/__tests__/related-links.test.js +183 -0
- package/dist/lib/components/related-chips/__tests__/related-links.test.js.map +1 -0
- package/dist/lib/components/related-chips/related-chips.component.d.ts +132 -0
- package/dist/lib/components/related-chips/related-chips.component.d.ts.map +1 -0
- package/dist/lib/components/related-chips/related-chips.component.js +241 -0
- package/dist/lib/components/related-chips/related-chips.component.js.map +1 -0
- package/dist/lib/components/related-chips/related-links.d.ts +151 -0
- package/dist/lib/components/related-chips/related-links.d.ts.map +1 -0
- package/dist/lib/components/related-chips/related-links.js +178 -0
- package/dist/lib/components/related-chips/related-links.js.map +1 -0
- package/dist/lib/data/__tests__/directory-headline.test.d.ts +2 -0
- package/dist/lib/data/__tests__/directory-headline.test.d.ts.map +1 -0
- package/dist/lib/data/__tests__/directory-headline.test.js +102 -0
- package/dist/lib/data/__tests__/directory-headline.test.js.map +1 -0
- package/dist/lib/data/directory-stats.d.ts +23 -1
- package/dist/lib/data/directory-stats.d.ts.map +1 -1
- package/dist/lib/data/directory-stats.js +40 -0
- package/dist/lib/data/directory-stats.js.map +1 -1
- package/dist/lib/data/directory-types.d.ts +34 -0
- package/dist/lib/data/directory-types.d.ts.map +1 -1
- package/dist/lib/pages/common-dashboard.page.d.ts +7 -7
- package/dist/lib/pages/common-dashboard.page.d.ts.map +1 -1
- package/dist/lib/pages/common-dashboard.page.js +228 -202
- package/dist/lib/pages/common-dashboard.page.js.map +1 -1
- package/dist/public-api.d.ts +2 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +2 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { RecordNavigationEvent } from '@memberjunction/ng-base-forms';
|
|
2
|
+
/**
|
|
3
|
+
* One relationship a record header offers to follow.
|
|
4
|
+
*
|
|
5
|
+
* Give it either a {@link RecordID} (the ordinary case — this record holds the id) or a
|
|
6
|
+
* {@link Filter} (the reverse case — the other record holds the id, as with an Order whose Deal
|
|
7
|
+
* points at it). A link with neither names nothing and is dropped.
|
|
8
|
+
*/
|
|
9
|
+
export interface BizAppsRelatedLink {
|
|
10
|
+
/** Stable identity, used to track the chip and to correlate it back to the caller's own model. */
|
|
11
|
+
Key: string;
|
|
12
|
+
/**
|
|
13
|
+
* Entity that owns the linked record, as a name — e.g. `'MJ_BizApps_Orders: Order Headers'`.
|
|
14
|
+
*
|
|
15
|
+
* An entity that is not in the catalog produces NO chip. That covers both "the app that owns
|
|
16
|
+
* this record is not installed here" and "this user may not read it", and the two are
|
|
17
|
+
* deliberately one outcome: the entity catalog is already permission-filtered, and a reader who
|
|
18
|
+
* cannot open the record is no better served by a chip than one whose host never had the app.
|
|
19
|
+
* `bizapps-sales`' `OrdersIsInstalled()` draws the same line for the same reason.
|
|
20
|
+
*/
|
|
21
|
+
EntityName: string;
|
|
22
|
+
/** Forward link: the linked record's primary key value, as held on the record being displayed. */
|
|
23
|
+
RecordID?: string | null;
|
|
24
|
+
/**
|
|
25
|
+
* Reverse link: a filter that selects the linked record. The first row wins, so pass something
|
|
26
|
+
* that identifies one record. Composed by the caller, which is the only party that knows the
|
|
27
|
+
* other entity's column names.
|
|
28
|
+
*/
|
|
29
|
+
Filter?: string;
|
|
30
|
+
/** Names the relationship outright. Wins over {@link LabelPrefix}. */
|
|
31
|
+
Label?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Builds the label as `<prefix> <entity's singular name>` — "Source Deal", "Source Order".
|
|
34
|
+
*
|
|
35
|
+
* For a polymorphic link, where the caller knows the ROLE the other record plays but not which
|
|
36
|
+
* entity fills it until the id is resolved. Hardcoding "Deal" is what this avoids.
|
|
37
|
+
*/
|
|
38
|
+
LabelPrefix?: string;
|
|
39
|
+
/** Font Awesome class for a small leading icon. Optional. */
|
|
40
|
+
Icon?: string;
|
|
41
|
+
}
|
|
42
|
+
/** The part of `EntityInfo` a link needs. `EntityInfo` satisfies this. */
|
|
43
|
+
export interface RelatedLinkEntity {
|
|
44
|
+
Name: string;
|
|
45
|
+
BaseTableDisplayName: string;
|
|
46
|
+
NameField: {
|
|
47
|
+
Name: string;
|
|
48
|
+
} | null;
|
|
49
|
+
PrimaryKeys: {
|
|
50
|
+
Name: string;
|
|
51
|
+
}[];
|
|
52
|
+
}
|
|
53
|
+
/** The part of `RunViewResult` a link needs. `RunViewResult<Record<string, unknown>>` satisfies this. */
|
|
54
|
+
export interface RelatedLinkReadResult {
|
|
55
|
+
Success: boolean;
|
|
56
|
+
Results?: Record<string, unknown>[];
|
|
57
|
+
}
|
|
58
|
+
/** Reads one row for a link. The component supplies a provider-scoped `RunView`. */
|
|
59
|
+
export type RelatedLinkReader = (params: {
|
|
60
|
+
EntityName: string;
|
|
61
|
+
Fields: string[];
|
|
62
|
+
ExtraFilter: string;
|
|
63
|
+
MaxRows: number;
|
|
64
|
+
}) => Promise<RelatedLinkReadResult>;
|
|
65
|
+
/** A link that resolved to something openable. Anything that did not resolve is absent, not blank. */
|
|
66
|
+
export interface ResolvedRelatedChip {
|
|
67
|
+
Key: string;
|
|
68
|
+
/** What the relationship is: "Source Deal", "Order". */
|
|
69
|
+
Label: string;
|
|
70
|
+
/** The linked record's name. Empty ONLY when the record exists but could not be read — see below. */
|
|
71
|
+
Name: string;
|
|
72
|
+
Icon: string | null;
|
|
73
|
+
EntityName: string;
|
|
74
|
+
PrimaryKeyField: string;
|
|
75
|
+
RecordID: string;
|
|
76
|
+
/** Accessible name for the chip's button, composed once here so the template stays declarative. */
|
|
77
|
+
AriaLabel: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The filter that selects the linked record, or `null` when the link names nothing to select.
|
|
81
|
+
*
|
|
82
|
+
* A `RecordID` is preferred over a `Filter` when both are given: the caller holding the id is the
|
|
83
|
+
* more direct statement of the same fact.
|
|
84
|
+
*/
|
|
85
|
+
export declare function FilterForRelatedLink(link: BizAppsRelatedLink, entity: RelatedLinkEntity): string | null;
|
|
86
|
+
/** The entity's primary key column, defaulting to `ID` as every other link in these apps does. */
|
|
87
|
+
export declare function PrimaryKeyFieldFor(entity: RelatedLinkEntity): string;
|
|
88
|
+
/**
|
|
89
|
+
* The chip's label: what the caller called it, else `<prefix> <entity>`, else the entity's own name.
|
|
90
|
+
*
|
|
91
|
+
* `BaseTableDisplayName` rather than `DisplayName` because a chip labels ONE record and the entity
|
|
92
|
+
* name is plural — it is what turns a Deals link into "Source Deal" without guessing at plurals.
|
|
93
|
+
*/
|
|
94
|
+
export declare function LabelForRelatedLink(link: BizAppsRelatedLink, entity: RelatedLinkEntity): string;
|
|
95
|
+
/**
|
|
96
|
+
* Resolve one link to a chip, or to `null` for "show nothing".
|
|
97
|
+
*
|
|
98
|
+
* ## The two rules this function exists to hold
|
|
99
|
+
*
|
|
100
|
+
* **A CHIP NEVER SHOWS A RAW ID.** The whole point of the row is that a reader recognises what they
|
|
101
|
+
* are about to open. So the name is read from the entity's own name field, and an entity with no
|
|
102
|
+
* name field produces no chip rather than a GUID wearing a label.
|
|
103
|
+
*
|
|
104
|
+
* **A CHIP THAT CANNOT NAVIGATE IS NOT RENDERED.** A link that goes nowhere is worse than an absent
|
|
105
|
+
* one: it invites a click and spends the reader's trust. Five ways a link produces nothing — the
|
|
106
|
+
* entity is not in the catalog, the entity has no name field, the link names neither a record nor a
|
|
107
|
+
* filter, the read succeeded while matching zero rows, and the read was refused.
|
|
108
|
+
*
|
|
109
|
+
* ## Why a read that THREW is treated differently from one that FAILED or found nothing
|
|
110
|
+
*
|
|
111
|
+
* Three different facts, and the line between them is whether the server answered.
|
|
112
|
+
*
|
|
113
|
+
* **Zero rows from a successful read** means the record is not there — `bizapps-contracts` hit
|
|
114
|
+
* exactly this with CTR-000026, whose hand-typed provenance pair names a row that does not exist,
|
|
115
|
+
* and whose header rendered an "Open" button that navigated nowhere. No chip.
|
|
116
|
+
*
|
|
117
|
+
* **`Success: false`** is the server answering and refusing. In MJ that is the channel for a
|
|
118
|
+
* permission denial as much as for a bad filter: `GraphQLDataProvider.InternalRunView` throws when
|
|
119
|
+
* the transport fails and returns `Success: false` with an `ErrorMessage` when the request reached
|
|
120
|
+
* the server and was turned down. A reader who may not read Orders would otherwise get an
|
|
121
|
+
* "Order · Open" chip that navigates to a record that will not open — the dead link this component
|
|
122
|
+
* exists to prevent. So a refusal produces NO chip.
|
|
123
|
+
*
|
|
124
|
+
* **A thrown read** means we do not know: the record may well exist and simply be unreachable this
|
|
125
|
+
* moment. So a throw keeps the chip when we already hold the id to open — labelled, with "Open"
|
|
126
|
+
* where the name would go — and drops it for a reverse link, where the throw also cost us the id.
|
|
127
|
+
*/
|
|
128
|
+
export declare function ResolveRelatedChip(link: BizAppsRelatedLink, entities: RelatedLinkEntity[], read: RelatedLinkReader): Promise<ResolvedRelatedChip | null>;
|
|
129
|
+
/** The part of a `MouseEvent` that decides where a chip opens. `MouseEvent` satisfies this. */
|
|
130
|
+
export interface RelatedChipClick {
|
|
131
|
+
ctrlKey: boolean;
|
|
132
|
+
metaKey: boolean;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The navigation event a chip click produces.
|
|
136
|
+
*
|
|
137
|
+
* Ctrl (Windows/Linux) and Cmd (macOS) both mean "new tab", which is the idiom MJ uses everywhere a
|
|
138
|
+
* record link is clicked.
|
|
139
|
+
*
|
|
140
|
+
* `OpenInNewTab` is OMITTED rather than set to `false` on a plain click, and that is the whole of
|
|
141
|
+
* what makes shift-click work. `NavigationService.shouldForceNewTab` returns `options.forceNewTab`
|
|
142
|
+
* whenever it is `!== undefined` and only otherwise consults its global shift-key state, and
|
|
143
|
+
* Explorer passes `forceNewTab: event.OpenInNewTab` straight through. An explicit `false` therefore
|
|
144
|
+
* does not mean "no opinion" — it means "same tab", and it suppresses the shift detection outright.
|
|
145
|
+
*
|
|
146
|
+
* `CompositeKey.FromKeyValuePair` rather than `FromID`: the latter assumes the target's primary key
|
|
147
|
+
* is literally called `ID`, which is true of these apps today and is not a thing a shared component
|
|
148
|
+
* gets to assume.
|
|
149
|
+
*/
|
|
150
|
+
export declare function RelatedChipNavigation(chip: ResolvedRelatedChip, event: RelatedChipClick): RecordNavigationEvent;
|
|
151
|
+
//# sourceMappingURL=related-links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"related-links.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/related-chips/related-links.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAE3E;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IAC/B,kGAAkG;IAClG,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,kGAAkG;IAClG,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,EAAE,MAAM,CAAC;IAC7B,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACnC,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACnC;AAED,yGAAyG;AACzG,MAAM,WAAW,qBAAqB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CACvC;AAED,oFAAoF;AACpF,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACnB,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;AAErC,sGAAsG;AACtG,MAAM,WAAW,mBAAmB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,qGAAqG;IACrG,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,mGAAmG;IACnG,SAAS,EAAE,MAAM,CAAC;CACrB;AAYD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAKvG;AAED,kGAAkG;AAClG,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAEpE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAQ/F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,kBAAkB,CACpC,IAAI,EAAE,kBAAkB,EACxB,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,IAAI,EAAE,iBAAiB,GACxB,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CA4CrC;AAmCD,+FAA+F;AAC/F,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,gBAAgB,GAAG,qBAAqB,CAU/G"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The resolve-and-hide rules behind `bizapps-related-chips`, kept out of the component.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS NOT IN THE COMPONENT. Everything here is a decision about whether a chip may be shown
|
|
5
|
+
* at all — "is the app installed", "does the record still exist", "did the read fail or did it
|
|
6
|
+
* succeed and find nothing" — and those are exactly the things that must be pinned by tests. A rule
|
|
7
|
+
* declared inside an Angular component cannot be exercised without standing up DI, which this
|
|
8
|
+
* package's node-environment vitest setup deliberately does not do. Same split as
|
|
9
|
+
* `bizapps-contracts`' `source-record-candidates.ts`.
|
|
10
|
+
*
|
|
11
|
+
* The dependencies below are narrowed to the shape each function actually reads rather than taking
|
|
12
|
+
* `IMetadataProvider` / `RunView`. `EntityInfo[]` and `RunViewResult` satisfy them structurally, so
|
|
13
|
+
* the component passes the real objects while a test passes a literal.
|
|
14
|
+
*
|
|
15
|
+
* @module @mj-biz-apps/common-ng
|
|
16
|
+
*/
|
|
17
|
+
import { CompositeKey } from '@memberjunction/core';
|
|
18
|
+
/** Single-quote escaping for a value going into an `ExtraFilter`. */
|
|
19
|
+
function quote(value) {
|
|
20
|
+
return value.replace(/'/g, "''");
|
|
21
|
+
}
|
|
22
|
+
/** `true` for a value that actually names a record — not null, not undefined, not blank. */
|
|
23
|
+
function isPresent(value) {
|
|
24
|
+
return value !== null && value !== undefined && String(value).trim().length > 0;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The filter that selects the linked record, or `null` when the link names nothing to select.
|
|
28
|
+
*
|
|
29
|
+
* A `RecordID` is preferred over a `Filter` when both are given: the caller holding the id is the
|
|
30
|
+
* more direct statement of the same fact.
|
|
31
|
+
*/
|
|
32
|
+
export function FilterForRelatedLink(link, entity) {
|
|
33
|
+
if (isPresent(link.RecordID)) {
|
|
34
|
+
return `${PrimaryKeyFieldFor(entity)} = '${quote(String(link.RecordID).trim())}'`;
|
|
35
|
+
}
|
|
36
|
+
return isPresent(link.Filter) ? link.Filter.trim() : null;
|
|
37
|
+
}
|
|
38
|
+
/** The entity's primary key column, defaulting to `ID` as every other link in these apps does. */
|
|
39
|
+
export function PrimaryKeyFieldFor(entity) {
|
|
40
|
+
return entity.PrimaryKeys?.[0]?.Name ?? 'ID';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The chip's label: what the caller called it, else `<prefix> <entity>`, else the entity's own name.
|
|
44
|
+
*
|
|
45
|
+
* `BaseTableDisplayName` rather than `DisplayName` because a chip labels ONE record and the entity
|
|
46
|
+
* name is plural — it is what turns a Deals link into "Source Deal" without guessing at plurals.
|
|
47
|
+
*/
|
|
48
|
+
export function LabelForRelatedLink(link, entity) {
|
|
49
|
+
if (isPresent(link.Label)) {
|
|
50
|
+
return link.Label.trim();
|
|
51
|
+
}
|
|
52
|
+
if (isPresent(link.LabelPrefix)) {
|
|
53
|
+
return `${link.LabelPrefix.trim()} ${entity.BaseTableDisplayName}`;
|
|
54
|
+
}
|
|
55
|
+
return entity.BaseTableDisplayName;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolve one link to a chip, or to `null` for "show nothing".
|
|
59
|
+
*
|
|
60
|
+
* ## The two rules this function exists to hold
|
|
61
|
+
*
|
|
62
|
+
* **A CHIP NEVER SHOWS A RAW ID.** The whole point of the row is that a reader recognises what they
|
|
63
|
+
* are about to open. So the name is read from the entity's own name field, and an entity with no
|
|
64
|
+
* name field produces no chip rather than a GUID wearing a label.
|
|
65
|
+
*
|
|
66
|
+
* **A CHIP THAT CANNOT NAVIGATE IS NOT RENDERED.** A link that goes nowhere is worse than an absent
|
|
67
|
+
* one: it invites a click and spends the reader's trust. Five ways a link produces nothing — the
|
|
68
|
+
* entity is not in the catalog, the entity has no name field, the link names neither a record nor a
|
|
69
|
+
* filter, the read succeeded while matching zero rows, and the read was refused.
|
|
70
|
+
*
|
|
71
|
+
* ## Why a read that THREW is treated differently from one that FAILED or found nothing
|
|
72
|
+
*
|
|
73
|
+
* Three different facts, and the line between them is whether the server answered.
|
|
74
|
+
*
|
|
75
|
+
* **Zero rows from a successful read** means the record is not there — `bizapps-contracts` hit
|
|
76
|
+
* exactly this with CTR-000026, whose hand-typed provenance pair names a row that does not exist,
|
|
77
|
+
* and whose header rendered an "Open" button that navigated nowhere. No chip.
|
|
78
|
+
*
|
|
79
|
+
* **`Success: false`** is the server answering and refusing. In MJ that is the channel for a
|
|
80
|
+
* permission denial as much as for a bad filter: `GraphQLDataProvider.InternalRunView` throws when
|
|
81
|
+
* the transport fails and returns `Success: false` with an `ErrorMessage` when the request reached
|
|
82
|
+
* the server and was turned down. A reader who may not read Orders would otherwise get an
|
|
83
|
+
* "Order · Open" chip that navigates to a record that will not open — the dead link this component
|
|
84
|
+
* exists to prevent. So a refusal produces NO chip.
|
|
85
|
+
*
|
|
86
|
+
* **A thrown read** means we do not know: the record may well exist and simply be unreachable this
|
|
87
|
+
* moment. So a throw keeps the chip when we already hold the id to open — labelled, with "Open"
|
|
88
|
+
* where the name would go — and drops it for a reverse link, where the throw also cost us the id.
|
|
89
|
+
*/
|
|
90
|
+
export async function ResolveRelatedChip(link, entities, read) {
|
|
91
|
+
const entity = entities?.find((e) => e.Name === link.EntityName) ?? null;
|
|
92
|
+
if (!entity) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const nameField = entity.NameField?.Name;
|
|
96
|
+
if (!isPresent(nameField)) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const filter = FilterForRelatedLink(link, entity);
|
|
100
|
+
if (!filter) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const pkField = PrimaryKeyFieldFor(entity);
|
|
104
|
+
const label = LabelForRelatedLink(link, entity);
|
|
105
|
+
try {
|
|
106
|
+
const result = await read({
|
|
107
|
+
EntityName: entity.Name,
|
|
108
|
+
Fields: [pkField, nameField],
|
|
109
|
+
ExtraFilter: filter,
|
|
110
|
+
MaxRows: 1,
|
|
111
|
+
});
|
|
112
|
+
if (result?.Success === false) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const row = result?.Success ? result.Results?.[0] : undefined;
|
|
116
|
+
if (result?.Success && !row) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
if (!row) {
|
|
120
|
+
return unreadableChip(link, entity, label, pkField);
|
|
121
|
+
}
|
|
122
|
+
const recordID = String(row[pkField] ?? link.RecordID ?? '');
|
|
123
|
+
if (!isPresent(recordID)) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return chip(link, entity, label, pkField, recordID, String(row[nameField] ?? ''));
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return unreadableChip(link, entity, label, pkField);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The chip for a record we could not read but can still open — only possible when the link carried
|
|
134
|
+
* the id itself. A reverse link has nothing left to open, so it renders nothing.
|
|
135
|
+
*/
|
|
136
|
+
function unreadableChip(link, entity, label, pkField) {
|
|
137
|
+
return isPresent(link.RecordID) ? chip(link, entity, label, pkField, String(link.RecordID).trim(), '') : null;
|
|
138
|
+
}
|
|
139
|
+
function chip(link, entity, label, pkField, recordID, name) {
|
|
140
|
+
return {
|
|
141
|
+
Key: link.Key,
|
|
142
|
+
Label: label,
|
|
143
|
+
Name: name,
|
|
144
|
+
Icon: link.Icon ?? null,
|
|
145
|
+
EntityName: entity.Name,
|
|
146
|
+
PrimaryKeyField: pkField,
|
|
147
|
+
RecordID: recordID,
|
|
148
|
+
AriaLabel: name ? `Open ${label} ${name}` : `Open ${label}`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The navigation event a chip click produces.
|
|
153
|
+
*
|
|
154
|
+
* Ctrl (Windows/Linux) and Cmd (macOS) both mean "new tab", which is the idiom MJ uses everywhere a
|
|
155
|
+
* record link is clicked.
|
|
156
|
+
*
|
|
157
|
+
* `OpenInNewTab` is OMITTED rather than set to `false` on a plain click, and that is the whole of
|
|
158
|
+
* what makes shift-click work. `NavigationService.shouldForceNewTab` returns `options.forceNewTab`
|
|
159
|
+
* whenever it is `!== undefined` and only otherwise consults its global shift-key state, and
|
|
160
|
+
* Explorer passes `forceNewTab: event.OpenInNewTab` straight through. An explicit `false` therefore
|
|
161
|
+
* does not mean "no opinion" — it means "same tab", and it suppresses the shift detection outright.
|
|
162
|
+
*
|
|
163
|
+
* `CompositeKey.FromKeyValuePair` rather than `FromID`: the latter assumes the target's primary key
|
|
164
|
+
* is literally called `ID`, which is true of these apps today and is not a thing a shared component
|
|
165
|
+
* gets to assume.
|
|
166
|
+
*/
|
|
167
|
+
export function RelatedChipNavigation(chip, event) {
|
|
168
|
+
const navigation = {
|
|
169
|
+
Kind: 'record',
|
|
170
|
+
EntityName: chip.EntityName,
|
|
171
|
+
PrimaryKey: CompositeKey.FromKeyValuePair(chip.PrimaryKeyField, chip.RecordID),
|
|
172
|
+
};
|
|
173
|
+
if (event.ctrlKey || event.metaKey) {
|
|
174
|
+
navigation.OpenInNewTab = true;
|
|
175
|
+
}
|
|
176
|
+
return navigation;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=related-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"related-links.js","sourceRoot":"","sources":["../../../../src/lib/components/related-chips/related-links.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-headline.test.d.ts","sourceRoot":"","sources":["../../../../src/lib/data/__tests__/directory-headline.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { BuildDirectoryHeadline, DIRECTORY_HEADLINE_UNREADABLE } from '../directory-stats';
|
|
3
|
+
/**
|
|
4
|
+
* These pin the one rule the directory dashboard cannot get wrong: a count nobody could read is NOT
|
|
5
|
+
* zero. `LoadDirectoryDashboardSummary` returns `null` when the query fails or comes back empty, and
|
|
6
|
+
* the headline it produces must reach the tiles as nulls — which render an em dash — with the row's
|
|
7
|
+
* error line set, never as four zeros claiming an empty party file.
|
|
8
|
+
*/
|
|
9
|
+
function queue(count, label = `queue-${count}`) {
|
|
10
|
+
return { Label: label, Count: count, Icon: 'fa-solid fa-flag', Tone: 'warning', PageId: 'people' };
|
|
11
|
+
}
|
|
12
|
+
function summary(partial = {}) {
|
|
13
|
+
return {
|
|
14
|
+
ActivePeopleCount: 120,
|
|
15
|
+
TotalPeopleCount: 120,
|
|
16
|
+
ActiveOrganizationCount: 30,
|
|
17
|
+
TotalOrganizationCount: 30,
|
|
18
|
+
RelationshipCount: 88,
|
|
19
|
+
Queues: [],
|
|
20
|
+
...partial,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
describe('BuildDirectoryHeadline', () => {
|
|
24
|
+
describe('a failed read', () => {
|
|
25
|
+
it('leaves every count null rather than zero — zero is a claim the directory is empty', () => {
|
|
26
|
+
const headline = BuildDirectoryHeadline(null);
|
|
27
|
+
expect(headline.ActivePeopleCount).toBeNull();
|
|
28
|
+
expect(headline.ActiveOrganizationCount).toBeNull();
|
|
29
|
+
expect(headline.RelationshipCount).toBeNull();
|
|
30
|
+
expect(headline.GapCount).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
it('says so once, on the row', () => {
|
|
33
|
+
expect(BuildDirectoryHeadline(null).Error).toBe(DIRECTORY_HEADLINE_UNREADABLE);
|
|
34
|
+
});
|
|
35
|
+
it('flags the read as failed, so no section reports itself empty', () => {
|
|
36
|
+
expect(BuildDirectoryHeadline(null).ReadFailed).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it('drops the footnotes, which have no count left to qualify', () => {
|
|
39
|
+
const headline = BuildDirectoryHeadline(null);
|
|
40
|
+
expect(headline.PeopleDetail).toBeNull();
|
|
41
|
+
expect(headline.OrganizationDetail).toBeNull();
|
|
42
|
+
});
|
|
43
|
+
it('treats undefined the same as null, the shape an unset property arrives as', () => {
|
|
44
|
+
expect(BuildDirectoryHeadline(undefined).ActivePeopleCount).toBeNull();
|
|
45
|
+
expect(BuildDirectoryHeadline(undefined).Error).toBe(DIRECTORY_HEADLINE_UNREADABLE);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('a successful read', () => {
|
|
49
|
+
it('carries the counts through and reports no error', () => {
|
|
50
|
+
const headline = BuildDirectoryHeadline(summary());
|
|
51
|
+
expect(headline.ActivePeopleCount).toBe(120);
|
|
52
|
+
expect(headline.ActiveOrganizationCount).toBe(30);
|
|
53
|
+
expect(headline.RelationshipCount).toBe(88);
|
|
54
|
+
expect(headline.Error).toBeNull();
|
|
55
|
+
expect(headline.ReadFailed).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
it('leaves ReadFailed false for a genuinely empty directory, so the sections still read empty', () => {
|
|
58
|
+
const headline = BuildDirectoryHeadline(summary({ ActivePeopleCount: 0, TotalPeopleCount: 0, ActiveOrganizationCount: 0, TotalOrganizationCount: 0, RelationshipCount: 0 }));
|
|
59
|
+
expect(headline.ReadFailed).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
it('keeps a real zero as zero — nothing on file is a legitimate answer, unlike an unread count', () => {
|
|
62
|
+
const headline = BuildDirectoryHeadline(summary({ ActivePeopleCount: 0, TotalPeopleCount: 0, RelationshipCount: 0 }));
|
|
63
|
+
expect(headline.ActivePeopleCount).toBe(0);
|
|
64
|
+
expect(headline.RelationshipCount).toBe(0);
|
|
65
|
+
expect(headline.GapCount).toBe(0);
|
|
66
|
+
expect(headline.Error).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
it('sums the queues into the gap count', () => {
|
|
69
|
+
const headline = BuildDirectoryHeadline(summary({ Queues: [queue(3), queue(4), queue(10)] }));
|
|
70
|
+
expect(headline.GapCount).toBe(17);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('the gap tone', () => {
|
|
74
|
+
it('warns on gaps that were actually counted', () => {
|
|
75
|
+
expect(BuildDirectoryHeadline(summary({ Queues: [queue(1)] })).GapTone).toBe('warn');
|
|
76
|
+
});
|
|
77
|
+
it('stays quiet when the count read as zero', () => {
|
|
78
|
+
expect(BuildDirectoryHeadline(summary({ Queues: [] })).GapTone).toBe('none');
|
|
79
|
+
});
|
|
80
|
+
it('stays quiet on an unread count — "could not check" is not "something to fix"', () => {
|
|
81
|
+
expect(BuildDirectoryHeadline(null).GapTone).toBe('none');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe('the footnotes', () => {
|
|
85
|
+
it('reads "everyone on file" when active equals total', () => {
|
|
86
|
+
const headline = BuildDirectoryHeadline(summary({ ActivePeopleCount: 120, TotalPeopleCount: 120 }));
|
|
87
|
+
expect(headline.PeopleDetail).toBe('Everyone currently on file');
|
|
88
|
+
expect(headline.OrganizationDetail).toBe('Active organizations');
|
|
89
|
+
});
|
|
90
|
+
it('names the total when inactive records exist', () => {
|
|
91
|
+
const headline = BuildDirectoryHeadline(summary({
|
|
92
|
+
ActivePeopleCount: 120,
|
|
93
|
+
TotalPeopleCount: 145,
|
|
94
|
+
ActiveOrganizationCount: 30,
|
|
95
|
+
TotalOrganizationCount: 41,
|
|
96
|
+
}));
|
|
97
|
+
expect(headline.PeopleDetail).toBe('145 total, including inactive');
|
|
98
|
+
expect(headline.OrganizationDetail).toBe('41 total, including inactive');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
//# sourceMappingURL=directory-headline.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-headline.test.js","sourceRoot":"","sources":["../../../../src/lib/data/__tests__/directory-headline.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DirectoryAttentionItem, DirectoryBarRow, DirectoryDayBar, DirectoryOrganizationRow, DirectoryPersonRow, DirectoryQueue, DirectoryRelationshipRow } from './directory-types';
|
|
1
|
+
import type { DirectoryAttentionItem, DirectoryBarRow, DirectoryDayBar, DirectoryHeadline, DirectoryOrganizationRow, DirectoryPersonRow, DirectoryQueue, DirectoryRelationshipRow } from './directory-types';
|
|
2
2
|
export declare function PersonEmail(person: DirectoryPersonRow): string | null;
|
|
3
3
|
export declare function PersonPhone(person: DirectoryPersonRow): string | null;
|
|
4
4
|
export declare function LocalDayKey(value: Date | string): string;
|
|
@@ -28,4 +28,26 @@ export declare function EscapeFilterValue(value: string): string;
|
|
|
28
28
|
* literal character instead of widening the match.
|
|
29
29
|
*/
|
|
30
30
|
export declare function EscapeLikeValue(value: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* The summary fields {@link BuildDirectoryHeadline} reads. Declared structurally so this module stays
|
|
33
|
+
* free of the query layer — `DirectoryDashboardSummary` in `directory-queries.ts` satisfies it.
|
|
34
|
+
*/
|
|
35
|
+
export interface DirectorySummaryCounts {
|
|
36
|
+
ActivePeopleCount: number;
|
|
37
|
+
TotalPeopleCount: number;
|
|
38
|
+
ActiveOrganizationCount: number;
|
|
39
|
+
TotalOrganizationCount: number;
|
|
40
|
+
RelationshipCount: number;
|
|
41
|
+
Queues: readonly DirectoryQueue[];
|
|
42
|
+
}
|
|
43
|
+
/** What the row says when the summary query did not return. Muted, not red — see `StatRowComponent`. */
|
|
44
|
+
export declare const DIRECTORY_HEADLINE_UNREADABLE = "The directory counts could not be read just now.";
|
|
45
|
+
/**
|
|
46
|
+
* Turn one summary read into the four headline tiles.
|
|
47
|
+
*
|
|
48
|
+
* A `null` summary is the failed read, and every count comes back `null` so each tile shows an em
|
|
49
|
+
* dash and the row carries the sentence. It is deliberately all-or-nothing: the counts come from a
|
|
50
|
+
* single query, so there is no state in which some of them are known.
|
|
51
|
+
*/
|
|
52
|
+
export declare function BuildDirectoryHeadline(summary: DirectorySummaryCounts | null | undefined): DirectoryHeadline;
|
|
31
53
|
//# sourceMappingURL=directory-stats.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-stats.d.ts","sourceRoot":"","sources":["../../../src/lib/data/directory-stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,EACd,wBAAwB,EAC3B,MAAM,mBAAmB,CAAC;AAE3B,wBAAgB,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAErE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAErE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAOxD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,GAAG,kBAAkB,EAAE,CAExF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GAAG,wBAAwB,EAAE,CAEzG;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,GAAG,kBAAkB,EAAE,CAE9F;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,GAAG,kBAAkB,EAAE,CAErG;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GAAG,wBAAwB,EAAE,CAE9G;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GAAG,wBAAwB,EAAE,CAEjH;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS;IAAE,cAAc,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,EAAE,EAAE,IAAI,SAAI,GAAG,eAAe,EAAE,CAc1G;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,eAAe,EAAE,CASlF;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,cAAc,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,SAAI,GAAG,CAAC,EAAE,CAI9G;AAED,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GAC1C,cAAc,EAAE,CAoClB;AAED,wBAAgB,mBAAmB,CAC/B,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GAC1C,sBAAsB,EAAE,CA6B1B;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,wBAAwB,GAAG,MAAM,CAIvE;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKrD"}
|
|
1
|
+
{"version":3,"file":"directory-stats.d.ts","sourceRoot":"","sources":["../../../src/lib/data/directory-stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,EACd,wBAAwB,EAC3B,MAAM,mBAAmB,CAAC;AAE3B,wBAAgB,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAErE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAErE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAOxD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,GAAG,kBAAkB,EAAE,CAExF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GAAG,wBAAwB,EAAE,CAEzG;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,GAAG,kBAAkB,EAAE,CAE9F;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,GAAG,kBAAkB,EAAE,CAErG;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GAAG,wBAAwB,EAAE,CAE9G;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GAAG,wBAAwB,EAAE,CAEjH;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS;IAAE,cAAc,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,EAAE,EAAE,IAAI,SAAI,GAAG,eAAe,EAAE,CAc1G;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,eAAe,EAAE,CASlF;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,cAAc,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,SAAI,GAAG,CAAC,EAAE,CAI9G;AAED,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GAC1C,cAAc,EAAE,CAoClB;AAED,wBAAgB,mBAAmB,CAC/B,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GAC1C,sBAAsB,EAAE,CA6B1B;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,wBAAwB,GAAG,MAAM,CAIvE;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACnC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB,EAAE,MAAM,CAAC;IAChC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;CACrC;AAED,wGAAwG;AACxG,eAAO,MAAM,6BAA6B,qDAAqD,CAAC;AAEhG;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,GAAG,iBAAiB,CAgC5G"}
|
|
@@ -146,4 +146,44 @@ export function EscapeLikeValue(value) {
|
|
|
146
146
|
.replace(/%/g, '[%]')
|
|
147
147
|
.replace(/_/g, '[_]');
|
|
148
148
|
}
|
|
149
|
+
/** What the row says when the summary query did not return. Muted, not red — see `StatRowComponent`. */
|
|
150
|
+
export const DIRECTORY_HEADLINE_UNREADABLE = 'The directory counts could not be read just now.';
|
|
151
|
+
/**
|
|
152
|
+
* Turn one summary read into the four headline tiles.
|
|
153
|
+
*
|
|
154
|
+
* A `null` summary is the failed read, and every count comes back `null` so each tile shows an em
|
|
155
|
+
* dash and the row carries the sentence. It is deliberately all-or-nothing: the counts come from a
|
|
156
|
+
* single query, so there is no state in which some of them are known.
|
|
157
|
+
*/
|
|
158
|
+
export function BuildDirectoryHeadline(summary) {
|
|
159
|
+
if (!summary) {
|
|
160
|
+
return {
|
|
161
|
+
ReadFailed: true,
|
|
162
|
+
ActivePeopleCount: null,
|
|
163
|
+
ActiveOrganizationCount: null,
|
|
164
|
+
RelationshipCount: null,
|
|
165
|
+
GapCount: null,
|
|
166
|
+
GapTone: 'none',
|
|
167
|
+
PeopleDetail: null,
|
|
168
|
+
OrganizationDetail: null,
|
|
169
|
+
Error: DIRECTORY_HEADLINE_UNREADABLE,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const gapCount = summary.Queues.reduce((sum, queue) => sum + queue.Count, 0);
|
|
173
|
+
return {
|
|
174
|
+
ReadFailed: false,
|
|
175
|
+
ActivePeopleCount: summary.ActivePeopleCount,
|
|
176
|
+
ActiveOrganizationCount: summary.ActiveOrganizationCount,
|
|
177
|
+
RelationshipCount: summary.RelationshipCount,
|
|
178
|
+
GapCount: gapCount,
|
|
179
|
+
GapTone: gapCount > 0 ? 'warn' : 'none',
|
|
180
|
+
PeopleDetail: summary.TotalPeopleCount === summary.ActivePeopleCount
|
|
181
|
+
? 'Everyone currently on file'
|
|
182
|
+
: `${summary.TotalPeopleCount} total, including inactive`,
|
|
183
|
+
OrganizationDetail: summary.TotalOrganizationCount === summary.ActiveOrganizationCount
|
|
184
|
+
? 'Active organizations'
|
|
185
|
+
: `${summary.TotalOrganizationCount} total, including inactive`,
|
|
186
|
+
Error: null,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
149
189
|
//# sourceMappingURL=directory-stats.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-stats.js","sourceRoot":"","sources":["../../../src/lib/data/directory-stats.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"directory-stats.js","sourceRoot":"","sources":["../../../src/lib/data/directory-stats.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,4 +30,38 @@ export interface DirectoryAttentionItem {
|
|
|
30
30
|
Headline: string;
|
|
31
31
|
Detail: string;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* The four headline counts of the directory dashboard, in the shape `bizapps-stat-tile` takes them.
|
|
35
|
+
*
|
|
36
|
+
* The counts are `number | null` rather than `number` on purpose. They all come from one query, so
|
|
37
|
+
* they are one atomic read: either every number is true or none of them is. `null` is the tile's
|
|
38
|
+
* "could not be read" value and renders an em dash; `0` is a real answer and renders "0". Defaulting
|
|
39
|
+
* an unread count to `0` tells the reader the directory is empty when it is only unreadable, which is
|
|
40
|
+
* the one thing a dashboard must not say.
|
|
41
|
+
*/
|
|
42
|
+
export interface DirectoryHeadline {
|
|
43
|
+
/**
|
|
44
|
+
* True when the summary query did not return. Every section fed by that one read consults this
|
|
45
|
+
* before it reports itself empty: a list that is empty because nothing was read is not "nothing
|
|
46
|
+
* to do", and a green check over an unread directory is the same lie as a zero count.
|
|
47
|
+
*/
|
|
48
|
+
ReadFailed: boolean;
|
|
49
|
+
ActivePeopleCount: number | null;
|
|
50
|
+
ActiveOrganizationCount: number | null;
|
|
51
|
+
RelationshipCount: number | null;
|
|
52
|
+
GapCount: number | null;
|
|
53
|
+
/**
|
|
54
|
+
* How the gap count should read. `warn` only on a gap count that was actually read and is above
|
|
55
|
+
* zero: an unknown count is an em dash, and colouring it amber would turn "we could not check"
|
|
56
|
+
* into "there is something to fix". Matches `StatTileTone`, kept as a literal union so the data
|
|
57
|
+
* layer does not depend on the component layer.
|
|
58
|
+
*/
|
|
59
|
+
GapTone: 'none' | 'warn';
|
|
60
|
+
/** Footnote under the people count, or `null` when there is no count to qualify. */
|
|
61
|
+
PeopleDetail: string | null;
|
|
62
|
+
/** Footnote under the organization count, or `null` when there is no count to qualify. */
|
|
63
|
+
OrganizationDetail: string | null;
|
|
64
|
+
/** One sentence for `bizapps-stat-row`'s error line, or `null` when everything read. */
|
|
65
|
+
Error: string | null;
|
|
66
|
+
}
|
|
33
67
|
//# sourceMappingURL=directory-types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-types.d.ts","sourceRoot":"","sources":["../../../src/lib/data/directory-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iCAAiC,EAAE,2BAA2B,EAAE,iCAAiC,EAAE,MAAM,8BAA8B,CAAC;AAEtJ,kEAAkE;AAClE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACjC,2BAA2B,EACzB,IAAI,GACJ,aAAa,GACb,WAAW,GACX,UAAU,GACV,OAAO,GACP,cAAc,GACd,OAAO,GACP,cAAc,GACd,QAAQ,GACR,OAAO,GACP,yBAAyB,GACzB,uBAAuB,GACvB,oBAAoB,GACpB,gBAAgB,CACrB,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACvC,iCAAiC,EAC/B,IAAI,GACJ,MAAM,GACN,WAAW,GACX,QAAQ,GACR,kBAAkB,GAClB,oBAAoB,GACpB,SAAS,GACT,OAAO,GACP,OAAO,GACP,QAAQ,GACR,oBAAoB,GACpB,gBAAgB,CACrB,CAAC;AAEF,wDAAwD;AACxD,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACvC,iCAAiC,EAC/B,IAAI,GACJ,kBAAkB,GAClB,OAAO,GACP,QAAQ,GACR,YAAY,GACZ,kBAAkB,GAClB,UAAU,GACV,gBAAgB,GAChB,cAAc,GACd,YAAY,GACZ,oBAAoB,GACpB,kBAAkB,GAClB,gBAAgB,CACrB,CAAC;AAEF,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3D,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACnC,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAClB"}
|
|
1
|
+
{"version":3,"file":"directory-types.d.ts","sourceRoot":"","sources":["../../../src/lib/data/directory-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iCAAiC,EAAE,2BAA2B,EAAE,iCAAiC,EAAE,MAAM,8BAA8B,CAAC;AAEtJ,kEAAkE;AAClE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACjC,2BAA2B,EACzB,IAAI,GACJ,aAAa,GACb,WAAW,GACX,UAAU,GACV,OAAO,GACP,cAAc,GACd,OAAO,GACP,cAAc,GACd,QAAQ,GACR,OAAO,GACP,yBAAyB,GACzB,uBAAuB,GACvB,oBAAoB,GACpB,gBAAgB,CACrB,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACvC,iCAAiC,EAC/B,IAAI,GACJ,MAAM,GACN,WAAW,GACX,QAAQ,GACR,kBAAkB,GAClB,oBAAoB,GACpB,SAAS,GACT,OAAO,GACP,OAAO,GACP,QAAQ,GACR,oBAAoB,GACpB,gBAAgB,CACrB,CAAC;AAEF,wDAAwD;AACxD,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACvC,iCAAiC,EAC/B,IAAI,GACJ,kBAAkB,GAClB,OAAO,GACP,QAAQ,GACR,YAAY,GACZ,kBAAkB,GAClB,UAAU,GACV,gBAAgB,GAChB,cAAc,GACd,YAAY,GACZ,oBAAoB,GACpB,kBAAkB,GAClB,gBAAgB,CACrB,CAAC;AAEF,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3D,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACnC,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAC9B;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,oFAAoF;IACpF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,0FAA0F;IAC1F,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,wFAAwF;IACxF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB"}
|