@atelic-action/artifact 4.2.0 → 5.0.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/README.md +18 -17
- package/package.json +4 -4
- package/src/documents/FirstCall.tsx +6 -6
- package/src/documents/Note.tsx +2 -2
- package/src/documents/Report.tsx +2 -2
- package/src/documents/{Read.tsx → TheRead.tsx} +258 -82
- package/src/documents/{Writeup.tsx → Topic.tsx} +59 -362
- package/src/documents/documents.css +37 -35
- package/src/documents/index.ts +24 -2
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ArtifactLink,
|
|
3
3
|
type ArtifactParagraph,
|
|
4
|
+
GradeChip,
|
|
5
|
+
gradeLabel,
|
|
4
6
|
isEmpty,
|
|
5
7
|
newTabProps,
|
|
6
8
|
Paragraph,
|
|
@@ -12,36 +14,39 @@ import {
|
|
|
12
14
|
import { IconList, ReportCard, type Tone } from "@atelic-action/ui/components";
|
|
13
15
|
import { ArrowUpRight, Check } from "lucide-react";
|
|
14
16
|
import type { ReactNode } from "react";
|
|
15
|
-
import {
|
|
17
|
+
import { isRuns, type ReadTopic, Topic } from "./Topic";
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
|
-
* The read:
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
20
|
+
* The read: the graded document a business gets from the practice, built from
|
|
21
|
+
* one snapshot. Its shape is the practice's FOOTPRINT.md, under What the
|
|
22
|
+
* Owner Gets: a title and a dated lede; the scorecard (every section under
|
|
23
|
+
* its tier, its grade, and one line, with the earlier letter beside it on a
|
|
24
|
+
* later read), with a note over it for what the read leaves out; the plan in
|
|
25
|
+
* order; then the tiers, each section the card in Topic.tsx (the trade name
|
|
26
|
+
* linked to its definition, the one liner, the grade, the why line as the
|
|
27
|
+
* summary of what we saw, the blocks, and the sources at the foot) plus what
|
|
28
|
+
* is the read's own, today and after and what we recommend; a tier may open
|
|
29
|
+
* on a table of letters per market and close on the paid facts line, which
|
|
30
|
+
* is never graded; then one closing line and the sign off. A beat with
|
|
31
|
+
* nothing in it is left out.
|
|
25
32
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* and what we recommend as a checklist; and the tools we looked through at
|
|
33
|
-
* the foot, as the sources. A beat with nothing in it is left out.
|
|
33
|
+
* The same page at two depths, and `access` says which. A public read is
|
|
34
|
+
* built from public readings alone and may be hosted open; a private one
|
|
35
|
+
* adds what the owner's own consoles show and deploys only behind the wall.
|
|
36
|
+
* The component draws whatever beats the content carries, so the depth
|
|
37
|
+
* lives in the content and `access` is the declaration the host is checked
|
|
38
|
+
* against (the content test, and `access` in site.config.ts).
|
|
34
39
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* says so under the table. Every prop but a finding's body and the sign off
|
|
41
|
-
* person's `role` is plain data, and both are equally happy with a string, so
|
|
42
|
-
* a routine can write a read as JSON.
|
|
40
|
+
* Every prop but a finding's body, a why line, and the sign off person's
|
|
41
|
+
* `role` is plain data, and all three are equally happy with a string, so a
|
|
42
|
+
* routine can write a read as JSON. `print` is the printed copy's own: the
|
|
43
|
+
* sender and recipient lockup where the header was, and the pointer to the
|
|
44
|
+
* hosted twin with its QR under the sign off.
|
|
43
45
|
*/
|
|
44
46
|
|
|
47
|
+
/** Whether anything on the page came from the owner's own consoles. */
|
|
48
|
+
export type ReadAccess = "public" | "private";
|
|
49
|
+
|
|
45
50
|
/** A link inside running text: the words the reader clicks and where they go. */
|
|
46
51
|
export type ReadLink = ArtifactLink;
|
|
47
52
|
|
|
@@ -55,13 +60,15 @@ export interface ReadChange {
|
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
/**
|
|
58
|
-
* One section of a tier: the
|
|
59
|
-
* `
|
|
60
|
-
*
|
|
63
|
+
* One section of a tier: the card (its `title`, `href`, `oneLiner`, `grade`,
|
|
64
|
+
* `why`, `blocks`, and `sources`), plus the scorecard's line, the earlier
|
|
65
|
+
* letter, today and after, and what we recommend.
|
|
61
66
|
*/
|
|
62
|
-
export interface ReadSection extends Omit<
|
|
63
|
-
/** The one line
|
|
67
|
+
export interface ReadSection extends Omit<ReadTopic, "why"> {
|
|
68
|
+
/** The one line on the scorecard. */
|
|
64
69
|
gist: ReadParagraph;
|
|
70
|
+
/** The grade in the earlier snapshot, on a later read: what moved. */
|
|
71
|
+
before?: WriteupGrade;
|
|
65
72
|
/** The summary of what we saw, in one or two sentences: the why line. A paragraph, so a read written as JSON can carry links in it. */
|
|
66
73
|
why?: ReactNode | ReadParagraph;
|
|
67
74
|
/** Today and after: what each thing is, and what it becomes. Set after the blocks. */
|
|
@@ -80,6 +87,29 @@ export interface ReadRecommendation {
|
|
|
80
87
|
tone?: Tone;
|
|
81
88
|
}
|
|
82
89
|
|
|
90
|
+
export interface ReadMarketRow {
|
|
91
|
+
market: string;
|
|
92
|
+
place?: string;
|
|
93
|
+
/** One grade per column, in column order; null draws a quiet dash for a column not read. */
|
|
94
|
+
grades: Array<WriteupGrade | null>;
|
|
95
|
+
read: ReactNode | ReadParagraph;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The markets table: one row per market or site, one letter per column, the read beside it. */
|
|
99
|
+
export interface ReadMarkets {
|
|
100
|
+
title: string;
|
|
101
|
+
lead?: string;
|
|
102
|
+
columns: string[];
|
|
103
|
+
rows: ReadMarketRow[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The paid facts line: what is rented, never graded (FOOTPRINT.md, Paid). */
|
|
107
|
+
export interface ReadRented {
|
|
108
|
+
/** The word before the line; defaults to the `rented` label. */
|
|
109
|
+
label?: string;
|
|
110
|
+
text: ReactNode | ReadParagraph;
|
|
111
|
+
}
|
|
112
|
+
|
|
83
113
|
/** A tier, e.g. Health: its heading, the question it answers, and its sections in order. */
|
|
84
114
|
export interface ReadTier {
|
|
85
115
|
/** The anchor a header stop points at, e.g. "health". */
|
|
@@ -90,7 +120,11 @@ export interface ReadTier {
|
|
|
90
120
|
href?: string;
|
|
91
121
|
/** The questions the tier answers, in the owner's words. */
|
|
92
122
|
lead?: string;
|
|
123
|
+
/** A table of letters per market or site, over the sections. */
|
|
124
|
+
markets?: ReadMarkets;
|
|
93
125
|
sections: ReadSection[];
|
|
126
|
+
/** The paid facts line, under the sections. Visibility's, by FOOTPRINT.md. */
|
|
127
|
+
rented?: ReadRented;
|
|
94
128
|
}
|
|
95
129
|
|
|
96
130
|
/** One step of the plan: a bold title and a sentence or two. */
|
|
@@ -117,21 +151,35 @@ export interface ReadPlan {
|
|
|
117
151
|
/** The close: one line, then the sign off. */
|
|
118
152
|
export interface ReadClose {
|
|
119
153
|
/** One sentence, e.g. what follows and when. */
|
|
120
|
-
line
|
|
154
|
+
line?: ReadParagraph;
|
|
121
155
|
signOff?: string;
|
|
122
156
|
person?: WriteupPerson;
|
|
123
157
|
}
|
|
124
158
|
|
|
159
|
+
/** The printed copy's own: the lockup where the header was, and the pointer to the hosted twin. */
|
|
160
|
+
export interface ReadPrint {
|
|
161
|
+
/** The sender's wordmark text, e.g. "atelic"; drawn with the orange run. */
|
|
162
|
+
sender: { name: string; href?: string };
|
|
163
|
+
/** The recipient's logo, lockup right of the sender. */
|
|
164
|
+
recipient: { logoSrc: string; logoAlt: string };
|
|
165
|
+
/** The pointer to the hosted page under the sign off, with its QR. */
|
|
166
|
+
twin?: { line: string; domain: string; qrSrc: string };
|
|
167
|
+
}
|
|
168
|
+
|
|
125
169
|
/** The fixed words the page sets, each overridable. */
|
|
126
170
|
export interface ReadLabels {
|
|
127
171
|
/** The dateline's words before the business, e.g. "Prepared for". */
|
|
128
172
|
preparedFor: string;
|
|
129
|
-
/** What a screen reader calls the
|
|
173
|
+
/** What a screen reader calls the scorecard when it has no title. */
|
|
130
174
|
gist: string;
|
|
131
|
-
/** What a screen reader hears before a letter, on the
|
|
175
|
+
/** What a screen reader hears before a letter, on the scorecard and each section's card. */
|
|
132
176
|
grade: string;
|
|
133
177
|
/** What a screen reader hears for a section with no grade. */
|
|
134
178
|
notGraded: string;
|
|
179
|
+
/** What a screen reader calls the earlier letter on the scorecard, e.g. "Before" or "June 2026". */
|
|
180
|
+
before: string;
|
|
181
|
+
/** The word before the paid facts line when it names none. */
|
|
182
|
+
rented: string;
|
|
135
183
|
/** The today and after table's first column. */
|
|
136
184
|
now: string;
|
|
137
185
|
/** The today and after table's second column. */
|
|
@@ -144,6 +192,8 @@ export interface ReadLabels {
|
|
|
144
192
|
sources: string;
|
|
145
193
|
/** The plan's heading when it names none. */
|
|
146
194
|
plan: string;
|
|
195
|
+
/** The note over the scorecard on a public read that sets none: what the read leaves out. */
|
|
196
|
+
publicNote: string;
|
|
147
197
|
}
|
|
148
198
|
|
|
149
199
|
export const readLabels: ReadLabels = {
|
|
@@ -151,15 +201,21 @@ export const readLabels: ReadLabels = {
|
|
|
151
201
|
gist: "The Gist",
|
|
152
202
|
grade: "Grade",
|
|
153
203
|
notGraded: "Not graded yet",
|
|
204
|
+
before: "Before",
|
|
205
|
+
rented: "Rented",
|
|
154
206
|
now: "Today",
|
|
155
207
|
after: "After",
|
|
156
208
|
recommend: "Recommendations",
|
|
157
209
|
why: "Why",
|
|
158
210
|
sources: "See it yourself",
|
|
159
211
|
plan: "The Plan",
|
|
212
|
+
publicNote:
|
|
213
|
+
"This page is read from the outside, through what anyone can open. A customer's read adds what the owner's own consoles show.",
|
|
160
214
|
};
|
|
161
215
|
|
|
162
|
-
export interface
|
|
216
|
+
export interface TheReadProps {
|
|
217
|
+
/** Whether anything on the page came from the owner's own consoles; a private read deploys only behind the wall. */
|
|
218
|
+
access: ReadAccess;
|
|
163
219
|
title: string;
|
|
164
220
|
/** The business, set in the dateline. */
|
|
165
221
|
preparedFor: string;
|
|
@@ -169,19 +225,21 @@ export interface ReadProps {
|
|
|
169
225
|
lede: ReadParagraph;
|
|
170
226
|
/** The heading over the scorecard, e.g. "The Scorecard". */
|
|
171
227
|
gistTitle?: string;
|
|
172
|
-
/** The line under the scorecard's heading, e.g. what it is. */
|
|
228
|
+
/** The line under the scorecard's heading, e.g. what it is. Set only with a `gistTitle`, since it reads as the heading's subtitle. */
|
|
173
229
|
gistLead?: string;
|
|
174
|
-
/** The anchor a header stop points at for the scorecard, e.g. "scorecard". */
|
|
230
|
+
/** The anchor a header stop points at for the scorecard, e.g. "scorecard": on the heading with a `gistTitle`, on the card itself without one. */
|
|
175
231
|
gistId?: string;
|
|
176
|
-
/** A callout over the scorecard, under its heading, e.g. what the read leaves out and when it follows. */
|
|
232
|
+
/** A callout over the scorecard, under its heading, e.g. what the read leaves out and when it follows. A public read that sets none gets the `publicNote` label. */
|
|
177
233
|
note?: ReadParagraph;
|
|
178
234
|
/** The key: what each letter means, in a few words each. */
|
|
179
235
|
gradeKey?: Array<{ grade: WriteupGrade; label: string }>;
|
|
180
|
-
/** The tiers in order. Each section appears
|
|
236
|
+
/** The tiers in order. Each section appears on the scorecard and in the body. */
|
|
181
237
|
tiers: ReadTier[];
|
|
182
238
|
plan: ReadPlan;
|
|
183
239
|
close: ReadClose;
|
|
184
240
|
labels?: Partial<ReadLabels>;
|
|
241
|
+
/** The printed copy's lockup and twin; the page prints without them when left out. */
|
|
242
|
+
print?: ReadPrint;
|
|
185
243
|
}
|
|
186
244
|
|
|
187
245
|
/** A paragraph with something to read. Content may arrive as JSON, so a missing one counts as empty. */
|
|
@@ -189,19 +247,6 @@ function filled(paragraph: ReadParagraph | undefined | null): paragraph is ReadP
|
|
|
189
247
|
return paragraph !== undefined && paragraph !== null && !isEmpty(paragraph);
|
|
190
248
|
}
|
|
191
249
|
|
|
192
|
-
/** A string or runs of text and links, the shape JSON content arrives in; a node of elements is anything else. */
|
|
193
|
-
function isRuns(value: unknown): value is ReadParagraph {
|
|
194
|
-
if (typeof value === "string") return true;
|
|
195
|
-
return (
|
|
196
|
-
Array.isArray(value) &&
|
|
197
|
-
value.every(
|
|
198
|
-
(run) =>
|
|
199
|
-
typeof run === "string" ||
|
|
200
|
-
(typeof run === "object" && run !== null && "text" in run && "href" in run),
|
|
201
|
-
)
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
250
|
/** Content as a node: runs are rendered, a blank is dropped, and a node passes through. */
|
|
206
251
|
function asNode(value: ReactNode | ReadParagraph | undefined | null): ReactNode {
|
|
207
252
|
if (value === undefined || value === null) return undefined;
|
|
@@ -276,8 +321,8 @@ function Gist({
|
|
|
276
321
|
id?: string;
|
|
277
322
|
title?: string;
|
|
278
323
|
lead?: string;
|
|
279
|
-
note?:
|
|
280
|
-
gradeKey?:
|
|
324
|
+
note?: ReadParagraph;
|
|
325
|
+
gradeKey?: TheReadProps["gradeKey"];
|
|
281
326
|
labels: ReadLabels;
|
|
282
327
|
}) {
|
|
283
328
|
return (
|
|
@@ -294,17 +339,19 @@ function Gist({
|
|
|
294
339
|
</div>
|
|
295
340
|
)}
|
|
296
341
|
<ReportCard
|
|
342
|
+
id={title ? undefined : id}
|
|
297
343
|
className="rd-gist"
|
|
298
344
|
label={title ?? labels.gist}
|
|
299
345
|
legend={gradeKey}
|
|
300
346
|
legendAt="foot"
|
|
301
|
-
labels={{ grade: labels.grade, notGraded: labels.notGraded }}
|
|
347
|
+
labels={{ grade: labels.grade, notGraded: labels.notGraded, before: labels.before }}
|
|
302
348
|
groups={tiers.map((tier) => ({
|
|
303
349
|
title: tier.title,
|
|
304
350
|
rows: tier.sections.map((section) => ({
|
|
305
351
|
name: section.title,
|
|
306
352
|
href: `#${section.id}`,
|
|
307
353
|
grade: section.grade,
|
|
354
|
+
before: section.before,
|
|
308
355
|
line: <Runs paragraph={section.gist} />,
|
|
309
356
|
})),
|
|
310
357
|
}))}
|
|
@@ -313,7 +360,7 @@ function Gist({
|
|
|
313
360
|
);
|
|
314
361
|
}
|
|
315
362
|
|
|
316
|
-
/** A section: the
|
|
363
|
+
/** A section: the card, with today and after and the checklist handed in after its blocks. */
|
|
317
364
|
function Section({ section, labels }: { section: ReadSection; labels: ReadLabels }) {
|
|
318
365
|
const changes = (section.changes ?? []).filter(
|
|
319
366
|
(change) => filled(change?.now) || filled(change?.after),
|
|
@@ -333,16 +380,21 @@ function Section({ section, labels }: { section: ReadSection; labels: ReadLabels
|
|
|
333
380
|
: block,
|
|
334
381
|
);
|
|
335
382
|
const sources = (section.sources ?? []).filter((source) => source?.label?.trim());
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
383
|
+
const {
|
|
384
|
+
gist: _gist,
|
|
385
|
+
before: _before,
|
|
386
|
+
changes: _changes,
|
|
387
|
+
recommend: _recommend,
|
|
388
|
+
...card
|
|
389
|
+
} = section;
|
|
390
|
+
const topic: ReadTopic = {
|
|
391
|
+
...card,
|
|
339
392
|
why: asNode(section.why),
|
|
340
393
|
whyLabel: section.whyLabel ?? labels.why,
|
|
341
394
|
gradeWord: section.gradeWord ?? labels.grade,
|
|
342
395
|
blocks,
|
|
343
396
|
sources,
|
|
344
397
|
sourcesLabel: section.sourcesLabel ?? labels.sources,
|
|
345
|
-
noShot: section.noShot ?? true,
|
|
346
398
|
};
|
|
347
399
|
const extra =
|
|
348
400
|
changes.length + recommend.length > 0 ? (
|
|
@@ -354,6 +406,79 @@ function Section({ section, labels }: { section: ReadSection; labels: ReadLabels
|
|
|
354
406
|
return <Topic topic={topic} extra={extra} />;
|
|
355
407
|
}
|
|
356
408
|
|
|
409
|
+
function Markets({ markets }: { markets: ReadMarkets }) {
|
|
410
|
+
return (
|
|
411
|
+
<section className="op-topic op-markets">
|
|
412
|
+
<header className="op-topic-head">
|
|
413
|
+
<div>
|
|
414
|
+
<h3>{markets.title}</h3>
|
|
415
|
+
{markets.lead && <p className="op-def">{markets.lead}</p>}
|
|
416
|
+
</div>
|
|
417
|
+
</header>
|
|
418
|
+
<table className="op-markets-table">
|
|
419
|
+
<thead>
|
|
420
|
+
<tr>
|
|
421
|
+
<th scope="col" className="op-markets-market">
|
|
422
|
+
<span className="op-sr-only">Market</span>
|
|
423
|
+
</th>
|
|
424
|
+
{markets.columns.map((column) => (
|
|
425
|
+
<th scope="col" className="op-markets-col" key={column}>
|
|
426
|
+
{column}
|
|
427
|
+
</th>
|
|
428
|
+
))}
|
|
429
|
+
<th scope="col" className="op-markets-read">
|
|
430
|
+
<span className="op-sr-only">Read</span>
|
|
431
|
+
</th>
|
|
432
|
+
</tr>
|
|
433
|
+
</thead>
|
|
434
|
+
<tbody>
|
|
435
|
+
{markets.rows.map((row) => (
|
|
436
|
+
<tr key={row.market}>
|
|
437
|
+
<th scope="row" className="op-markets-market">
|
|
438
|
+
{row.market}
|
|
439
|
+
{row.place && <span className="op-markets-place">{row.place}</span>}
|
|
440
|
+
</th>
|
|
441
|
+
{markets.columns.map((column, i) => {
|
|
442
|
+
const grade = row.grades[i] ?? null;
|
|
443
|
+
return (
|
|
444
|
+
<td className="op-markets-grade" key={column}>
|
|
445
|
+
<span className="op-markets-label" aria-hidden="true">
|
|
446
|
+
{column}
|
|
447
|
+
</span>
|
|
448
|
+
{grade ? (
|
|
449
|
+
<>
|
|
450
|
+
<span className="op-sr-only">{`${column}, grade ${gradeLabel[grade]}`}</span>
|
|
451
|
+
<span aria-hidden="true">
|
|
452
|
+
<GradeChip grade={grade} />
|
|
453
|
+
</span>
|
|
454
|
+
</>
|
|
455
|
+
) : (
|
|
456
|
+
<>
|
|
457
|
+
<span className="op-sr-only">{`${column}, not read`}</span>
|
|
458
|
+
<span className="op-dash" aria-hidden="true" />
|
|
459
|
+
</>
|
|
460
|
+
)}
|
|
461
|
+
</td>
|
|
462
|
+
);
|
|
463
|
+
})}
|
|
464
|
+
<td className="op-markets-read">{asNode(row.read)}</td>
|
|
465
|
+
</tr>
|
|
466
|
+
))}
|
|
467
|
+
</tbody>
|
|
468
|
+
</table>
|
|
469
|
+
</section>
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function Rented({ rented, label }: { rented: ReadRented; label: string }) {
|
|
474
|
+
return (
|
|
475
|
+
<p className="op-rented">
|
|
476
|
+
<span className="op-rented-label">{rented.label ?? label}</span>
|
|
477
|
+
<span className="op-rented-text">{asNode(rented.text)}</span>
|
|
478
|
+
</p>
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
357
482
|
function Plan({ plan, labels }: { plan: ReadPlan; labels: ReadLabels }) {
|
|
358
483
|
return (
|
|
359
484
|
<section className="rd-plan" id={plan.id}>
|
|
@@ -387,7 +512,37 @@ function Plan({ plan, labels }: { plan: ReadPlan; labels: ReadLabels }) {
|
|
|
387
512
|
);
|
|
388
513
|
}
|
|
389
514
|
|
|
390
|
-
|
|
515
|
+
function Tier({ tier, labels }: { tier: ReadTier; labels: ReadLabels }) {
|
|
516
|
+
const rented =
|
|
517
|
+
tier.rented && (isRuns(tier.rented.text) ? filled(tier.rented.text) : tier.rented.text != null)
|
|
518
|
+
? tier.rented
|
|
519
|
+
: undefined;
|
|
520
|
+
return (
|
|
521
|
+
<div className="rd-tier">
|
|
522
|
+
<div className="op-part" id={tier.id}>
|
|
523
|
+
<h2>
|
|
524
|
+
{tier.href ? (
|
|
525
|
+
<a href={tier.href} {...newTabProps(tier.href, true)}>
|
|
526
|
+
{tier.title}
|
|
527
|
+
<ArrowUpRight size={14} aria-hidden="true" />
|
|
528
|
+
</a>
|
|
529
|
+
) : (
|
|
530
|
+
tier.title
|
|
531
|
+
)}
|
|
532
|
+
</h2>
|
|
533
|
+
{tier.lead && <p>{tier.lead}</p>}
|
|
534
|
+
</div>
|
|
535
|
+
{tier.markets && tier.markets.rows.length > 0 && <Markets markets={tier.markets} />}
|
|
536
|
+
{tier.sections.map((section) => (
|
|
537
|
+
<Section section={section} labels={labels} key={section.id} />
|
|
538
|
+
))}
|
|
539
|
+
{rented && <Rented rented={rented} label={labels.rented} />}
|
|
540
|
+
</div>
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export function TheRead({
|
|
545
|
+
access,
|
|
391
546
|
title,
|
|
392
547
|
preparedFor,
|
|
393
548
|
date,
|
|
@@ -401,13 +556,36 @@ export function Read({
|
|
|
401
556
|
plan,
|
|
402
557
|
close,
|
|
403
558
|
labels: overrides,
|
|
404
|
-
|
|
559
|
+
print,
|
|
560
|
+
}: TheReadProps) {
|
|
405
561
|
const labels = { ...readLabels, ...overrides };
|
|
406
562
|
const shownTiers = tiers.filter((tier) => tier.sections.length > 0);
|
|
563
|
+
const shownNote = filled(note) ? note : access === "public" ? labels.publicNote : undefined;
|
|
564
|
+
const signOff = Boolean(close.signOff && close.person);
|
|
407
565
|
return (
|
|
408
|
-
<div className="op rd">
|
|
566
|
+
<div className="op rd" data-access={access}>
|
|
409
567
|
<article className="op-doc rd-doc">
|
|
410
568
|
<header className="op-head">
|
|
569
|
+
{print && (
|
|
570
|
+
<div className="op-top">
|
|
571
|
+
<span className="op-mark">
|
|
572
|
+
{print.sender.href ? (
|
|
573
|
+
<a href={print.sender.href}>{print.sender.name}</a>
|
|
574
|
+
) : (
|
|
575
|
+
print.sender.name
|
|
576
|
+
)}
|
|
577
|
+
<span className="op-run" aria-hidden="true" />
|
|
578
|
+
</span>
|
|
579
|
+
<span className="op-x" aria-hidden="true">
|
|
580
|
+
×
|
|
581
|
+
</span>
|
|
582
|
+
<img
|
|
583
|
+
className="op-recipient"
|
|
584
|
+
src={print.recipient.logoSrc}
|
|
585
|
+
alt={print.recipient.logoAlt}
|
|
586
|
+
/>
|
|
587
|
+
</div>
|
|
588
|
+
)}
|
|
411
589
|
<p className="op-meta">
|
|
412
590
|
{labels.preparedFor} <b>{preparedFor}</b>
|
|
413
591
|
<span className="op-meta-sep" aria-hidden="true">
|
|
@@ -424,36 +602,34 @@ export function Read({
|
|
|
424
602
|
id={gistId}
|
|
425
603
|
title={gistTitle}
|
|
426
604
|
lead={gistLead}
|
|
427
|
-
note={
|
|
605
|
+
note={shownNote}
|
|
428
606
|
gradeKey={gradeKey}
|
|
429
607
|
labels={labels}
|
|
430
608
|
/>
|
|
431
609
|
)}
|
|
432
610
|
{plan.steps.length > 0 && <Plan plan={plan} labels={labels} />}
|
|
433
611
|
{shownTiers.map((tier) => (
|
|
434
|
-
<
|
|
435
|
-
<div className="op-part" id={tier.id}>
|
|
436
|
-
<h2>
|
|
437
|
-
{tier.href ? (
|
|
438
|
-
<a href={tier.href} {...newTabProps(tier.href, true)}>
|
|
439
|
-
{tier.title}
|
|
440
|
-
<ArrowUpRight size={14} aria-hidden="true" />
|
|
441
|
-
</a>
|
|
442
|
-
) : (
|
|
443
|
-
tier.title
|
|
444
|
-
)}
|
|
445
|
-
</h2>
|
|
446
|
-
{tier.lead && <p>{tier.lead}</p>}
|
|
447
|
-
</div>
|
|
448
|
-
{tier.sections.map((section) => (
|
|
449
|
-
<Section section={section} labels={labels} key={section.id} />
|
|
450
|
-
))}
|
|
451
|
-
</div>
|
|
612
|
+
<Tier tier={tier} labels={labels} key={tier.title} />
|
|
452
613
|
))}
|
|
453
614
|
{filled(close.line) && <Paragraph paragraph={close.line} className="rd-closing" />}
|
|
454
|
-
{
|
|
615
|
+
{(signOff || print?.twin) && (
|
|
455
616
|
<footer className="op-close rd-close">
|
|
456
|
-
|
|
617
|
+
{signOff && close.signOff && close.person && (
|
|
618
|
+
<SignOff signOff={close.signOff} person={close.person} />
|
|
619
|
+
)}
|
|
620
|
+
{print?.twin && (
|
|
621
|
+
<div className="op-twin">
|
|
622
|
+
<div className="op-twin-text">
|
|
623
|
+
{print.twin.line}
|
|
624
|
+
<b>{print.twin.domain}</b>
|
|
625
|
+
</div>
|
|
626
|
+
<img
|
|
627
|
+
className="op-qr"
|
|
628
|
+
src={print.twin.qrSrc}
|
|
629
|
+
alt={`QR code for ${print.twin.domain}`}
|
|
630
|
+
/>
|
|
631
|
+
</div>
|
|
632
|
+
)}
|
|
457
633
|
</footer>
|
|
458
634
|
)}
|
|
459
635
|
</article>
|