@dappermountain/payload-plugin-achievements 0.6.0 → 1.0.0-beta.1
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 +31 -16
- package/dist/admin/LucideIconField.d.ts +3 -2
- package/dist/admin/LucideIconField.d.ts.map +1 -1
- package/dist/admin/LucideIconField.js +2 -2
- package/dist/collections/EventTypes/access/index.d.ts +1 -11
- package/dist/collections/EventTypes/access/index.d.ts.map +1 -1
- package/dist/collections/Metrics/access/index.d.ts +1 -11
- package/dist/collections/Metrics/access/index.d.ts.map +1 -1
- package/dist/collections/Reactions/access/index.d.ts +1 -11
- package/dist/collections/Reactions/access/index.d.ts.map +1 -1
- package/dist/collections/index.d.ts.map +1 -1
- package/dist/collections/index.js +13 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -1,31 +1,46 @@
|
|
|
1
1
|
# `@dappermountain/payload-plugin-achievements`
|
|
2
2
|
|
|
3
|
-
Achievements,
|
|
3
|
+
Achievements, tiers, points, and an activity log for Payload CMS — all configured in Admin.
|
|
4
4
|
|
|
5
|
-
You describe what people can earn and what has to happen first. Your app records those happenings. The plugin derives the current
|
|
5
|
+
You describe what people can earn and what has to happen first. Your app records those happenings. The plugin derives the current tier and progress from that history, so the ladder stays in step with real activity.
|
|
6
6
|
|
|
7
7
|
## What you get
|
|
8
8
|
|
|
9
9
|
- **A catalog in Admin.** Achievements, tiers, metrics, event types, and reactions. Editors change rules there.
|
|
10
10
|
- **Composable rules.** Nested all-of / any-of trees, plus your own leaf types.
|
|
11
|
-
- **Derived
|
|
11
|
+
- **Derived tiers.** Current tier and next-tier fill come from the same unlock rules.
|
|
12
12
|
- **Request and review.** Members can ask for an achievement; reviewers approve or reject. Composed achievements can grant themselves when their completion rules pass.
|
|
13
|
-
- **An activity log.** Grants, score changes,
|
|
13
|
+
- **An activity log.** Grants, score changes, tier moves, and your own event kinds.
|
|
14
14
|
- **Optional multi-tenant scope.** Point `scope` at your tenants / workspaces / orgs collection.
|
|
15
15
|
- **Host hooks.** Prefixable slugs, collection overrides, a review policy you inject, and server helpers for trusted code.
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## Payload version support
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
19
|
+
| Plugin line | Payload | Notes |
|
|
20
|
+
| ----------- | ------- | ----- |
|
|
21
|
+
| **`1.x`** (this package — e.g. `1.0.0-beta.1`) | **4** (`^4.0.0-beta.1` / `^4.0.0`) | Current line. Opt out of P4 default versions/authorship on high-churn collections. |
|
|
22
|
+
| **`0.6.x`** | **3** (`^3.89`) | Last Payload 3 line. Same product features (reactions, composites, etc.). Stay on `0.6.x` until you upgrade the host to Payload 4. |
|
|
23
|
+
|
|
24
|
+
One published version cannot peer both Payload majors (Admin field types and collection defaults diverge). Pick the line that matches your host.
|
|
25
|
+
|
|
26
|
+
## Requirements (1.x)
|
|
27
|
+
|
|
28
|
+
- Payload `^4.0.0-beta.1` / `^4.0.0`
|
|
29
|
+
- `@payloadcms/ui` (same train as Payload)
|
|
30
|
+
- `@payloadcms/richtext-lexical` (same train as Payload)
|
|
22
31
|
- `lucide-react` `^0.543`
|
|
23
32
|
- React `^19`
|
|
24
33
|
|
|
34
|
+
Plugin collections opt out of Payload 4’s default versions and authorship (append-only logs and high-churn grants should not grow `_versions` tables).
|
|
35
|
+
|
|
25
36
|
## Install
|
|
26
37
|
|
|
27
38
|
```bash
|
|
28
|
-
|
|
39
|
+
# Payload 4 hosts
|
|
40
|
+
bun add @dappermountain/payload-plugin-achievements@^1.0.0-beta.1
|
|
41
|
+
|
|
42
|
+
# Payload 3 hosts (stay on the 0.6 line)
|
|
43
|
+
bun add @dappermountain/payload-plugin-achievements@0.6
|
|
29
44
|
```
|
|
30
45
|
|
|
31
46
|
`npm` and other clients work too. Next.js hosts should list the package in `transpilePackages` so the `'use client'` Admin fields compile.
|
|
@@ -63,7 +78,7 @@ Think of four layers:
|
|
|
63
78
|
|
|
64
79
|
1. **Catalog** — named things in Admin (event types, metrics, tiers, achievements, reactions).
|
|
65
80
|
2. **Log** — what happened. Your hooks call `recordLog` / `recordMetricChange`. Grant and revoke write their own rows.
|
|
66
|
-
3. **Grants** — what someone has earned. One row per user (+ scope). Creating or deleting a grant updates the log and re-checks composed achievements and
|
|
81
|
+
3. **Grants** — what someone has earned. One row per user (+ scope). Creating or deleting a grant updates the log and re-checks composed achievements and tiers.
|
|
67
82
|
4. **Reactions** — set-based side effects when a host event is logged or an achievement is granted/revoked.
|
|
68
83
|
|
|
69
84
|
Writing a host or metric log also re-checks composed achievements and derived tiers. Grant and revoke logs skip that path because the Grants hooks already ran it.
|
|
@@ -81,7 +96,7 @@ Slugs use the prefix `achievement-` so they sit beside your own collections. Ove
|
|
|
81
96
|
| `achievements` | `achievement-definitions` | Earnable items with eligibility and completion rules. |
|
|
82
97
|
| `grants` | `achievement-grants` | One earned achievement per user (+ scope). |
|
|
83
98
|
| `achievementRequests` | `achievement-requests` | Request → pending / approved / rejected. |
|
|
84
|
-
| `tierRequests` | `achievement-tier-requests` |
|
|
99
|
+
| `tierRequests` | `achievement-tier-requests` | Tier unlock waiting on review (`requiresReview` on the tier). |
|
|
85
100
|
| `logs` | `achievement-logs` | Activity history. |
|
|
86
101
|
| `reactions` | `achievement-reactions` | Side effects when something is logged or granted. |
|
|
87
102
|
|
|
@@ -98,11 +113,11 @@ Flags on the type decide which log fields Admin asks for:
|
|
|
98
113
|
| Requires an actor | `actor` — who caused it (a peer, an admin, …) |
|
|
99
114
|
| Adjusts a metric | `metric` + `change` |
|
|
100
115
|
| Requires a subject | `subject` — the host document (a post, a comment, a user, …) |
|
|
101
|
-
| Snapshot actor tiers | stamps the actor’s derived
|
|
116
|
+
| Snapshot actor tiers | stamps the actor’s derived tier at write time |
|
|
102
117
|
|
|
103
|
-
**Actor tier** on an `event-count` rule then counts only logs whose snapshot already met that
|
|
118
|
+
**Actor tier** on an `event-count` rule then counts only logs whose snapshot already met that tier in this scope. Leave Actor tier blank to count every matching event. A filter only matches logs that were stamped.
|
|
104
119
|
|
|
105
|
-
Unscoped logs stamp every ladder. Scoped logs stamp that tenant plus any unscoped
|
|
120
|
+
Unscoped logs stamp every ladder. Scoped logs stamp that tenant plus any unscoped tiers.
|
|
106
121
|
|
|
107
122
|
## Subjects
|
|
108
123
|
|
|
@@ -161,7 +176,7 @@ Unlock (tiers), eligibility, and completion (achievements) share one tree:
|
|
|
161
176
|
|
|
162
177
|
**Eligibility** gates who may request an achievement. **Completion** describes what a composed achievement is made of. When `requiresReview` is off and completion passes, the parent grants itself. Nested achievement graphs skip cycles.
|
|
163
178
|
|
|
164
|
-
**Current tier** walks
|
|
179
|
+
**Current tier** walks the ladder in `rank` order (`resolveCurrentTier`). A tier with `requiresReview` becomes current after an **approved** tier request. Turning `requiresReview` off approves pending requests for that row and writes missing `tier.changed` logs for people who already meet the unlock rules.
|
|
165
180
|
|
|
166
181
|
**Next-tier fill** (`resolveTierProgress` / `evaluateRuleProgress`):
|
|
167
182
|
|
|
@@ -380,7 +395,7 @@ achievementPlugin({
|
|
|
380
395
|
| Option | Default | Notes |
|
|
381
396
|
| --- | --- | --- |
|
|
382
397
|
| `me` | `'/achievements/me'` | Signed-in user’s grants, derived tiers, lean requests. `?reviews=1` adds a review snapshot. Always the caller’s own snapshot. `false` skips it. |
|
|
383
|
-
| `leaderboard` | `'/achievements/leaderboard'` | Stored-metric
|
|
398
|
+
| `leaderboard` | `'/achievements/leaderboard'` | Stored-metric leaderboard. `false` skips it. |
|
|
384
399
|
| `reconcile` | `'/achievements/reconcile'` | Reviewer repair endpoint. `false` skips it. |
|
|
385
400
|
|
|
386
401
|
`me` query: `scope`, `limit`, `page`, `requestsLimit`, `reviews`. Leaderboard: `metric` (required), `scope`, `limit`, `page`.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TextFieldClientProps } from 'payload';
|
|
2
|
+
import React from 'react';
|
|
2
3
|
/**
|
|
3
4
|
* Emoji-picker style Lucide field: searchable icon grid, stores kebab-case names.
|
|
4
5
|
*/
|
|
5
|
-
export declare
|
|
6
|
+
export declare function LucideIconField(props: TextFieldClientProps): React.JSX.Element;
|
|
6
7
|
//# sourceMappingURL=LucideIconField.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LucideIconField.d.ts","sourceRoot":"","sources":["../../src/admin/LucideIconField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"LucideIconField.d.ts","sourceRoot":"","sources":["../../src/admin/LucideIconField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAGnD,OAAO,KAA4D,MAAM,OAAO,CAAA;AAiChF;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,qBAsO1D"}
|
|
@@ -28,7 +28,7 @@ const CELL = 36;
|
|
|
28
28
|
/**
|
|
29
29
|
* Emoji-picker style Lucide field: searchable icon grid, stores kebab-case names.
|
|
30
30
|
*/
|
|
31
|
-
export
|
|
31
|
+
export function LucideIconField(props) {
|
|
32
32
|
const { field, path: pathFromProps, readOnly } = props;
|
|
33
33
|
const path = pathFromProps ?? field?.name ?? 'icon';
|
|
34
34
|
const { errorMessage, setValue, showError, value } = useField({
|
|
@@ -168,4 +168,4 @@ export const LucideIconField = (props) => {
|
|
|
168
168
|
: totalMatches > MAX_VISIBLE
|
|
169
169
|
? `Showing ${MAX_VISIBLE} of ${totalMatches} — refine search`
|
|
170
170
|
: `${totalMatches} icon${totalMatches === 1 ? '' : 's'}` })] })) : null] }), _jsx(FieldDescription, { description: field?.admin?.description, path: path }), _jsx(FieldError, { message: errorMessage, path: path, showError: showError })] }));
|
|
171
|
-
}
|
|
171
|
+
}
|
|
@@ -1,12 +1,2 @@
|
|
|
1
|
-
export declare const access:
|
|
2
|
-
admin?: ({ req }: {
|
|
3
|
-
req: import("payload").PayloadRequest;
|
|
4
|
-
}) => boolean | Promise<boolean>;
|
|
5
|
-
create?: import("payload").Access;
|
|
6
|
-
delete?: import("payload").Access;
|
|
7
|
-
read?: import("payload").Access;
|
|
8
|
-
readVersions?: import("payload").Access;
|
|
9
|
-
unlock?: import("payload").Access;
|
|
10
|
-
update?: import("payload").Access;
|
|
11
|
-
};
|
|
1
|
+
export declare const access: import("payload").CollectionAccess<any>;
|
|
12
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/EventTypes/access/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/EventTypes/access/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM,yCAAiC,CAAA"}
|
|
@@ -1,12 +1,2 @@
|
|
|
1
|
-
export declare const access:
|
|
2
|
-
admin?: ({ req }: {
|
|
3
|
-
req: import("payload").PayloadRequest;
|
|
4
|
-
}) => boolean | Promise<boolean>;
|
|
5
|
-
create?: import("payload").Access;
|
|
6
|
-
delete?: import("payload").Access;
|
|
7
|
-
read?: import("payload").Access;
|
|
8
|
-
readVersions?: import("payload").Access;
|
|
9
|
-
unlock?: import("payload").Access;
|
|
10
|
-
update?: import("payload").Access;
|
|
11
|
-
};
|
|
1
|
+
export declare const access: import("payload").CollectionAccess<any>;
|
|
12
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Metrics/access/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Metrics/access/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM,yCAA8B,CAAA"}
|
|
@@ -1,12 +1,2 @@
|
|
|
1
|
-
export declare const access:
|
|
2
|
-
admin?: ({ req }: {
|
|
3
|
-
req: import("payload").PayloadRequest;
|
|
4
|
-
}) => boolean | Promise<boolean>;
|
|
5
|
-
create?: import("payload").Access;
|
|
6
|
-
delete?: import("payload").Access;
|
|
7
|
-
read?: import("payload").Access;
|
|
8
|
-
readVersions?: import("payload").Access;
|
|
9
|
-
unlock?: import("payload").Access;
|
|
10
|
-
update?: import("payload").Access;
|
|
11
|
-
};
|
|
1
|
+
export declare const access: import("payload").CollectionAccess<any>;
|
|
12
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Reactions/access/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Reactions/access/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM,yCAAgC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collections/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collections/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AA0C/C,qEAAqE;AACrE,wBAAgB,2BAA2B,IAAI,gBAAgB,EAAE,CAMhE"}
|
|
@@ -22,8 +22,20 @@ const builders = {
|
|
|
22
22
|
logs: buildLogsCollection,
|
|
23
23
|
reactions: buildReactionsCollection,
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Payload 4 enables versions + authorship by default. Achievement collections are
|
|
27
|
+
* high-churn catalogs and append-only logs — opt out so hosts do not grow `_versions`
|
|
28
|
+
* / authorship columns for every grant, request, and metric balance.
|
|
29
|
+
*/
|
|
30
|
+
function withPayload4Defaults(collection) {
|
|
31
|
+
return {
|
|
32
|
+
...collection,
|
|
33
|
+
versions: collection.versions ?? false,
|
|
34
|
+
authorship: collection.authorship ?? false,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
25
37
|
/** Build achievement collections after plugin options are stored. */
|
|
26
38
|
export function buildAchievementCollections() {
|
|
27
39
|
const overrides = getAchievementOptions().collections?.overrides;
|
|
28
|
-
return Object.keys(builders).map((key) => applyCollectionOverrides(builders[key](), overrides?.[key]));
|
|
40
|
+
return Object.keys(builders).map((key) => applyCollectionOverrides(withPayload4Defaults(builders[key]()), overrides?.[key]));
|
|
29
41
|
}
|