@reqquest/ui 1.12.1 → 1.14.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.
@@ -1,4 +1,4 @@
1
- import type { LayoutStructureNode, LayoutStructureNodeRoot, UserProfile } from '@txstate-mws/carbon-svelte';
1
+ import type { LayoutStructureNode, LayoutStructureNodeRoot, ShellItem, UserProfile } from '@txstate-mws/carbon-svelte';
2
2
  import type { Component } from 'svelte';
3
3
  import type { ProgramKey, PromptKey, RequirementKey } from './keys.js';
4
4
  export interface PromptLoader {
@@ -10,7 +10,31 @@ export interface PromptLoader {
10
10
  delay?: number;
11
11
  }
12
12
  export type Loader = PromptLoader | boolean;
13
- export interface ProgramDefinition {
13
+ /**
14
+ * The panels the reviewer screen renders by default, grouped by requirement type. A program's
15
+ * `reviewSections` may place any of them by name.
16
+ *
17
+ * - `GENERAL` - "General Questions": PREQUAL requirements no custom panel claimed
18
+ * - `PROGRAM` - the panel titled with the program name: QUALIFICATION and POSTQUAL requirements no custom panel claimed
19
+ * - `REVIEWER` - "Reviewer Questions": PREAPPROVAL and APPROVAL requirements no custom panel claimed
20
+ * - `ACCEPTANCE` - "Acceptance": ACCEPTANCE requirements no custom panel claimed
21
+ */
22
+ export type ReviewDefaultSection = 'GENERAL' | 'PROGRAM' | 'REVIEWER' | 'ACCEPTANCE';
23
+ export type ReviewSection<RK extends string = RequirementKey> =
24
+ /** A custom panel showing exactly these requirements, in this order. */
25
+ {
26
+ title: string;
27
+ requirementKeys: RK[];
28
+ }
29
+ /** The panel for one of the program's workflow stages, titled by the stage. */
30
+ | {
31
+ workflowStage: string;
32
+ }
33
+ /** One of the default type-based panels, holding the requirements no custom panel claimed. */
34
+ | {
35
+ section: ReviewDefaultSection;
36
+ };
37
+ export interface ProgramDefinition<RK extends string = RequirementKey> {
14
38
  /**
15
39
  * The key of the program this decorates. Autocompletes and rejects unknown keys once the project
16
40
  * generates a key declaration; plain `string` until then.
@@ -20,6 +44,11 @@ export interface ProgramDefinition {
20
44
  * An icon to represent this program in the navigation.
21
45
  */
22
46
  icon?: Component;
47
+ /**
48
+ * Lay out the reviewer screen. Without this, the reviewer sees one panel per requirement type,
49
+ * in this order, each omitted when it has nothing to show
50
+ */
51
+ reviewSections?: ReviewSection<RK>[];
23
52
  }
24
53
  export interface RequirementDefinition {
25
54
  /**
@@ -187,8 +216,8 @@ export interface Terminologies {
187
216
  * `UIConfig<SimplePromptKey, SimpleRequirementKey, SimpleProgramKey>`.
188
217
  */
189
218
  export interface UIConfig<PK extends string = PromptKey, RK extends string = RequirementKey, GK extends string = ProgramKey> {
190
- programs: ProgramDefinition[] | {
191
- [K in GK]?: Omit<ProgramDefinition, 'key'>;
219
+ programs: ProgramDefinition<RK>[] | {
220
+ [K in GK]?: Omit<ProgramDefinition<RK>, 'key'>;
192
221
  };
193
222
  requirements: RequirementDefinition[] | {
194
223
  [K in RK]?: Omit<RequirementDefinition, 'key'>;
@@ -242,6 +271,11 @@ export interface UIConfig<PK extends string = PromptKey, RK extends string = Req
242
271
  */
243
272
  supportUrl?: string;
244
273
  extraNavItems?: LayoutStructureNodeRoot<LayoutStructureNode>[];
274
+ /**
275
+ * Extra entries for the user-profile menu in the header. Same shape as carbon-svelte's
276
+ * UIShell `profilelinks`. These appear before the built-in Logout entry, which is always present.
277
+ */
278
+ profileLinks?: ShellItem[];
245
279
  /**
246
280
  * This is where you should describe what kind of attributes app users will be able to search users by.
247
281
  * Displayed on user management page /roles/users.
@@ -333,6 +367,11 @@ export declare class UIRegistry {
333
367
  */
334
368
  config: AnyUIConfig;
335
369
  constructor(config: AnyUIConfig);
370
+ /**
371
+ * `reviewSections` is pure layout, so a mistake in it surfaces as a silently missing or doubled
372
+ * panel on the reviewer screen.
373
+ */
374
+ protected validateReviewSections(program: ProgramDefinition): void;
336
375
  getWord(key: keyof Terminologies, count?: number, inclusive?: boolean): string;
337
376
  getPlural(key: keyof Terminologies): string;
338
377
  getPrompt(key: string): PromptDefinition | undefined;
package/dist/registry.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { plural } from 'txstate-utils';
2
+ const reviewDefaultSections = ['GENERAL', 'PROGRAM', 'REVIEWER', 'ACCEPTANCE'];
2
3
  /**
3
4
  * Accepts either shape of a definition list and returns a key -> definition map. The keyed form
4
5
  * carries its key in the object key, so it is grafted back on to keep every consumer's
@@ -38,6 +39,8 @@ export class UIRegistry {
38
39
  this.promptMap = toDefinitionMap(config.prompts);
39
40
  this.requirementMap = toDefinitionMap(config.requirements);
40
41
  this.programMap = toDefinitionMap(config.programs);
42
+ for (const program of Object.values(this.programMap))
43
+ this.validateReviewSections(program);
41
44
  this.userLookup = config.userLookup;
42
45
  this.lang = {
43
46
  appRequest: config.terminology?.appRequest ?? (Object.keys(this.programMap).length > 1 ? 'App Request' : 'Application'),
@@ -49,6 +52,37 @@ export class UIRegistry {
49
52
  this.plural[key] = config.terminology?.plural?.[key] ?? plural(this.lang[key]);
50
53
  }
51
54
  }
55
+ /**
56
+ * `reviewSections` is pure layout, so a mistake in it surfaces as a silently missing or doubled
57
+ * panel on the reviewer screen.
58
+ */
59
+ validateReviewSections(program) {
60
+ const where = `[reqquest] reviewSections of program \`${program.key}\``;
61
+ const placed = new Set();
62
+ const usedStages = new Set();
63
+ const usedSections = new Set();
64
+ for (const entry of program.reviewSections ?? []) {
65
+ if ('requirementKeys' in entry) {
66
+ for (const requirementKey of entry.requirementKeys) {
67
+ if (placed.has(requirementKey))
68
+ console.error(`${where}: \`${requirementKey}\` is placed in more than one panel.`);
69
+ placed.add(requirementKey);
70
+ }
71
+ }
72
+ else if ('workflowStage' in entry) {
73
+ if (usedStages.has(entry.workflowStage))
74
+ console.error(`${where}: workflow stage \`${entry.workflowStage}\` is placed more than once.`);
75
+ usedStages.add(entry.workflowStage);
76
+ }
77
+ else {
78
+ if (!reviewDefaultSections.includes(entry.section))
79
+ console.error(`${where}: \`${String(entry.section)}\` is not a default section. Use one of ${reviewDefaultSections.join(', ')}.`);
80
+ if (usedSections.has(entry.section))
81
+ console.error(`${where}: default section \`${entry.section}\` is placed more than once.`);
82
+ usedSections.add(entry.section);
83
+ }
84
+ }
85
+ }
52
86
  getWord(key, count = 1, inclusive = false) {
53
87
  return (inclusive ? count + ' ' : '') + (count !== 1 ? this.plural[key] : this.lang[key]);
54
88
  }
@@ -8,8 +8,6 @@ export type Scalars = {
8
8
  String: string;
9
9
  };
10
10
  export interface Access {
11
- /** Current user is permitted to create announcements. */
12
- createAnnouncement: Scalars['Boolean'];
13
11
  /** Current user may create a new app request on behalf of another user and should be shown the Create App Request button on the reviewer dashboard or main app request list. */
14
12
  createAppRequestOther: Scalars['Boolean'];
15
13
  /** Current user may create a new app request for themselves and should be shown the Create App Request button. */
@@ -18,12 +16,12 @@ export interface Access {
18
16
  createPeriod: Scalars['Boolean'];
19
17
  /** Current user is permitted to create new roles in the role management UI. */
20
18
  createRole: Scalars['Boolean'];
21
- /** Current user is permitted to delete announcements. */
22
- deleteAnnouncement: Scalars['Boolean'];
23
- /** Current user is permitted to update announcements. */
24
- updateAnnouncement: Scalars['Boolean'];
19
+ /** Current user is permitted to manage site-wide announcements: create, update, and delete them. */
20
+ manageAnnouncements: Scalars['Boolean'];
25
21
  /** The current user, if any. */
26
22
  user: (AccessUser | null);
23
+ /** Current user is permitted to view the announcement management page, whether or not they may change anything. */
24
+ viewAnnouncementManagement: Scalars['Boolean'];
27
25
  /** Current user is permitted to view the app request list. */
28
26
  viewAppRequestList: Scalars['Boolean'];
29
27
  /** Current user is permitted to view the applicant dashboard. */
@@ -169,6 +167,8 @@ export interface Announcement {
169
167
  /** The announcement will not display after this date. Null means it displays until it is disabled. */
170
168
  end: (Scalars['DateTime'] | null);
171
169
  id: Scalars['ID'];
170
+ /** Whether the announcement should be displaying right now: enabled and inside whatever date bounds it has. */
171
+ isActive: Scalars['Boolean'];
172
172
  /** URL the announcement directs the user to for more information. */
173
173
  link: (Scalars['String'] | null);
174
174
  /** The text to display for the link. */
@@ -719,7 +719,7 @@ export interface Query {
719
719
  */
720
720
  access: Access;
721
721
  accessUsers: AccessUser[];
722
- /** Retrieve site-wide announcements. Applicants only ever see currently active announcements; users with the Announcement view control see them all. */
722
+ /** Retrieve site-wide announcements. Any authenticated user may retrieve any announcement, including ones that are disabled or outside their date range; pass filter.active to limit the result to the announcements that should be displaying right now. */
723
723
  announcements: Announcement[];
724
724
  /** The activity log for this app request. This is a list of actions taken on the app request, such as submission, updating prompts, make an offer, add a note, etc. It will be sorted by the date of the activity in descending order. */
725
725
  appRequestActivity: AppRequestActivity[];
@@ -767,7 +767,7 @@ export interface RequirementPrompt {
767
767
  moot: Scalars['Boolean'];
768
768
  /** A human readable title for the prompt in the navigation. You probably want it to be shorter than the full title. If not provided, the title will be used. */
769
769
  navTitle: Scalars['String'];
770
- /** True when this row exists only because its requirement listed the prompt in `promptKeysNoDisplay` - a dependency it reads but does not own. Such a row must not be rendered beneath that requirement; the prompt is displayed under the requirement that does own it. This is not derivable from `visibility`: a no-display row is UNREACHABLE, but so is a prompt whose own requirement resolved before reaching it, and that one is still the requirement's to show. */
770
+ /** True when this row exists only because its requirement listed the prompt in `promptKeysNoDisplay` */
771
771
  noDisplay: Scalars['Boolean'];
772
772
  optOut: Scalars['Boolean'];
773
773
  /** Preload data that has been generated according to the prompt definition. For example, a prompt might query the database for answers given in previous requests or query an external API to learn facts about the user. */
@@ -837,8 +837,6 @@ export interface ValidatedResponse {
837
837
  __typename: 'ValidatedResponse';
838
838
  }
839
839
  export interface AccessGenqlSelection {
840
- /** Current user is permitted to create announcements. */
841
- createAnnouncement?: boolean | number;
842
840
  /** Current user may create a new app request on behalf of another user and should be shown the Create App Request button on the reviewer dashboard or main app request list. */
843
841
  createAppRequestOther?: boolean | number;
844
842
  /** Current user may create a new app request for themselves and should be shown the Create App Request button. */
@@ -847,12 +845,12 @@ export interface AccessGenqlSelection {
847
845
  createPeriod?: boolean | number;
848
846
  /** Current user is permitted to create new roles in the role management UI. */
849
847
  createRole?: boolean | number;
850
- /** Current user is permitted to delete announcements. */
851
- deleteAnnouncement?: boolean | number;
852
- /** Current user is permitted to update announcements. */
853
- updateAnnouncement?: boolean | number;
848
+ /** Current user is permitted to manage site-wide announcements: create, update, and delete them. */
849
+ manageAnnouncements?: boolean | number;
854
850
  /** The current user, if any. */
855
851
  user?: AccessUserGenqlSelection;
852
+ /** Current user is permitted to view the announcement management page, whether or not they may change anything. */
853
+ viewAnnouncementManagement?: boolean | number;
856
854
  /** Current user is permitted to view the app request list. */
857
855
  viewAppRequestList?: boolean | number;
858
856
  /** Current user is permitted to view the applicant dashboard. */
@@ -1072,6 +1070,8 @@ export interface AnnouncementGenqlSelection {
1072
1070
  /** The announcement will not display after this date. Null means it displays until it is disabled. */
1073
1071
  end?: boolean | number;
1074
1072
  id?: boolean | number;
1073
+ /** Whether the announcement should be displaying right now: enabled and inside whatever date bounds it has. */
1074
+ isActive?: boolean | number;
1075
1075
  /** URL the announcement directs the user to for more information. */
1076
1076
  link?: boolean | number;
1077
1077
  /** The text to display for the link. */
@@ -1086,7 +1086,7 @@ export interface AnnouncementGenqlSelection {
1086
1086
  __scalar?: boolean | number;
1087
1087
  }
1088
1088
  export interface AnnouncementFilters {
1089
- /** Return only the announcements that should be displayed at this moment: enabled OR within their date range. */
1089
+ /** Return only the announcements that should be displaying right now (true) or only those that should not (false). Omit to return all announcements regardless of state. */
1090
1090
  active?: (Scalars['Boolean'] | null);
1091
1091
  /** Return only enabled (true) or only disabled (false) announcements. */
1092
1092
  enabled?: (Scalars['Boolean'] | null);
@@ -2018,7 +2018,7 @@ export interface QueryGenqlSelection {
2018
2018
  paged?: (Pagination | null);
2019
2019
  };
2020
2020
  });
2021
- /** Retrieve site-wide announcements. Applicants only ever see currently active announcements; users with the Announcement view control see them all. */
2021
+ /** Retrieve site-wide announcements. Any authenticated user may retrieve any announcement, including ones that are disabled or outside their date range; pass filter.active to limit the result to the announcements that should be displaying right now. */
2022
2022
  announcements?: (AnnouncementGenqlSelection & {
2023
2023
  __args?: {
2024
2024
  filter?: (AnnouncementFilters | null);
@@ -2124,7 +2124,7 @@ export interface RequirementPromptGenqlSelection {
2124
2124
  moot?: boolean | number;
2125
2125
  /** A human readable title for the prompt in the navigation. You probably want it to be shorter than the full title. If not provided, the title will be used. */
2126
2126
  navTitle?: boolean | number;
2127
- /** True when this row exists only because its requirement listed the prompt in `promptKeysNoDisplay` - a dependency it reads but does not own. Such a row must not be rendered beneath that requirement; the prompt is displayed under the requirement that does own it. This is not derivable from `visibility`: a no-display row is UNREACHABLE, but so is a prompt whose own requirement resolved before reaching it, and that one is still the requirement's to show. */
2127
+ /** True when this row exists only because its requirement listed the prompt in `promptKeysNoDisplay` */
2128
2128
  noDisplay?: boolean | number;
2129
2129
  optOut?: boolean | number;
2130
2130
  /** Preload data that has been generated according to the prompt definition. For example, a prompt might query the database for answers given in previous requests or query an external API to learn facts about the user. */
@@ -1,7 +1,4 @@
1
1
  type Access {
2
- """Current user is permitted to create announcements."""
3
- createAnnouncement: Boolean!
4
-
5
2
  """
6
3
  Current user may create a new app request on behalf of another user and should be shown the Create App Request button on the reviewer dashboard or main app request list.
7
4
  """
@@ -22,15 +19,19 @@ type Access {
22
19
  """
23
20
  createRole: Boolean!
24
21
 
25
- """Current user is permitted to delete announcements."""
26
- deleteAnnouncement: Boolean!
27
-
28
- """Current user is permitted to update announcements."""
29
- updateAnnouncement: Boolean!
22
+ """
23
+ Current user is permitted to manage site-wide announcements: create, update, and delete them.
24
+ """
25
+ manageAnnouncements: Boolean!
30
26
 
31
27
  """The current user, if any."""
32
28
  user: AccessUser
33
29
 
30
+ """
31
+ Current user is permitted to view the announcement management page, whether or not they may change anything.
32
+ """
33
+ viewAnnouncementManagement: Boolean!
34
+
34
35
  """Current user is permitted to view the app request list."""
35
36
  viewAppRequestList: Boolean!
36
37
 
@@ -303,6 +304,11 @@ type Announcement {
303
304
  end: DateTime
304
305
  id: ID!
305
306
 
307
+ """
308
+ Whether the announcement should be displaying right now: enabled and inside whatever date bounds it has.
309
+ """
310
+ isActive: Boolean!
311
+
306
312
  """URL the announcement directs the user to for more information."""
307
313
  link: String
308
314
 
@@ -323,7 +329,7 @@ type Announcement {
323
329
 
324
330
  input AnnouncementFilters {
325
331
  """
326
- Return only the announcements that should be displayed at this moment: enabled OR within their date range.
332
+ Return only the announcements that should be displaying right now (true) or only those that should not (false). Omit to return all announcements regardless of state.
327
333
  """
328
334
  active: Boolean
329
335
 
@@ -1831,7 +1837,7 @@ type Query {
1831
1837
  accessUsers(filter: AccessUserFilter, paged: Pagination): [AccessUser!]!
1832
1838
 
1833
1839
  """
1834
- Retrieve site-wide announcements. Applicants only ever see currently active announcements; users with the Announcement view control see them all.
1840
+ Retrieve site-wide announcements. Any authenticated user may retrieve any announcement, including ones that are disabled or outside their date range; pass filter.active to limit the result to the announcements that should be displaying right now.
1835
1841
  """
1836
1842
  announcements(filter: AnnouncementFilters): [Announcement!]!
1837
1843
 
@@ -1956,7 +1962,7 @@ type RequirementPrompt {
1956
1962
  navTitle: String!
1957
1963
 
1958
1964
  """
1959
- True when this row exists only because its requirement listed the prompt in `promptKeysNoDisplay` - a dependency it reads but does not own. Such a row must not be rendered beneath that requirement; the prompt is displayed under the requirement that does own it. This is not derivable from `visibility`: a no-display row is UNREACHABLE, but so is a prompt whose own requirement resolved before reaching it, and that one is still the requirement's to show.
1965
+ True when this row exists only because its requirement listed the prompt in `promptKeysNoDisplay`
1960
1966
  """
1961
1967
  noDisplay: Boolean!
1962
1968
  optOut: Boolean!
@@ -2,14 +2,13 @@ declare const _default: {
2
2
  scalars: number[];
3
3
  types: {
4
4
  Access: {
5
- createAnnouncement: number[];
6
5
  createAppRequestOther: number[];
7
6
  createAppRequestSelf: number[];
8
7
  createPeriod: number[];
9
8
  createRole: number[];
10
- deleteAnnouncement: number[];
11
- updateAnnouncement: number[];
9
+ manageAnnouncements: number[];
12
10
  user: number[];
11
+ viewAnnouncementManagement: number[];
13
12
  viewAppRequestList: number[];
14
13
  viewApplicantDashboard: number[];
15
14
  viewMetrics: number[];
@@ -168,6 +167,7 @@ declare const _default: {
168
167
  enabled: number[];
169
168
  end: number[];
170
169
  id: number[];
170
+ isActive: number[];
171
171
  link: number[];
172
172
  linkText: number[];
173
173
  start: number[];
@@ -21,9 +21,6 @@ export default {
21
21
  ],
22
22
  "types": {
23
23
  "Access": {
24
- "createAnnouncement": [
25
- 45
26
- ],
27
24
  "createAppRequestOther": [
28
25
  45
29
26
  ],
@@ -36,15 +33,15 @@ export default {
36
33
  "createRole": [
37
34
  45
38
35
  ],
39
- "deleteAnnouncement": [
40
- 45
41
- ],
42
- "updateAnnouncement": [
36
+ "manageAnnouncements": [
43
37
  45
44
38
  ],
45
39
  "user": [
46
40
  17
47
41
  ],
42
+ "viewAnnouncementManagement": [
43
+ 45
44
+ ],
48
45
  "viewAppRequestList": [
49
46
  45
50
47
  ],
@@ -431,6 +428,9 @@ export default {
431
428
  "id": [
432
429
  53
433
430
  ],
431
+ "isActive": [
432
+ 45
433
+ ],
434
434
  "link": [
435
435
  89
436
436
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reqquest/ui",
3
- "version": "1.12.1",
3
+ "version": "1.14.0",
4
4
  "overrides": {
5
5
  "esrap": "2.3.2"
6
6
  },
@@ -23,6 +23,8 @@
23
23
  "prepublishOnly": "npm run package",
24
24
  "check": "svelte-check --tsconfig ./tsconfig.json",
25
25
  "check:registry": "node check-registry.js",
26
+ "docs:components": "node scripts/build-components-md.js",
27
+ "docs:components:check": "node scripts/build-components-md.js --check",
26
28
  "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch"
27
29
  },
28
30
  "peerDependencies": {