@dappermountain/payload-plugin-achievements 0.2.3 → 0.2.5

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.
Files changed (32) hide show
  1. package/README.md +5 -1
  2. package/dist/admin/LucideIconField.d.ts +6 -0
  3. package/dist/admin/LucideIconField.d.ts.map +1 -0
  4. package/dist/admin/LucideIconField.js +56 -0
  5. package/dist/collections/Tiers/index.d.ts.map +1 -1
  6. package/dist/collections/Tiers/index.js +4 -1
  7. package/dist/collections/fields/localizedText.d.ts +1 -0
  8. package/dist/collections/fields/localizedText.d.ts.map +1 -1
  9. package/dist/collections/fields/localizedText.js +28 -1
  10. package/dist/collections/fields/usersAchievementsGroup.d.ts.map +1 -1
  11. package/dist/collections/fields/usersAchievementsGroup.js +2 -1
  12. package/dist/exports/client.d.ts +1 -0
  13. package/dist/exports/client.d.ts.map +1 -1
  14. package/dist/exports/client.js +1 -0
  15. package/dist/fields/descriptionPlaintext.d.ts +6 -0
  16. package/dist/fields/descriptionPlaintext.d.ts.map +1 -0
  17. package/dist/fields/descriptionPlaintext.js +24 -0
  18. package/dist/fields/plainTextToLexical.d.ts +7 -0
  19. package/dist/fields/plainTextToLexical.d.ts.map +1 -0
  20. package/dist/fields/plainTextToLexical.js +40 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +2 -0
  24. package/dist/seed/index.d.ts.map +1 -1
  25. package/dist/seed/index.js +6 -3
  26. package/dist/services/achievement/buildCatalogProgressGroups.d.ts +5 -3
  27. package/dist/services/achievement/buildCatalogProgressGroups.d.ts.map +1 -1
  28. package/dist/services/achievement/buildCatalogProgressGroups.js +11 -6
  29. package/dist/services/achievement/getUserProgress.d.ts +2 -1
  30. package/dist/services/achievement/getUserProgress.d.ts.map +1 -1
  31. package/dist/services/achievement/getUserProgress.js +4 -3
  32. package/package.json +5 -1
package/README.md CHANGED
@@ -133,9 +133,13 @@ When your Payload config enables **`localization`**, catalog display fields are
133
133
 
134
134
  | Collection | Localized | Not localized |
135
135
  | --- | --- | --- |
136
- | Achievements / tiers | `name`, `description` | `slug`, rules, flags |
136
+ | Achievements / tiers | `name` (text), `description` (**Lexical** rich text) | `slug`, rules, flags |
137
137
  | Metrics / event types | `name` | `slug`, system flags |
138
138
 
139
+ Tier `icon` is a searchable Lucide picker in Admin (stores the kebab-case name, e.g. `sparkles`). Hosts must peer-depend on `@payloadcms/richtext-lexical` and `lucide-react` (same major as Payload). After upgrading, run `payload generate:importmap` and push/migrate schema so `description` columns accept JSON.
140
+
141
+ Progress / `GET /api/achievements/me` pass through catalog `description` as **Lexical** (same as the collection document). Convert to HTML/plaintext in the UI with `@payloadcms/richtext-lexical` helpers (`convertLexicalToHTML`, `convertLexicalToPlaintext`) — the plugin also exports `descriptionToPlaintext` / `plainTextToLexical` for hosts.
142
+
139
143
  UI chrome and validation messages ship in English. Application UI copy stays in your app’s i18n layer — the plugin does not inject host translation dictionaries. Seed extra locales yourself after the English system catalog is in place.
140
144
 
141
145
  ## Configuration reference
@@ -0,0 +1,6 @@
1
+ import type { TextFieldClientComponent } from 'payload';
2
+ /**
3
+ * Searchable Lucide icon picker. Stores the kebab-case Lucide name (e.g. `sparkles`).
4
+ */
5
+ export declare const LucideIconField: TextFieldClientComponent;
6
+ //# sourceMappingURL=LucideIconField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LucideIconField.d.ts","sourceRoot":"","sources":["../../src/admin/LucideIconField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAgB,MAAM,SAAS,CAAA;AA4BrE;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,wBA4D7B,CAAA"}
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { FieldDescription, FieldError, FieldLabel, SelectInput, useField } from '@payloadcms/ui';
4
+ import { icons } from 'lucide-react';
5
+ import { useMemo } from 'react';
6
+ function toKebabCase(pascal) {
7
+ return pascal
8
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
9
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
10
+ .toLowerCase();
11
+ }
12
+ function toPascalCase(kebab) {
13
+ return kebab
14
+ .split('-')
15
+ .filter(Boolean)
16
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
17
+ .join('');
18
+ }
19
+ const ICON_OPTIONS = Object.keys(icons)
20
+ .filter((key) => /^[A-Z]/.test(key))
21
+ .map((pascal) => {
22
+ const value = toKebabCase(pascal);
23
+ return { label: value, value };
24
+ })
25
+ .sort((a, b) => a.value.localeCompare(b.value));
26
+ /**
27
+ * Searchable Lucide icon picker. Stores the kebab-case Lucide name (e.g. `sparkles`).
28
+ */
29
+ export const LucideIconField = (props) => {
30
+ const { field, path: pathFromProps, readOnly } = props;
31
+ const path = pathFromProps ?? field?.name ?? 'icon';
32
+ const { errorMessage, setValue, showError, value } = useField({
33
+ path,
34
+ });
35
+ const selected = typeof value === 'string' && value.length > 0 ? value : undefined;
36
+ const PreviewIcon = selected
37
+ ? icons[toPascalCase(selected)]
38
+ : undefined;
39
+ const options = useMemo(() => ICON_OPTIONS, []);
40
+ return (_jsxs("div", { className: "field-type", children: [_jsx(FieldLabel, { label: field?.label ?? 'Icon', path: path, required: Boolean(field?.required) }), _jsxs("div", { style: { alignItems: 'center', display: 'flex', gap: '0.75rem' }, children: [_jsx("div", { "aria-hidden": true, style: {
41
+ alignItems: 'center',
42
+ border: '1px solid var(--theme-elevation-150)',
43
+ borderRadius: 6,
44
+ display: 'flex',
45
+ flexShrink: 0,
46
+ height: 40,
47
+ justifyContent: 'center',
48
+ width: 40,
49
+ }, children: PreviewIcon ? (_jsx(PreviewIcon, { size: 20 })) : (_jsx("span", { style: { color: 'var(--theme-elevation-400)', fontSize: 12 }, children: "\u2014" })) }), _jsx("div", { style: { flex: 1, minWidth: 0 }, children: _jsx(SelectInput, { isClearable: true, name: path, onChange: (option) => {
50
+ if (Array.isArray(option)) {
51
+ setValue(option[0]?.value ?? null);
52
+ return;
53
+ }
54
+ setValue(option?.value ?? null);
55
+ }, options: options, path: path, readOnly: Boolean(readOnly), value: selected }) })] }), _jsx(FieldDescription, { description: field?.admin?.description, path: path }), _jsx(FieldError, { message: errorMessage, path: path, showError: showError })] }));
56
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/Tiers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAQ/C,wBAAgB,oBAAoB,IAAI,gBAAgB,CAwFvD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/Tiers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAQ/C,wBAAgB,oBAAoB,IAAI,gBAAgB,CA2FvD"}
@@ -49,7 +49,10 @@ export function buildTiersCollection() {
49
49
  type: 'text',
50
50
  admin: {
51
51
  width: mediaCollection ? '50%' : '100%',
52
- description: 'Optional icon key your app maps (e.g. Lucide names: sparkles, shield, crown, gem). Used on the rank ladder when no image is set.',
52
+ description: 'Optional Lucide icon for the rank ladder when no image is set (searchable by name).',
53
+ components: {
54
+ Field: '@dappermountain/payload-plugin-achievements/client#LucideIconField',
55
+ },
53
56
  },
54
57
  },
55
58
  ...(mediaCollection
@@ -6,5 +6,6 @@ import type { Field } from 'payload';
6
6
  export declare function localizedNameField(admin?: {
7
7
  width?: string;
8
8
  }): Field;
9
+ /** Catalog blurb — Lexical in Admin; seed may still pass plain strings (coerced). */
9
10
  export declare function localizedDescriptionField(): Field;
10
11
  //# sourceMappingURL=localizedText.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"localizedText.d.ts","sourceRoot":"","sources":["../../../src/collections/fields/localizedText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAQpE;AAED,wBAAgB,yBAAyB,IAAI,KAAK,CAMjD"}
1
+ {"version":3,"file":"localizedText.d.ts","sourceRoot":"","sources":["../../../src/collections/fields/localizedText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAgBpC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAQpE;AAED,qFAAqF;AACrF,wBAAgB,yBAAyB,IAAI,KAAK,CA4BjD"}
@@ -1,3 +1,5 @@
1
+ import { BoldFeature, FixedToolbarFeature, InlineToolbarFeature, ItalicFeature, LinkFeature, OrderedListFeature, ParagraphFeature, UnderlineFeature, UnorderedListFeature, lexicalEditor, } from '@payloadcms/richtext-lexical';
2
+ import { plainTextToLexical } from '../../fields/plainTextToLexical.js';
1
3
  /**
2
4
  * Display copy for catalog rows. Localized when the host enables Payload `localization`.
3
5
  * Slugs stay non-localized identifiers for rules and code.
@@ -11,10 +13,35 @@ export function localizedNameField(admin) {
11
13
  ...(admin ? { admin } : {}),
12
14
  };
13
15
  }
16
+ /** Catalog blurb — Lexical in Admin; seed may still pass plain strings (coerced). */
14
17
  export function localizedDescriptionField() {
15
18
  return {
16
19
  name: 'description',
17
- type: 'textarea',
20
+ type: 'richText',
18
21
  localized: true,
22
+ editor: lexicalEditor({
23
+ features: () => [
24
+ ParagraphFeature(),
25
+ BoldFeature(),
26
+ ItalicFeature(),
27
+ UnderlineFeature(),
28
+ UnorderedListFeature(),
29
+ OrderedListFeature(),
30
+ LinkFeature(),
31
+ FixedToolbarFeature(),
32
+ InlineToolbarFeature(),
33
+ ],
34
+ }),
35
+ hooks: {
36
+ beforeValidate: [
37
+ ({ value }) => {
38
+ if (value == null || value === '')
39
+ return value;
40
+ if (typeof value === 'string')
41
+ return plainTextToLexical(value);
42
+ return value;
43
+ },
44
+ ],
45
+ },
19
46
  };
20
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"usersAchievementsGroup.d.ts","sourceRoot":"","sources":["../../../src/collections/fields/usersAchievementsGroup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAA;AAEnE;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,0BAA0B,GAAG,KAAK,CAgCjF"}
1
+ {"version":3,"file":"usersAchievementsGroup.d.ts","sourceRoot":"","sources":["../../../src/collections/fields/usersAchievementsGroup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAA;AAEnE;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,0BAA0B,GAAG,KAAK,CAiCjF"}
@@ -17,7 +17,8 @@ export function usersAchievementsGroup(options) {
17
17
  collection: slugs.grants,
18
18
  on: 'user',
19
19
  admin: {
20
- allowCreate: false,
20
+ // Create from the user doc — Payload pre-fills `user` on the grant drawer.
21
+ allowCreate: true,
21
22
  defaultColumns: ['achievement', 'completedAt', 'scope'],
22
23
  },
23
24
  },
@@ -1,4 +1,5 @@
1
1
  export { ActorField } from '../admin/ActorField.js';
2
+ export { LucideIconField } from '../admin/LucideIconField.js';
2
3
  export { MetricChangeField } from '../admin/MetricChangeField.js';
3
4
  export { MetricField } from '../admin/MetricField.js';
4
5
  export { ReconcileProgressionButton } from '../admin/ReconcileProgressionButton.js';
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/exports/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAA"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/exports/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAA"}
@@ -1,4 +1,5 @@
1
1
  export { ActorField } from '../admin/ActorField.js';
2
+ export { LucideIconField } from '../admin/LucideIconField.js';
2
3
  export { MetricChangeField } from '../admin/MetricChangeField.js';
3
4
  export { MetricField } from '../admin/MetricField.js';
4
5
  export { ReconcileProgressionButton } from '../admin/ReconcileProgressionButton.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Progress / me APIs expose catalog blurbs as plain strings for search and simple UI.
3
+ * Admin stores Lexical JSON; legacy string rows still work.
4
+ */
5
+ export declare function descriptionToPlaintext(value: unknown): string | undefined;
6
+ //# sourceMappingURL=descriptionPlaintext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"descriptionPlaintext.d.ts","sourceRoot":"","sources":["../../src/fields/descriptionPlaintext.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAgBzE"}
@@ -0,0 +1,24 @@
1
+ import { convertLexicalToPlaintext } from '@payloadcms/richtext-lexical/plaintext';
2
+ /**
3
+ * Progress / me APIs expose catalog blurbs as plain strings for search and simple UI.
4
+ * Admin stores Lexical JSON; legacy string rows still work.
5
+ */
6
+ export function descriptionToPlaintext(value) {
7
+ if (value == null)
8
+ return undefined;
9
+ if (typeof value === 'string') {
10
+ const trimmed = value.trim();
11
+ return trimmed.length > 0 ? trimmed : undefined;
12
+ }
13
+ if (typeof value !== 'object')
14
+ return undefined;
15
+ try {
16
+ const plaintext = convertLexicalToPlaintext({
17
+ data: value,
18
+ }).trim();
19
+ return plaintext.length > 0 ? plaintext : undefined;
20
+ }
21
+ catch {
22
+ return undefined;
23
+ }
24
+ }
@@ -0,0 +1,7 @@
1
+ import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical';
2
+ /**
3
+ * Minimal Lexical editor state for a single paragraph of plain text.
4
+ * Used by seed + beforeValidate coercion when hosts still pass strings.
5
+ */
6
+ export declare function plainTextToLexical(text: string): SerializedEditorState;
7
+ //# sourceMappingURL=plainTextToLexical.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plainTextToLexical.d.ts","sourceRoot":"","sources":["../../src/fields/plainTextToLexical.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAEjF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAmCtE"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Minimal Lexical editor state for a single paragraph of plain text.
3
+ * Used by seed + beforeValidate coercion when hosts still pass strings.
4
+ */
5
+ export function plainTextToLexical(text) {
6
+ const trimmed = text.trim();
7
+ return {
8
+ root: {
9
+ type: 'root',
10
+ format: '',
11
+ indent: 0,
12
+ version: 1,
13
+ direction: 'ltr',
14
+ children: [
15
+ {
16
+ type: 'paragraph',
17
+ format: '',
18
+ indent: 0,
19
+ version: 1,
20
+ direction: 'ltr',
21
+ textFormat: 0,
22
+ textStyle: '',
23
+ children: trimmed
24
+ ? [
25
+ {
26
+ type: 'text',
27
+ text: trimmed,
28
+ detail: 0,
29
+ format: 0,
30
+ mode: 'normal',
31
+ style: '',
32
+ version: 1,
33
+ },
34
+ ]
35
+ : [],
36
+ },
37
+ ],
38
+ },
39
+ };
40
+ }
package/dist/index.d.ts CHANGED
@@ -10,4 +10,6 @@ export { DEFAULT_COLLECTION_BASE_SLUGS, DEFAULT_COLLECTION_PREFIX, DEFAULT_ADMIN
10
10
  export { DEFAULT_ME_ENDPOINT_PATH, DEFAULT_RECONCILE_ENDPOINT_PATH } from './defaults.js';
11
11
  export { collectionOf, slugOf } from './collections/helpers.js';
12
12
  export { isAchievementPluginEnabled } from './enabled.js';
13
+ export { descriptionToPlaintext } from './fields/descriptionPlaintext.js';
14
+ export { plainTextToLexical } from './fields/plainTextToLexical.js';
13
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,+BAA+B,EAC/B,kBAAkB,EAClB,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EACV,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,EACpB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,8BAA8B,EAC9B,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EACV,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EACjB,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AACtH,OAAO,EAAE,wBAAwB,EAAE,+BAA+B,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,+BAA+B,EAC/B,kBAAkB,EAClB,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EACV,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,EACpB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,8BAA8B,EAC9B,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EACV,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EACjB,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AACtH,OAAO,EAAE,wBAAwB,EAAE,+BAA+B,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA"}
package/dist/index.js CHANGED
@@ -6,3 +6,5 @@ export { DEFAULT_COLLECTION_BASE_SLUGS, DEFAULT_COLLECTION_PREFIX, DEFAULT_ADMIN
6
6
  export { DEFAULT_ME_ENDPOINT_PATH, DEFAULT_RECONCILE_ENDPOINT_PATH } from './defaults.js';
7
7
  export { collectionOf, slugOf } from './collections/helpers.js';
8
8
  export { isAchievementPluginEnabled } from './enabled.js';
9
+ export { descriptionToPlaintext } from './fields/descriptionPlaintext.js';
10
+ export { plainTextToLexical } from './fields/plainTextToLexical.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seed/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAMtC,mGAAmG;AACnG,MAAM,MAAM,4BAA4B,GAAG,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAExG,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,4BAA4B,CAAA;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,4BAA4B,CAAA;IAC1C,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAA;CACrG,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,4BAA4B,CAAA;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,4BAA4B,CAAA;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAA;IACzG,qGAAqG;IACrG,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAA;CACzG,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,4BAA4B,CAAA;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gFAAgF;IAChF,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,CAAC,EAAE,yBAAyB,EAAE,CAAA;IACxC,OAAO,CAAC,EAAE,yBAAyB,EAAE,CAAA;IACrC,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAC7B,YAAY,CAAC,EAAE,0BAA0B,EAAE,CAAA;IAC3C,gEAAgE;IAChE,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAA;CAClC,CAAA;AAmMD,2FAA2F;AAC3F,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB5F;AAiCD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmLnG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seed/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAOtC,mGAAmG;AACnG,MAAM,MAAM,4BAA4B,GAAG,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAExG,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,4BAA4B,CAAA;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,4BAA4B,CAAA;IAC1C,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAA;CACrG,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,4BAA4B,CAAA;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,4BAA4B,CAAA;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAA;IACzG,qGAAqG;IACrG,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAA;CACzG,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,4BAA4B,CAAA;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gFAAgF;IAChF,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,CAAC,EAAE,yBAAyB,EAAE,CAAA;IACxC,OAAO,CAAC,EAAE,yBAAyB,EAAE,CAAA;IACrC,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAC7B,YAAY,CAAC,EAAE,0BAA0B,EAAE,CAAA;IAC3C,gEAAgE;IAChE,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAA;CAClC,CAAA;AAmMD,2FAA2F;AAC3F,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB5F;AAiCD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqLnG"}
@@ -1,5 +1,6 @@
1
1
  import { ENGINE_EVENT_TYPES, ENGINE_METRICS } from '../catalog.js';
2
2
  import { collectionOf } from '../collections/helpers.js';
3
+ import { plainTextToLexical } from '../fields/plainTextToLexical.js';
3
4
  import { normalizeRuleGroup } from '../services/achievement/evaluateRules.js';
4
5
  function localizedParts(value) {
5
6
  if (value == null)
@@ -31,7 +32,7 @@ async function writeLocalizedFields(payload, args) {
31
32
  data: {
32
33
  ...(locale === 'en' ? base : {}),
33
34
  ...(name != null ? { name } : {}),
34
- ...(description != null ? { description } : {}),
35
+ ...(description != null ? { description: plainTextToLexical(description) } : {}),
35
36
  },
36
37
  locale: locale,
37
38
  overrideAccess: true,
@@ -242,7 +243,7 @@ export async function seedAchievements(payload, input) {
242
243
  ...baseData,
243
244
  name: nameParts.en ?? tier.slug,
244
245
  ...(localizedParts(tier.description).en != null
245
- ? { description: localizedParts(tier.description).en }
246
+ ? { description: plainTextToLexical(localizedParts(tier.description).en) }
246
247
  : {}),
247
248
  },
248
249
  locale: 'en',
@@ -290,7 +291,9 @@ export async function seedAchievements(payload, input) {
290
291
  data: {
291
292
  ...baseData,
292
293
  name: nameParts.en ?? achievement.slug,
293
- ...(descriptionParts.en != null ? { description: descriptionParts.en } : {}),
294
+ ...(descriptionParts.en != null
295
+ ? { description: plainTextToLexical(descriptionParts.en) }
296
+ : {}),
294
297
  },
295
298
  locale: 'en',
296
299
  overrideAccess: true,
@@ -2,7 +2,8 @@ export type CatalogProgressItem = {
2
2
  id: string;
3
3
  name?: string;
4
4
  slug?: string;
5
- description?: string;
5
+ /** Same as the achievement collection field (Lexical JSON, or legacy string). */
6
+ description?: unknown;
6
7
  completedAt?: string | null;
7
8
  earned: boolean;
8
9
  };
@@ -10,14 +11,15 @@ export type CatalogProgressGroup = {
10
11
  id: string;
11
12
  title: string;
12
13
  slug?: string;
13
- description?: string;
14
+ /** Same as the achievement collection field (Lexical JSON, or legacy string). */
15
+ description?: unknown;
14
16
  items: CatalogProgressItem[];
15
17
  };
16
18
  type CatalogDoc = {
17
19
  id: string;
18
20
  name?: string;
19
21
  slug?: string;
20
- description?: string | null;
22
+ description?: unknown;
21
23
  completionRules?: unknown;
22
24
  };
23
25
  type GrantDoc = {
@@ -1 +1 @@
1
- {"version":3,"file":"buildCatalogProgressGroups.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/buildCatalogProgressGroups.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,mBAAmB,EAAE,CAAA;CAC7B,CAAA;AAED,KAAK,UAAU,GAAG;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAWD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,YAAY,EAAE,UAAU,EAAE,CAAA;IAC1B,MAAM,EAAE,QAAQ,EAAE,CAAA;CACnB,GAAG,oBAAoB,EAAE,CA2DzB"}
1
+ {"version":3,"file":"buildCatalogProgressGroups.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/buildCatalogProgressGroups.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,KAAK,EAAE,mBAAmB,EAAE,CAAA;CAC7B,CAAA;AAED,KAAK,UAAU,GAAG;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAiBD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,YAAY,EAAE,UAAU,EAAE,CAAA;IAC1B,MAAM,EAAE,QAAQ,EAAE,CAAA;CACnB,GAAG,oBAAoB,EAAE,CAyDzB"}
@@ -7,6 +7,13 @@ function grantKey(achievement) {
7
7
  : '';
8
8
  return { id, slug };
9
9
  }
10
+ function passDescription(value) {
11
+ if (typeof value === 'string' && value.trim().length > 0)
12
+ return value;
13
+ if (value != null && typeof value === 'object')
14
+ return value;
15
+ return undefined;
16
+ }
10
17
  /**
11
18
  * Groups catalog achievements by composite parents (`completionRules`).
12
19
  * Children follow completion-rule order. Unearned children stay in the list with `earned: false`.
@@ -27,14 +34,13 @@ export function buildCatalogProgressGroups(args) {
27
34
  .map((doc) => [String(doc.slug), doc]));
28
35
  const toItem = (doc) => {
29
36
  const grant = grantsById.get(doc.id) ?? (doc.slug ? grantsBySlug.get(doc.slug) : undefined);
37
+ const description = passDescription(doc.description);
30
38
  return {
31
39
  id: doc.id,
32
40
  earned: Boolean(grant),
33
41
  ...(typeof doc.name === 'string' && doc.name ? { name: doc.name } : {}),
34
42
  ...(typeof doc.slug === 'string' && doc.slug ? { slug: doc.slug } : {}),
35
- ...(typeof doc.description === 'string' && doc.description
36
- ? { description: doc.description }
37
- : {}),
43
+ ...(description !== undefined ? { description } : {}),
38
44
  completedAt: grant?.completedAt ?? null,
39
45
  };
40
46
  };
@@ -56,13 +62,12 @@ export function buildCatalogProgressGroups(args) {
56
62
  seen.add(child.id);
57
63
  items.push(toItem(child));
58
64
  }
65
+ const description = passDescription(parent.description);
59
66
  return {
60
67
  id: parent.id,
61
68
  title: parent.name || parent.slug || parent.id,
62
69
  ...(parent.slug ? { slug: parent.slug } : {}),
63
- ...(typeof parent.description === 'string' && parent.description
64
- ? { description: parent.description }
65
- : {}),
70
+ ...(description !== undefined ? { description } : {}),
66
71
  items,
67
72
  };
68
73
  });
@@ -1,9 +1,10 @@
1
1
  import type { PaginatedDocs, Payload, PayloadRequest } from 'payload';
2
+ /** Catalog slice — `description` matches the collection (Lexical JSON, or legacy string). */
2
3
  type LeanCatalog = {
3
4
  id: string;
4
5
  name?: string;
5
6
  slug?: string;
6
- description?: string;
7
+ description?: unknown;
7
8
  };
8
9
  type LeanTier = LeanCatalog & {
9
10
  rank?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"getUserProgress.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/getUserProgress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAOrE,KAAK,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AACrF,KAAK,QAAQ,GAAG,WAAW,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAExE,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAiBD,iBAAS,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;EAU9C;AAED,iBAAS,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;EAShD;AAoBD,MAAM,MAAM,sBAAsB,GAAG,aAAa,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,GAAG;IACjF,sCAAsC;IACtC,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,QAAQ,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,EAAE,CAAA;CAC3C,CAAA;AAkED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,CAAC,EAAE,qBAAqB,CAAA;IAC9B,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAiDlC"}
1
+ {"version":3,"file":"getUserProgress.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/getUserProgress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAOrE,6FAA6F;AAC7F,KAAK,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AACtF,KAAK,QAAQ,GAAG,WAAW,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAExE,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAmBD,iBAAS,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;EAU9C;AAED,iBAAS,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;EAShD;AAoBD,MAAM,MAAM,sBAAsB,GAAG,aAAa,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,GAAG;IACjF,sCAAsC;IACtC,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,QAAQ,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,EAAE,CAAA;CAC3C,CAAA;AAkED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,CAAC,EAAE,qBAAqB,CAAA;IAC9B,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAiDlC"}
@@ -9,13 +9,14 @@ function leanCatalog(value) {
9
9
  if (!value || typeof value !== 'object')
10
10
  return { id };
11
11
  const row = value;
12
+ const description = row.description;
13
+ const hasDescription = (typeof description === 'string' && description.trim().length > 0) ||
14
+ (description != null && typeof description === 'object');
12
15
  return {
13
16
  id,
14
17
  ...(typeof row.name === 'string' && row.name ? { name: row.name } : {}),
15
18
  ...(typeof row.slug === 'string' && row.slug ? { slug: row.slug } : {}),
16
- ...(typeof row.description === 'string' && row.description
17
- ? { description: row.description }
18
- : {}),
19
+ ...(hasDescription ? { description } : {}),
19
20
  };
20
21
  }
21
22
  function leanGrant(doc) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dappermountain/payload-plugin-achievements",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "A robust, Admin-configured achievements engine for Payload: composable unlock rules, derived rank progress, request/review grants, metrics, and scoped multi-tenant support.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -69,16 +69,20 @@
69
69
  "skills:check": "bunx skills check -p"
70
70
  },
71
71
  "peerDependencies": {
72
+ "@payloadcms/richtext-lexical": "^3.88.0",
72
73
  "@payloadcms/ui": "^3.88.0",
74
+ "lucide-react": "^0.543.0",
73
75
  "payload": "^3.88.0",
74
76
  "react": "^19.0.0"
75
77
  },
76
78
  "devDependencies": {
77
79
  "@dappermountain/agent-payload": "^0.1.5",
78
80
  "@dappermountain/agent-practices": "^0.1.2",
81
+ "@payloadcms/richtext-lexical": "3.88.0",
79
82
  "@payloadcms/ui": "3.88.0",
80
83
  "@types/bun": "1.4.0",
81
84
  "@types/react": "^19.0.0",
85
+ "lucide-react": "0.543.0",
82
86
  "payload": "3.88.0",
83
87
  "prettier": "^3.6.2",
84
88
  "react": "19.2.4",