@simmalugnt-se/payload-content-health 0.5.1 → 0.6.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/CHANGELOG.md +22 -0
- package/README.md +17 -9
- package/dist/categories.d.ts +21 -0
- package/dist/categories.js +43 -0
- package/dist/copy.d.ts +12 -17
- package/dist/copy.js +19 -65
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/links.d.ts +6 -0
- package/dist/links.js +8 -2
- package/dist/scan.js +3 -3
- package/dist/styles/tokens.css +26 -0
- package/dist/styles/view.css +166 -64
- package/dist/styles/widget.css +71 -85
- package/dist/view/ContentHealthView.d.ts +4 -3
- package/dist/view/ContentHealthView.js +33 -10
- package/dist/view/WorkList.d.ts +12 -5
- package/dist/view/WorkList.js +56 -14
- package/dist/widget/ContentHealthWidget.d.ts +3 -2
- package/dist/widget/ContentHealthWidget.js +16 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
- The dashboard widget is a summary: a ring for the share in order, and one row per category (SEO,
|
|
6
|
+
Structure, Translations, Accessibility) with a bar and "N to fix". It no longer lists documents;
|
|
7
|
+
each row opens the work list on its category, and "Open details" opens the list.
|
|
8
|
+
- The work list shows one category at a time as tabs, with the number of findings on each and the
|
|
9
|
+
category with most to fix open first. Under the tabs, every check has its own section with the
|
|
10
|
+
documents that need work, and its own "Fix N with the assistant" next to the heading. The check
|
|
11
|
+
filter and "Clear filters" are gone; collection and locale remain as selects where there is more
|
|
12
|
+
than one. `?category=` is the new address parameter; `?check=` still opens that check's category.
|
|
13
|
+
- Red and amber mark what needs work, next to the blue accent. Colors are in `styles/tokens.css`.
|
|
14
|
+
- `CATEGORY_IDS`, `categoryOf` and `categoryStats` are exported.
|
|
15
|
+
|
|
16
|
+
## 0.5.2
|
|
17
|
+
|
|
18
|
+
- Work-list filters use Payload's ReactSelect with its standard menu and focus styling.
|
|
19
|
+
Filters, Clear filters and individual and batch assistant buttons share one control height.
|
|
20
|
+
The filter row fills the available width, with Clear filters aligned to the batch action below.
|
|
21
|
+
- The workspace dev app now includes content health alongside the editor assistant and visual
|
|
22
|
+
editing, with alt text, SEO, heading, thin content and translation checks on its seed data.
|
|
23
|
+
- Source builds typecheck with the host app's generated collection and locale types.
|
|
24
|
+
|
|
3
25
|
## 0.5.1
|
|
4
26
|
|
|
5
27
|
- The assistant's button now shows in the header of the work list. The view did not pass Admin's
|
package/README.md
CHANGED
|
@@ -112,14 +112,22 @@ The plugin registers the widget `content-health`. When the project sets no
|
|
|
112
112
|
widget. Payload keeps each editor's own layout as a preference once they edit the dashboard; they add
|
|
113
113
|
the widget from the dashboard's edit mode, and can limit it to some checks in its settings.
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
The widget is a summary: the share of checked values in order, and one row per category with a bar
|
|
116
|
+
and how much there is to fix. Categories group the checks: SEO (meta titles and descriptions, their
|
|
117
|
+
lengths, share images), Structure (headings, page length), Translations and Accessibility (alt
|
|
118
|
+
texts). A category none of the configured checks belong to is left out. Each row opens the work list
|
|
119
|
+
on that category, and "Open details" opens the list itself.
|
|
120
|
+
|
|
121
|
+
Pass `addToDefaultLayout: false` to only register the widget.
|
|
117
122
|
|
|
118
123
|
## Work list
|
|
119
124
|
|
|
120
|
-
`/admin/content-health` (linked at the top of the nav)
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
`/admin/content-health` (linked at the top of the nav) shows one category at a time as tabs, each
|
|
126
|
+
with its number of findings, and under them one section per check with the documents that need work.
|
|
127
|
+
The category with most to fix opens first. Where the project has several collections or locales, two
|
|
128
|
+
selects narrow the list. The tab and the selects are kept in the address, e.g.
|
|
129
|
+
`/admin/content-health?category=seo&locale=sv`; an older link with `?check=seo.title-missing` opens
|
|
130
|
+
that check's category. The view scans once per visit.
|
|
123
131
|
|
|
124
132
|
Each row opens the document in the finding's locale with the field brought into view: tabs and
|
|
125
133
|
collapsed rows are opened, and the field is scrolled to, focused and highlighted. A finding in a row
|
|
@@ -147,10 +155,10 @@ Both parameters are removed from the address once read. `editURL(adminRoute, fin
|
|
|
147
155
|
|
|
148
156
|
### Many documents at once
|
|
149
157
|
|
|
150
|
-
Where the assistant allows `content.update` (0.11.0 or later),
|
|
151
|
-
meta titles or descriptions (missing or the wrong length)
|
|
152
|
-
the assistant"
|
|
153
|
-
documents
|
|
158
|
+
Where the assistant allows `content.update` (0.11.0 or later), the sections for missing alt texts,
|
|
159
|
+
meta titles or descriptions (missing or the wrong length) and missing translations offer "Fix N with
|
|
160
|
+
the assistant" next to their heading. It opens the assistant's panel beside the list with up to 20 of
|
|
161
|
+
the documents in the section and one prompt for all of them; the assistant proposes a change per document, and
|
|
154
162
|
the editor saves the ones they want. Saved documents drop out of the list. `batchURL(adminRoute,
|
|
155
163
|
filters, targets, prompt)` and `batchTarget(finding)` build the link.
|
|
156
164
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CheckStats } from "./group.ts";
|
|
2
|
+
import type { CheckId } from "./types.ts";
|
|
3
|
+
/** What an editor sorts the checks by: the order tabs and rows come in. */
|
|
4
|
+
export declare const CATEGORY_IDS: readonly ["seo", "structure", "translations", "accessibility"];
|
|
5
|
+
export type CategoryId = (typeof CATEGORY_IDS)[number];
|
|
6
|
+
export declare const CATEGORY_CHECKS: Record<CategoryId, readonly CheckId[]>;
|
|
7
|
+
export declare function categoryOf(check: CheckId): CategoryId;
|
|
8
|
+
export type CategoryStats = {
|
|
9
|
+
category: CategoryId;
|
|
10
|
+
/** Field values the category's checks applied to. */
|
|
11
|
+
checked: number;
|
|
12
|
+
/** Values with a finding. */
|
|
13
|
+
failing: number;
|
|
14
|
+
};
|
|
15
|
+
/** Per category with at least one check that ran, in `CATEGORY_IDS` order. */
|
|
16
|
+
export declare function categoryStats(stats: CheckStats[]): CategoryStats[];
|
|
17
|
+
/** How a category reads at a glance: nothing to measure, all in order, mostly in order, or not. */
|
|
18
|
+
export type Tone = "none" | "ok" | "warn" | "bad";
|
|
19
|
+
export declare function categoryTone({ checked, failing, }: Pick<CategoryStats, "checked" | "failing">): Tone;
|
|
20
|
+
/** The category to open first: the one with most to fix, else the first. */
|
|
21
|
+
export declare function defaultCategory(stats: CategoryStats[]): CategoryId | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** What an editor sorts the checks by: the order tabs and rows come in. */
|
|
2
|
+
export const CATEGORY_IDS = ["seo", "structure", "translations", "accessibility"];
|
|
3
|
+
export const CATEGORY_CHECKS = {
|
|
4
|
+
seo: [
|
|
5
|
+
"seo.title-missing",
|
|
6
|
+
"seo.title-length",
|
|
7
|
+
"seo.description-missing",
|
|
8
|
+
"seo.description-length",
|
|
9
|
+
"seo.image-missing",
|
|
10
|
+
],
|
|
11
|
+
structure: ["content.h1-missing", "content.h1-multiple", "content.heading-skip", "content.thin"],
|
|
12
|
+
translations: ["content.untranslated"],
|
|
13
|
+
accessibility: ["media.alt-missing"],
|
|
14
|
+
};
|
|
15
|
+
export function categoryOf(check) {
|
|
16
|
+
return CATEGORY_IDS.find((id) => CATEGORY_CHECKS[id].includes(check));
|
|
17
|
+
}
|
|
18
|
+
/** Per category with at least one check that ran, in `CATEGORY_IDS` order. */
|
|
19
|
+
export function categoryStats(stats) {
|
|
20
|
+
return CATEGORY_IDS.flatMap((category) => {
|
|
21
|
+
const own = stats.filter((stat) => categoryOf(stat.check) === category);
|
|
22
|
+
return own.length
|
|
23
|
+
? [
|
|
24
|
+
{
|
|
25
|
+
category,
|
|
26
|
+
checked: own.reduce((sum, stat) => sum + stat.checked, 0),
|
|
27
|
+
failing: own.reduce((sum, stat) => sum + stat.failing, 0),
|
|
28
|
+
},
|
|
29
|
+
]
|
|
30
|
+
: [];
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function categoryTone({ checked, failing, }) {
|
|
34
|
+
if (!checked)
|
|
35
|
+
return failing ? "bad" : "none";
|
|
36
|
+
if (!failing)
|
|
37
|
+
return "ok";
|
|
38
|
+
return (checked - failing) / checked >= 0.5 ? "warn" : "bad";
|
|
39
|
+
}
|
|
40
|
+
/** The category to open first: the one with most to fix, else the first. */
|
|
41
|
+
export function defaultCategory(stats) {
|
|
42
|
+
return stats.reduce((best, stat) => (!best || stat.failing > best.failing ? stat : best), undefined)?.category;
|
|
43
|
+
}
|
package/dist/copy.d.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/** Everything the widget and the work list say, in Admin's language. */
|
|
2
|
+
import type { CategoryId } from "./categories.ts";
|
|
2
3
|
import type { CheckId, Finding } from "./types.ts";
|
|
3
4
|
export type UiLanguage = "sv" | "en";
|
|
4
5
|
export declare function uiLanguage(adminLanguage: string | undefined): UiLanguage;
|
|
5
6
|
export declare const CHECK_LABELS: Record<UiLanguage, Record<CheckId, string>>;
|
|
6
|
-
|
|
7
|
-
export declare const TILE_LABELS: Record<UiLanguage, Record<CheckId, string>>;
|
|
8
|
-
type Status = (ok: number, total: number) => string;
|
|
9
|
-
/** A tile's "x of y" line, phrased as what is in order for that check. */
|
|
10
|
-
export declare const STATUS: Record<UiLanguage, Record<CheckId, Status>>;
|
|
7
|
+
export declare const CATEGORY_LABELS: Record<UiLanguage, Record<CategoryId, string>>;
|
|
11
8
|
export declare const COPY: {
|
|
12
9
|
readonly en: {
|
|
13
10
|
readonly title: "Content health";
|
|
@@ -17,8 +14,8 @@ export declare const COPY: {
|
|
|
17
14
|
readonly ok: (ok: number, total: number) => string;
|
|
18
15
|
readonly checkPasses: "All good";
|
|
19
16
|
readonly nothingYet: "Nothing to measure yet";
|
|
20
|
-
readonly
|
|
21
|
-
readonly
|
|
17
|
+
readonly toFix: (count: number) => string;
|
|
18
|
+
readonly openDetails: "Open details";
|
|
22
19
|
readonly length: (actual: number, min: number, max: number) => string;
|
|
23
20
|
readonly headingSkip: (previous: number, level: number) => string;
|
|
24
21
|
readonly h1Count: (count: number) => string;
|
|
@@ -34,8 +31,8 @@ export declare const COPY: {
|
|
|
34
31
|
readonly ok: (ok: number, total: number) => string;
|
|
35
32
|
readonly checkPasses: "Allt i ordning";
|
|
36
33
|
readonly nothingYet: "Inget att mäta än";
|
|
37
|
-
readonly
|
|
38
|
-
readonly
|
|
34
|
+
readonly toFix: (count: number) => string;
|
|
35
|
+
readonly openDetails: "Öppna detaljer";
|
|
39
36
|
readonly length: (actual: number, min: number, max: number) => string;
|
|
40
37
|
readonly headingSkip: (previous: number, level: number) => string;
|
|
41
38
|
readonly h1Count: (count: number) => string;
|
|
@@ -62,13 +59,12 @@ export declare const LIST_COPY: {
|
|
|
62
59
|
readonly nav: "Content health";
|
|
63
60
|
readonly intro: "Everything the checks found, in the latest version of each document, drafts included.";
|
|
64
61
|
readonly summary: (findings: number, documents: number) => string;
|
|
65
|
-
readonly
|
|
62
|
+
readonly categories: "Categories";
|
|
66
63
|
readonly collection: "Collection";
|
|
67
64
|
readonly locale: "Locale";
|
|
68
65
|
readonly all: "All";
|
|
69
|
-
readonly
|
|
70
|
-
readonly
|
|
71
|
-
readonly issue: "Issue";
|
|
66
|
+
readonly documents: (count: number) => string;
|
|
67
|
+
readonly categoryOk: "Everything in this category is in order.";
|
|
72
68
|
readonly draft: "Draft";
|
|
73
69
|
readonly fix: "Fix with the assistant";
|
|
74
70
|
readonly fixMany: (count: number, total: number) => string;
|
|
@@ -80,13 +76,12 @@ export declare const LIST_COPY: {
|
|
|
80
76
|
readonly nav: "Innehållshälsa";
|
|
81
77
|
readonly intro: "Allt kontrollerna hittade, i den senaste versionen av varje dokument, utkast inräknade.";
|
|
82
78
|
readonly summary: (findings: number, documents: number) => string;
|
|
83
|
-
readonly
|
|
79
|
+
readonly categories: "Kategorier";
|
|
84
80
|
readonly collection: "Samling";
|
|
85
81
|
readonly locale: "Språk";
|
|
86
82
|
readonly all: "Alla";
|
|
87
|
-
readonly
|
|
88
|
-
readonly
|
|
89
|
-
readonly issue: "Problem";
|
|
83
|
+
readonly documents: (count: number) => string;
|
|
84
|
+
readonly categoryOk: "Allt i den här kategorin är i ordning.";
|
|
90
85
|
readonly draft: "Utkast";
|
|
91
86
|
readonly fix: "Åtgärda med assistenten";
|
|
92
87
|
readonly fixMany: (count: number, total: number) => string;
|
package/dist/copy.js
CHANGED
|
@@ -29,62 +29,18 @@ export const CHECK_LABELS = {
|
|
|
29
29
|
"content.untranslated": "Saknar översättning",
|
|
30
30
|
},
|
|
31
31
|
};
|
|
32
|
-
|
|
33
|
-
export const TILE_LABELS = {
|
|
32
|
+
export const CATEGORY_LABELS = {
|
|
34
33
|
en: {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"seo.description-length": "Meta description length",
|
|
40
|
-
"seo.image-missing": "Share images",
|
|
41
|
-
"content.h1-missing": "Main heading",
|
|
42
|
-
"content.h1-multiple": "Single main heading",
|
|
43
|
-
"content.heading-skip": "Heading order",
|
|
44
|
-
"content.thin": "Page length",
|
|
45
|
-
"content.untranslated": "Translations",
|
|
46
|
-
},
|
|
47
|
-
sv: {
|
|
48
|
-
"media.alt-missing": "Alt-texter",
|
|
49
|
-
"seo.title-missing": "Metatitlar",
|
|
50
|
-
"seo.title-length": "Metatitlarnas längd",
|
|
51
|
-
"seo.description-missing": "Metabeskrivningar",
|
|
52
|
-
"seo.description-length": "Metabeskrivningarnas längd",
|
|
53
|
-
"seo.image-missing": "Delningsbilder",
|
|
54
|
-
"content.h1-missing": "Huvudrubrik",
|
|
55
|
-
"content.h1-multiple": "En huvudrubrik",
|
|
56
|
-
"content.heading-skip": "Rubrikordning",
|
|
57
|
-
"content.thin": "Sidlängd",
|
|
58
|
-
"content.untranslated": "Översättningar",
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
/** A tile's "x of y" line, phrased as what is in order for that check. */
|
|
62
|
-
export const STATUS = {
|
|
63
|
-
en: {
|
|
64
|
-
"media.alt-missing": (ok, total) => `${ok} of ${total} filled in`,
|
|
65
|
-
"seo.title-missing": (ok, total) => `${ok} of ${total} filled in`,
|
|
66
|
-
"seo.title-length": (ok, total) => `${ok} of ${total} the right length`,
|
|
67
|
-
"seo.description-missing": (ok, total) => `${ok} of ${total} filled in`,
|
|
68
|
-
"seo.description-length": (ok, total) => `${ok} of ${total} the right length`,
|
|
69
|
-
"seo.image-missing": (ok, total) => `${ok} of ${total} filled in`,
|
|
70
|
-
"content.h1-missing": (ok, total) => `${ok} of ${total} pages have an H1`,
|
|
71
|
-
"content.h1-multiple": (ok, total) => `${ok} of ${total} pages have at most one`,
|
|
72
|
-
"content.heading-skip": (ok, total) => `${ok} of ${total} pages in order`,
|
|
73
|
-
"content.thin": (ok, total) => `${ok} of ${total} pages long enough`,
|
|
74
|
-
"content.untranslated": (ok, total) => `${ok} of ${total} translated`,
|
|
34
|
+
seo: "SEO",
|
|
35
|
+
structure: "Structure",
|
|
36
|
+
translations: "Translations",
|
|
37
|
+
accessibility: "Accessibility",
|
|
75
38
|
},
|
|
76
39
|
sv: {
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"seo.description-length": (ok, total) => `${ok} av ${total} med rätt längd`,
|
|
82
|
-
"seo.image-missing": (ok, total) => `${ok} av ${total} ifyllda`,
|
|
83
|
-
"content.h1-missing": (ok, total) => `${ok} av ${total} sidor har en H1`,
|
|
84
|
-
"content.h1-multiple": (ok, total) => `${ok} av ${total} sidor har högst en`,
|
|
85
|
-
"content.heading-skip": (ok, total) => `${ok} av ${total} sidor i ordning`,
|
|
86
|
-
"content.thin": (ok, total) => `${ok} av ${total} sidor tillräckligt långa`,
|
|
87
|
-
"content.untranslated": (ok, total) => `${ok} av ${total} översatta`,
|
|
40
|
+
seo: "SEO",
|
|
41
|
+
structure: "Struktur",
|
|
42
|
+
translations: "Översättningar",
|
|
43
|
+
accessibility: "Tillgänglighet",
|
|
88
44
|
},
|
|
89
45
|
};
|
|
90
46
|
export const COPY = {
|
|
@@ -96,8 +52,8 @@ export const COPY = {
|
|
|
96
52
|
ok: (ok, total) => `${ok} of ${total} OK`,
|
|
97
53
|
checkPasses: "All good",
|
|
98
54
|
nothingYet: "Nothing to measure yet",
|
|
99
|
-
|
|
100
|
-
|
|
55
|
+
toFix: (count) => `${count} to fix`,
|
|
56
|
+
openDetails: "Open details",
|
|
101
57
|
length: (actual, min, max) => `${actual} characters, aim for ${min}–${max}`,
|
|
102
58
|
headingSkip: (previous, level) => `H${previous} → H${level}`,
|
|
103
59
|
h1Count: (count) => `${count} H1s`,
|
|
@@ -113,8 +69,8 @@ export const COPY = {
|
|
|
113
69
|
ok: (ok, total) => `${ok} av ${total} OK`,
|
|
114
70
|
checkPasses: "Allt i ordning",
|
|
115
71
|
nothingYet: "Inget att mäta än",
|
|
116
|
-
|
|
117
|
-
|
|
72
|
+
toFix: (count) => `${count} att åtgärda`,
|
|
73
|
+
openDetails: "Öppna detaljer",
|
|
118
74
|
length: (actual, min, max) => `${actual} tecken, sikta på ${min}–${max}`,
|
|
119
75
|
headingSkip: (previous, level) => `H${previous} → H${level}`,
|
|
120
76
|
h1Count: (count) => `${count} H1:or`,
|
|
@@ -257,13 +213,12 @@ export const LIST_COPY = {
|
|
|
257
213
|
nav: "Content health",
|
|
258
214
|
intro: "Everything the checks found, in the latest version of each document, drafts included.",
|
|
259
215
|
summary: (findings, documents) => `${findings} ${findings === 1 ? "issue" : "issues"} in ${documents} ${documents === 1 ? "document" : "documents"}`,
|
|
260
|
-
|
|
216
|
+
categories: "Categories",
|
|
261
217
|
collection: "Collection",
|
|
262
218
|
locale: "Locale",
|
|
263
219
|
all: "All",
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
issue: "Issue",
|
|
220
|
+
documents: (count) => `${count} ${count === 1 ? "document" : "documents"}`,
|
|
221
|
+
categoryOk: "Everything in this category is in order.",
|
|
267
222
|
draft: "Draft",
|
|
268
223
|
fix: "Fix with the assistant",
|
|
269
224
|
fixMany: (count, total) => count < total
|
|
@@ -277,13 +232,12 @@ export const LIST_COPY = {
|
|
|
277
232
|
nav: "Innehållshälsa",
|
|
278
233
|
intro: "Allt kontrollerna hittade, i den senaste versionen av varje dokument, utkast inräknade.",
|
|
279
234
|
summary: (findings, documents) => `${findings} problem i ${documents} dokument`,
|
|
280
|
-
|
|
235
|
+
categories: "Kategorier",
|
|
281
236
|
collection: "Samling",
|
|
282
237
|
locale: "Språk",
|
|
283
238
|
all: "Alla",
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
issue: "Problem",
|
|
239
|
+
documents: (count) => `${count} dokument`,
|
|
240
|
+
categoryOk: "Allt i den här kategorin är i ordning.",
|
|
287
241
|
draft: "Utkast",
|
|
288
242
|
fix: "Åtgärda med assistenten",
|
|
289
243
|
fixMany: (count, total) => count < total
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export type { CategoryId, CategoryStats } from "./categories.ts";
|
|
2
|
+
export { CATEGORY_IDS, categoryOf, categoryStats } from "./categories.ts";
|
|
1
3
|
export type { DocumentInput, DocumentResult, FieldRule, ResolvedRule } from "./checks.ts";
|
|
2
4
|
export { checkDocument, DEFAULT_MIN_WORDS, rulesFor, SEO_DEFAULTS } from "./checks.ts";
|
|
3
5
|
export type { Heading, PageContent } from "./content.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { CATEGORY_IDS, categoryOf, categoryStats } from "./categories.js";
|
|
1
2
|
export { checkDocument, DEFAULT_MIN_WORDS, rulesFor, SEO_DEFAULTS } from "./checks.js";
|
|
2
3
|
export { countWords, readContent } from "./content.js";
|
|
3
4
|
export { checkStats, countDocuments, groupFindings } from "./group.js";
|
package/dist/links.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CategoryId } from "./categories.ts";
|
|
1
2
|
import { type CheckId, type Finding } from "./types.ts";
|
|
2
3
|
/** Admin path of the work list. */
|
|
3
4
|
export declare const VIEW_PATH = "/content-health";
|
|
@@ -62,7 +63,12 @@ export declare function batchTarget(finding: Finding): string;
|
|
|
62
63
|
export declare function batchURL(adminRoute: string, filters: ListFilters, targets: string[], ask: string): string;
|
|
63
64
|
/** The document in the finding's locale with its field brought into view, and a prompt when given. */
|
|
64
65
|
export declare function editURL(adminRoute: string, finding: Finding, ask?: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* The work list's filters. `category` picks the tab. `check` is only an entry point (an older link,
|
|
68
|
+
* or a batch link): the view turns it into its category and `check` stays out of the state.
|
|
69
|
+
*/
|
|
65
70
|
export type ListFilters = {
|
|
71
|
+
category?: CategoryId;
|
|
66
72
|
check?: CheckId;
|
|
67
73
|
collection?: string;
|
|
68
74
|
locale?: string;
|
package/dist/links.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* the client bridge build and read the same addresses.
|
|
5
5
|
*/
|
|
6
6
|
import { formatAdminURL } from "payload/shared";
|
|
7
|
+
import { CATEGORY_IDS, categoryOf } from "./categories.js";
|
|
7
8
|
import { CHECK_IDS } from "./types.js";
|
|
8
9
|
/** Admin path of the work list. */
|
|
9
10
|
export const VIEW_PATH = "/content-health";
|
|
@@ -128,7 +129,7 @@ export function listURL(adminRoute, filters = {}) {
|
|
|
128
129
|
}
|
|
129
130
|
export function filterQuery(filters) {
|
|
130
131
|
const params = new URLSearchParams();
|
|
131
|
-
for (const key of ["check", "collection", "locale"]) {
|
|
132
|
+
for (const key of ["category", "check", "collection", "locale"]) {
|
|
132
133
|
const value = filters[key];
|
|
133
134
|
if (value) {
|
|
134
135
|
params.set(key, value);
|
|
@@ -142,17 +143,22 @@ export function readFilters(searchParams) {
|
|
|
142
143
|
const value = searchParams?.[key];
|
|
143
144
|
return (Array.isArray(value) ? value[0] : value) || undefined;
|
|
144
145
|
};
|
|
146
|
+
const category = first("category");
|
|
145
147
|
const check = first("check");
|
|
146
148
|
const collection = first("collection");
|
|
147
149
|
const locale = first("locale");
|
|
148
150
|
return {
|
|
151
|
+
...(category && CATEGORY_IDS.includes(category)
|
|
152
|
+
? { category: category }
|
|
153
|
+
: {}),
|
|
149
154
|
...(check && CHECK_IDS.includes(check) ? { check: check } : {}),
|
|
150
155
|
...(collection ? { collection } : {}),
|
|
151
156
|
...(locale ? { locale } : {}),
|
|
152
157
|
};
|
|
153
158
|
}
|
|
154
159
|
export function matchesFilters(finding, filters) {
|
|
155
|
-
return ((!filters.
|
|
160
|
+
return ((!filters.category || categoryOf(finding.check) === filters.category) &&
|
|
161
|
+
(!filters.check || finding.check === filters.check) &&
|
|
156
162
|
(!filters.collection || finding.collection === filters.collection) &&
|
|
157
163
|
// Unlocalized fields belong to every locale.
|
|
158
164
|
(!filters.locale || !finding.locale || finding.locale === filters.locale));
|
package/dist/scan.js
CHANGED
|
@@ -31,7 +31,7 @@ export async function scanContentHealth({ payload, user, checks }) {
|
|
|
31
31
|
? payload.config.localization.localeCodes
|
|
32
32
|
: [undefined];
|
|
33
33
|
for (const [slug, collectionOptions] of Object.entries(options.collections)) {
|
|
34
|
-
const collection = payload.collections
|
|
34
|
+
const collection = payload.config.collections.find((collection) => collection.slug === slug);
|
|
35
35
|
if (!collection) {
|
|
36
36
|
result.problems.push(`Collection "${slug}" does not exist.`);
|
|
37
37
|
continue;
|
|
@@ -95,7 +95,7 @@ export async function scanContentHealth({ payload, user, checks }) {
|
|
|
95
95
|
depth: 0,
|
|
96
96
|
draft: drafts,
|
|
97
97
|
fallbackLocale: false,
|
|
98
|
-
...(locale ? { locale } : {}),
|
|
98
|
+
...(locale ? { locale: locale } : {}),
|
|
99
99
|
overrideAccess: false,
|
|
100
100
|
pagination: false,
|
|
101
101
|
select: select,
|
|
@@ -159,7 +159,7 @@ async function scanTranslations(input) {
|
|
|
159
159
|
depth: 0,
|
|
160
160
|
draft: input.drafts,
|
|
161
161
|
fallbackLocale: false,
|
|
162
|
-
locale,
|
|
162
|
+
locale: locale,
|
|
163
163
|
overrideAccess: false,
|
|
164
164
|
pagination: false,
|
|
165
165
|
user: input.user,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Colors shared by the widget and the work list. The accent is the blue the editor assistant and
|
|
3
|
+
* visual editing use; red and amber mark what needs work. The text tones are darker, so small text
|
|
4
|
+
* keeps its contrast on Payload's light background.
|
|
5
|
+
*/
|
|
6
|
+
.content-health,
|
|
7
|
+
.content-health-list {
|
|
8
|
+
--content-health-accent: #2563eb;
|
|
9
|
+
--content-health-accent-text: #2563eb;
|
|
10
|
+
--content-health-accent-tint: rgba(37, 99, 235, 0.1);
|
|
11
|
+
--content-health-bad: #dc2626;
|
|
12
|
+
--content-health-bad-text: #b91c1c;
|
|
13
|
+
--content-health-warn: #d97706;
|
|
14
|
+
--content-health-warn-text: #b45309;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
html[data-theme="dark"] .content-health,
|
|
18
|
+
html[data-theme="dark"] .content-health-list {
|
|
19
|
+
--content-health-accent: #60a5fa;
|
|
20
|
+
--content-health-accent-text: #60a5fa;
|
|
21
|
+
--content-health-accent-tint: rgba(96, 165, 250, 0.14);
|
|
22
|
+
--content-health-bad: #f87171;
|
|
23
|
+
--content-health-bad-text: #f87171;
|
|
24
|
+
--content-health-warn: #fbbf24;
|
|
25
|
+
--content-health-warn-text: #fbbf24;
|
|
26
|
+
}
|