@dappermountain/payload-plugin-achievements 0.1.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.
Files changed (237) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +319 -0
  3. package/dist/access/helpers/index.d.ts +9 -0
  4. package/dist/access/helpers/index.d.ts.map +1 -0
  5. package/dist/access/helpers/index.js +25 -0
  6. package/dist/access/index.d.ts +3 -0
  7. package/dist/access/index.d.ts.map +1 -0
  8. package/dist/access/index.js +2 -0
  9. package/dist/access/roles/index.d.ts +15 -0
  10. package/dist/access/roles/index.d.ts.map +1 -0
  11. package/dist/access/roles/index.js +55 -0
  12. package/dist/admin/ActorField.d.ts +9 -0
  13. package/dist/admin/ActorField.d.ts.map +1 -0
  14. package/dist/admin/ActorField.js +19 -0
  15. package/dist/admin/MetricChangeField.d.ts +9 -0
  16. package/dist/admin/MetricChangeField.d.ts.map +1 -0
  17. package/dist/admin/MetricChangeField.js +21 -0
  18. package/dist/admin/MetricField.d.ts +9 -0
  19. package/dist/admin/MetricField.d.ts.map +1 -0
  20. package/dist/admin/MetricField.js +23 -0
  21. package/dist/admin/useEventTypeFlags.d.ts +7 -0
  22. package/dist/admin/useEventTypeFlags.d.ts.map +1 -0
  23. package/dist/admin/useEventTypeFlags.js +68 -0
  24. package/dist/catalog.d.ts +21 -0
  25. package/dist/catalog.d.ts.map +1 -0
  26. package/dist/catalog.js +9 -0
  27. package/dist/collections/AchievementRequests/access/index.d.ts +3 -0
  28. package/dist/collections/AchievementRequests/access/index.d.ts.map +1 -0
  29. package/dist/collections/AchievementRequests/access/index.js +8 -0
  30. package/dist/collections/AchievementRequests/hooks/index.d.ts +3 -0
  31. package/dist/collections/AchievementRequests/hooks/index.d.ts.map +1 -0
  32. package/dist/collections/AchievementRequests/hooks/index.js +5 -0
  33. package/dist/collections/AchievementRequests/hooks/prepareAchievementRequest.d.ts +12 -0
  34. package/dist/collections/AchievementRequests/hooks/prepareAchievementRequest.d.ts.map +1 -0
  35. package/dist/collections/AchievementRequests/hooks/prepareAchievementRequest.js +105 -0
  36. package/dist/collections/AchievementRequests/index.d.ts +3 -0
  37. package/dist/collections/AchievementRequests/index.d.ts.map +1 -0
  38. package/dist/collections/AchievementRequests/index.js +39 -0
  39. package/dist/collections/Achievements/access/index.d.ts +3 -0
  40. package/dist/collections/Achievements/access/index.d.ts.map +1 -0
  41. package/dist/collections/Achievements/access/index.js +2 -0
  42. package/dist/collections/Achievements/hooks/index.d.ts +3 -0
  43. package/dist/collections/Achievements/hooks/index.d.ts.map +1 -0
  44. package/dist/collections/Achievements/hooks/index.js +4 -0
  45. package/dist/collections/Achievements/hooks/validateEligibilityRules.d.ts +3 -0
  46. package/dist/collections/Achievements/hooks/validateEligibilityRules.d.ts.map +1 -0
  47. package/dist/collections/Achievements/hooks/validateEligibilityRules.js +8 -0
  48. package/dist/collections/Achievements/index.d.ts +3 -0
  49. package/dist/collections/Achievements/index.d.ts.map +1 -0
  50. package/dist/collections/Achievements/index.js +62 -0
  51. package/dist/collections/EventTypes/access/index.d.ts +12 -0
  52. package/dist/collections/EventTypes/access/index.d.ts.map +1 -0
  53. package/dist/collections/EventTypes/access/index.js +2 -0
  54. package/dist/collections/EventTypes/hooks/index.d.ts +3 -0
  55. package/dist/collections/EventTypes/hooks/index.d.ts.map +1 -0
  56. package/dist/collections/EventTypes/hooks/index.js +6 -0
  57. package/dist/collections/EventTypes/index.d.ts +3 -0
  58. package/dist/collections/EventTypes/index.d.ts.map +1 -0
  59. package/dist/collections/EventTypes/index.js +67 -0
  60. package/dist/collections/Grants/access/index.d.ts +3 -0
  61. package/dist/collections/Grants/access/index.d.ts.map +1 -0
  62. package/dist/collections/Grants/access/index.js +8 -0
  63. package/dist/collections/Grants/hooks/ensureUniqueGrant.d.ts +4 -0
  64. package/dist/collections/Grants/hooks/ensureUniqueGrant.d.ts.map +1 -0
  65. package/dist/collections/Grants/hooks/ensureUniqueGrant.js +34 -0
  66. package/dist/collections/Grants/hooks/index.d.ts +3 -0
  67. package/dist/collections/Grants/hooks/index.d.ts.map +1 -0
  68. package/dist/collections/Grants/hooks/index.js +7 -0
  69. package/dist/collections/Grants/hooks/setGrantTitle.d.ts +4 -0
  70. package/dist/collections/Grants/hooks/setGrantTitle.d.ts.map +1 -0
  71. package/dist/collections/Grants/hooks/setGrantTitle.js +47 -0
  72. package/dist/collections/Grants/index.d.ts +3 -0
  73. package/dist/collections/Grants/index.d.ts.map +1 -0
  74. package/dist/collections/Grants/index.js +62 -0
  75. package/dist/collections/Logs/access/index.d.ts +3 -0
  76. package/dist/collections/Logs/access/index.d.ts.map +1 -0
  77. package/dist/collections/Logs/access/index.js +8 -0
  78. package/dist/collections/Logs/fields/validators.d.ts +5 -0
  79. package/dist/collections/Logs/fields/validators.d.ts.map +1 -0
  80. package/dist/collections/Logs/fields/validators.js +44 -0
  81. package/dist/collections/Logs/hooks/ensureTypeRequirements.d.ts +3 -0
  82. package/dist/collections/Logs/hooks/ensureTypeRequirements.d.ts.map +1 -0
  83. package/dist/collections/Logs/hooks/ensureTypeRequirements.js +35 -0
  84. package/dist/collections/Logs/hooks/index.d.ts +3 -0
  85. package/dist/collections/Logs/hooks/index.d.ts.map +1 -0
  86. package/dist/collections/Logs/hooks/index.js +4 -0
  87. package/dist/collections/Logs/index.d.ts +4 -0
  88. package/dist/collections/Logs/index.d.ts.map +1 -0
  89. package/dist/collections/Logs/index.js +113 -0
  90. package/dist/collections/Metrics/access/index.d.ts +12 -0
  91. package/dist/collections/Metrics/access/index.d.ts.map +1 -0
  92. package/dist/collections/Metrics/access/index.js +2 -0
  93. package/dist/collections/Metrics/hooks/index.d.ts +3 -0
  94. package/dist/collections/Metrics/hooks/index.d.ts.map +1 -0
  95. package/dist/collections/Metrics/hooks/index.js +6 -0
  96. package/dist/collections/Metrics/index.d.ts +3 -0
  97. package/dist/collections/Metrics/index.d.ts.map +1 -0
  98. package/dist/collections/Metrics/index.js +35 -0
  99. package/dist/collections/Tiers/access/index.d.ts +3 -0
  100. package/dist/collections/Tiers/access/index.d.ts.map +1 -0
  101. package/dist/collections/Tiers/access/index.js +2 -0
  102. package/dist/collections/Tiers/hooks/index.d.ts +3 -0
  103. package/dist/collections/Tiers/hooks/index.d.ts.map +1 -0
  104. package/dist/collections/Tiers/hooks/index.js +4 -0
  105. package/dist/collections/Tiers/hooks/validateUnlockRules.d.ts +3 -0
  106. package/dist/collections/Tiers/hooks/validateUnlockRules.d.ts.map +1 -0
  107. package/dist/collections/Tiers/hooks/validateUnlockRules.js +7 -0
  108. package/dist/collections/Tiers/index.d.ts +3 -0
  109. package/dist/collections/Tiers/index.d.ts.map +1 -0
  110. package/dist/collections/Tiers/index.js +81 -0
  111. package/dist/collections/access/definitions.d.ts +5 -0
  112. package/dist/collections/access/definitions.d.ts.map +1 -0
  113. package/dist/collections/access/definitions.js +11 -0
  114. package/dist/collections/applyOverrides.d.ts +9 -0
  115. package/dist/collections/applyOverrides.d.ts.map +1 -0
  116. package/dist/collections/applyOverrides.js +34 -0
  117. package/dist/collections/fields/catalogFilter.d.ts +8 -0
  118. package/dist/collections/fields/catalogFilter.d.ts.map +1 -0
  119. package/dist/collections/fields/catalogFilter.js +10 -0
  120. package/dist/collections/fields/index.d.ts +6 -0
  121. package/dist/collections/fields/index.d.ts.map +1 -0
  122. package/dist/collections/fields/index.js +5 -0
  123. package/dist/collections/fields/localizedText.d.ts +10 -0
  124. package/dist/collections/fields/localizedText.d.ts.map +1 -0
  125. package/dist/collections/fields/localizedText.js +20 -0
  126. package/dist/collections/fields/ruleFields.d.ts +8 -0
  127. package/dist/collections/fields/ruleFields.d.ts.map +1 -0
  128. package/dist/collections/fields/ruleFields.js +123 -0
  129. package/dist/collections/fields/scopeField.d.ts +7 -0
  130. package/dist/collections/fields/scopeField.d.ts.map +1 -0
  131. package/dist/collections/fields/scopeField.js +22 -0
  132. package/dist/collections/fields/userField.d.ts +4 -0
  133. package/dist/collections/fields/userField.d.ts.map +1 -0
  134. package/dist/collections/fields/userField.js +12 -0
  135. package/dist/collections/fields/usersAchievementsGroup.d.ts +8 -0
  136. package/dist/collections/fields/usersAchievementsGroup.d.ts.map +1 -0
  137. package/dist/collections/fields/usersAchievementsGroup.js +36 -0
  138. package/dist/collections/helpers.d.ts +16 -0
  139. package/dist/collections/helpers.d.ts.map +1 -0
  140. package/dist/collections/helpers.js +31 -0
  141. package/dist/collections/hooks/eachRuleLeaf.d.ts +3 -0
  142. package/dist/collections/hooks/eachRuleLeaf.d.ts.map +1 -0
  143. package/dist/collections/hooks/eachRuleLeaf.js +20 -0
  144. package/dist/collections/hooks/index.d.ts +4 -0
  145. package/dist/collections/hooks/index.d.ts.map +1 -0
  146. package/dist/collections/hooks/index.js +3 -0
  147. package/dist/collections/hooks/lockSystemCatalogFields.d.ts +7 -0
  148. package/dist/collections/hooks/lockSystemCatalogFields.d.ts.map +1 -0
  149. package/dist/collections/hooks/lockSystemCatalogFields.js +15 -0
  150. package/dist/collections/hooks/preventCatalogDelete.d.ts +5 -0
  151. package/dist/collections/hooks/preventCatalogDelete.d.ts.map +1 -0
  152. package/dist/collections/hooks/preventCatalogDelete.js +89 -0
  153. package/dist/collections/index.d.ts +4 -0
  154. package/dist/collections/index.d.ts.map +1 -0
  155. package/dist/collections/index.js +23 -0
  156. package/dist/collections/slugs.d.ts +22 -0
  157. package/dist/collections/slugs.d.ts.map +1 -0
  158. package/dist/collections/slugs.js +51 -0
  159. package/dist/defaults.d.ts +21 -0
  160. package/dist/defaults.d.ts.map +1 -0
  161. package/dist/defaults.js +31 -0
  162. package/dist/enabled.d.ts +4 -0
  163. package/dist/enabled.d.ts.map +1 -0
  164. package/dist/enabled.js +9 -0
  165. package/dist/endpoints/index.d.ts +4 -0
  166. package/dist/endpoints/index.d.ts.map +1 -0
  167. package/dist/endpoints/index.js +6 -0
  168. package/dist/endpoints/me.d.ts +7 -0
  169. package/dist/endpoints/me.d.ts.map +1 -0
  170. package/dist/endpoints/me.js +50 -0
  171. package/dist/exports/client.d.ts +4 -0
  172. package/dist/exports/client.d.ts.map +1 -0
  173. package/dist/exports/client.js +3 -0
  174. package/dist/extensions/registry.d.ts +3 -0
  175. package/dist/extensions/registry.d.ts.map +1 -0
  176. package/dist/extensions/registry.js +12 -0
  177. package/dist/index.d.ts +13 -0
  178. package/dist/index.d.ts.map +1 -0
  179. package/dist/index.js +8 -0
  180. package/dist/options-store.d.ts +7 -0
  181. package/dist/options-store.d.ts.map +1 -0
  182. package/dist/options-store.js +15 -0
  183. package/dist/plugin.d.ts +8 -0
  184. package/dist/plugin.d.ts.map +1 -0
  185. package/dist/plugin.js +31 -0
  186. package/dist/seed/index.d.ts +63 -0
  187. package/dist/seed/index.d.ts.map +1 -0
  188. package/dist/seed/index.js +375 -0
  189. package/dist/services/achievement/buildCatalogProgressGroups.d.ts +37 -0
  190. package/dist/services/achievement/buildCatalogProgressGroups.d.ts.map +1 -0
  191. package/dist/services/achievement/buildCatalogProgressGroups.js +70 -0
  192. package/dist/services/achievement/builtInRules.d.ts +3 -0
  193. package/dist/services/achievement/builtInRules.d.ts.map +1 -0
  194. package/dist/services/achievement/builtInRules.js +280 -0
  195. package/dist/services/achievement/evaluateRules.d.ts +48 -0
  196. package/dist/services/achievement/evaluateRules.d.ts.map +1 -0
  197. package/dist/services/achievement/evaluateRules.js +206 -0
  198. package/dist/services/achievement/eventTypeRequiresActor.d.ts +24 -0
  199. package/dist/services/achievement/eventTypeRequiresActor.d.ts.map +1 -0
  200. package/dist/services/achievement/eventTypeRequiresActor.js +46 -0
  201. package/dist/services/achievement/getUserProgress.d.ts +53 -0
  202. package/dist/services/achievement/getUserProgress.d.ts.map +1 -0
  203. package/dist/services/achievement/getUserProgress.js +163 -0
  204. package/dist/services/achievement/index.d.ts +14 -0
  205. package/dist/services/achievement/index.d.ts.map +1 -0
  206. package/dist/services/achievement/index.js +9 -0
  207. package/dist/services/achievement/recordEvent.d.ts +41 -0
  208. package/dist/services/achievement/recordEvent.d.ts.map +1 -0
  209. package/dist/services/achievement/recordEvent.js +126 -0
  210. package/dist/services/achievement/relationId.d.ts +3 -0
  211. package/dist/services/achievement/relationId.d.ts.map +1 -0
  212. package/dist/services/achievement/relationId.js +15 -0
  213. package/dist/services/achievement/resolveCatalog.d.ts +14 -0
  214. package/dist/services/achievement/resolveCatalog.d.ts.map +1 -0
  215. package/dist/services/achievement/resolveCatalog.js +34 -0
  216. package/dist/services/achievement/resolveCurrentTier.d.ts +21 -0
  217. package/dist/services/achievement/resolveCurrentTier.d.ts.map +1 -0
  218. package/dist/services/achievement/resolveCurrentTier.js +51 -0
  219. package/dist/services/achievement/resolveTierProgress.d.ts +35 -0
  220. package/dist/services/achievement/resolveTierProgress.d.ts.map +1 -0
  221. package/dist/services/achievement/resolveTierProgress.js +99 -0
  222. package/dist/services/achievement/reviewAchievementRequest.d.ts +11 -0
  223. package/dist/services/achievement/reviewAchievementRequest.d.ts.map +1 -0
  224. package/dist/services/achievement/reviewAchievementRequest.js +37 -0
  225. package/dist/services/achievement/submitAchievementRequest.d.ts +21 -0
  226. package/dist/services/achievement/submitAchievementRequest.d.ts.map +1 -0
  227. package/dist/services/achievement/submitAchievementRequest.js +126 -0
  228. package/dist/services/achievement/validateRules.d.ts +11 -0
  229. package/dist/services/achievement/validateRules.d.ts.map +1 -0
  230. package/dist/services/achievement/validateRules.js +74 -0
  231. package/dist/services/achievement/where.d.ts +9 -0
  232. package/dist/services/achievement/where.d.ts.map +1 -0
  233. package/dist/services/achievement/where.js +23 -0
  234. package/dist/types.d.ts +117 -0
  235. package/dist/types.d.ts.map +1 -0
  236. package/dist/types.js +1 -0
  237. package/package.json +85 -0
@@ -0,0 +1,62 @@
1
+ import { collectionAdmin, collectionOf, columnsWithOptionalScope, optionalFields, slugOf } from '../helpers';
2
+ import { scopeField, userField } from '../fields';
3
+ import { access } from './access';
4
+ import { hooks } from './hooks';
5
+ export function buildGrantsCollection() {
6
+ return {
7
+ slug: slugOf('grants'),
8
+ labels: {
9
+ singular: 'Achievement Grant',
10
+ plural: 'Achievement Grants',
11
+ },
12
+ access: { ...access },
13
+ hooks,
14
+ admin: collectionAdmin({
15
+ useAsTitle: 'title',
16
+ defaultColumns: columnsWithOptionalScope(['title', 'user', 'completedAt', 'scope']),
17
+ listSearchableFields: ['title'],
18
+ description: 'What someone has earned — one row per grant. Bulk create works here.',
19
+ }),
20
+ fields: [
21
+ {
22
+ name: 'title',
23
+ type: 'text',
24
+ index: true,
25
+ admin: {
26
+ readOnly: true,
27
+ description: 'Copied from the achievement name for lists and search.',
28
+ },
29
+ },
30
+ ...optionalFields(scopeField()),
31
+ userField(),
32
+ {
33
+ type: 'row',
34
+ fields: [
35
+ {
36
+ name: 'achievement',
37
+ type: 'relationship',
38
+ relationTo: collectionOf('achievements'),
39
+ required: true,
40
+ index: true,
41
+ admin: { width: '60%' },
42
+ },
43
+ {
44
+ name: 'completedAt',
45
+ type: 'date',
46
+ required: true,
47
+ defaultValue: () => new Date().toISOString(),
48
+ admin: {
49
+ width: '40%',
50
+ date: { pickerAppearance: 'dayAndTime' },
51
+ },
52
+ },
53
+ ],
54
+ },
55
+ {
56
+ name: 'note',
57
+ type: 'textarea',
58
+ admin: { description: 'Optional note about why this was granted.' },
59
+ },
60
+ ],
61
+ };
62
+ }
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ export declare const access: NonNullable<CollectionConfig['access']>;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Logs/access/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAK/C,eAAO,MAAM,MAAM,EAAE,WAAW,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAK1D,CAAA"}
@@ -0,0 +1,8 @@
1
+ import { requireOne } from '../../../access/index';
2
+ import { isReviewer, isSelfUser } from '../../../access/roles/index';
3
+ export const access = {
4
+ read: requireOne(isReviewer('logs'), isSelfUser('user')),
5
+ create: requireOne(isReviewer('logs')),
6
+ update: requireOne(isReviewer('logs')),
7
+ delete: requireOne(isReviewer('logs')),
8
+ };
@@ -0,0 +1,5 @@
1
+ import type { Validate } from 'payload';
2
+ export declare const validateActor: Validate;
3
+ export declare const validateMetric: Validate;
4
+ export declare const validateChange: Validate;
5
+ //# sourceMappingURL=validators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../src/collections/Logs/fields/validators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAQvC,eAAO,MAAM,aAAa,EAAE,QAW3B,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,QAW5B,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,QAW5B,CAAA"}
@@ -0,0 +1,44 @@
1
+ import { eventTypeRequiresActor, eventTypeRequiresMetric, } from '../../../services/achievement/eventTypeRequiresActor';
2
+ import { relationId } from '../../../services/achievement/relationId';
3
+ export const validateActor = async (value, { data, req }) => {
4
+ if (!req?.payload)
5
+ return true;
6
+ const typeId = relationId(data?.type);
7
+ if (!typeId)
8
+ return true;
9
+ if (!(await eventTypeRequiresActor({ payload: req.payload, req, typeIdOrDoc: typeId }))) {
10
+ return true;
11
+ }
12
+ if (!relationId(value)) {
13
+ return 'This log type needs an actor (who caused it).';
14
+ }
15
+ return true;
16
+ };
17
+ export const validateMetric = async (value, { data, req }) => {
18
+ if (!req?.payload)
19
+ return true;
20
+ const typeId = relationId(data?.type);
21
+ if (!typeId)
22
+ return true;
23
+ if (!(await eventTypeRequiresMetric({ payload: req.payload, req, typeIdOrDoc: typeId }))) {
24
+ return true;
25
+ }
26
+ if (!relationId(value)) {
27
+ return 'This log type needs a metric.';
28
+ }
29
+ return true;
30
+ };
31
+ export const validateChange = async (value, { data, req }) => {
32
+ if (!req?.payload)
33
+ return true;
34
+ const typeId = relationId(data?.type);
35
+ if (!typeId)
36
+ return true;
37
+ if (!(await eventTypeRequiresMetric({ payload: req.payload, req, typeIdOrDoc: typeId }))) {
38
+ return true;
39
+ }
40
+ if (typeof value !== 'number' || Number.isNaN(value)) {
41
+ return 'This log type needs a change amount.';
42
+ }
43
+ return true;
44
+ };
@@ -0,0 +1,3 @@
1
+ import type { CollectionBeforeValidateHook } from 'payload';
2
+ export declare const ensureTypeRequirements: CollectionBeforeValidateHook;
3
+ //# sourceMappingURL=ensureTypeRequirements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensureTypeRequirements.d.ts","sourceRoot":"","sources":["../../../../src/collections/Logs/hooks/ensureTypeRequirements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAA;AAS3D,eAAO,MAAM,sBAAsB,EAAE,4BAkCpC,CAAA"}
@@ -0,0 +1,35 @@
1
+ import { APIError } from 'payload';
2
+ import { eventTypeRequiresActor, eventTypeRequiresMetric, } from '../../../services/achievement/eventTypeRequiresActor';
3
+ import { relationId } from '../../../services/achievement/relationId';
4
+ export const ensureTypeRequirements = async ({ data, req }) => {
5
+ if (!data)
6
+ return data;
7
+ const typeId = relationId(data.type);
8
+ if (!typeId)
9
+ return data;
10
+ const flags = {
11
+ requiresActor: await eventTypeRequiresActor({
12
+ payload: req.payload,
13
+ req,
14
+ typeIdOrDoc: typeId,
15
+ }),
16
+ requiresMetric: await eventTypeRequiresMetric({
17
+ payload: req.payload,
18
+ req,
19
+ typeIdOrDoc: typeId,
20
+ }),
21
+ };
22
+ if (flags.requiresActor && !relationId(data.actor)) {
23
+ throw new APIError('This log type needs an actor (who caused it).', 400);
24
+ }
25
+ if (flags.requiresMetric) {
26
+ if (!relationId(data.metric)) {
27
+ throw new APIError('This log type needs a metric.', 400);
28
+ }
29
+ const change = data.change;
30
+ if (typeof change !== 'number' || Number.isNaN(change)) {
31
+ throw new APIError('This log type needs a change amount.', 400);
32
+ }
33
+ }
34
+ return data;
35
+ };
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ export declare const hooks: CollectionConfig['hooks'];
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Logs/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAI/C,eAAO,MAAM,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAE3C,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { ensureTypeRequirements } from './ensureTypeRequirements';
2
+ export const hooks = {
3
+ beforeValidate: [ensureTypeRequirements],
4
+ };
@@ -0,0 +1,4 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ /** Append-only occurrence log. `type` / `metric` are catalog relationships. */
3
+ export declare function buildLogsCollection(): CollectionConfig;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/Logs/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAS,MAAM,SAAS,CAAA;AAUtD,+EAA+E;AAC/E,wBAAgB,mBAAmB,IAAI,gBAAgB,CA0GtD"}
@@ -0,0 +1,113 @@
1
+ import { catalogFilterOptions } from '../fields/catalogFilter';
2
+ import { collectionAdmin, collectionOf, optionalFields, slugOf } from '../helpers';
3
+ import { getAchievementOptions } from '../../options-store';
4
+ import { scopeField, userField } from '../fields';
5
+ import { access } from './access';
6
+ import { validateActor, validateChange, validateMetric } from './fields/validators';
7
+ import { hooks } from './hooks';
8
+ /** Append-only occurrence log. `type` / `metric` are catalog relationships. */
9
+ export function buildLogsCollection() {
10
+ const users = getAchievementOptions().usersCollectionSlug ?? 'users';
11
+ const eventTypesSlug = collectionOf('eventTypes');
12
+ return {
13
+ slug: slugOf('logs'),
14
+ labels: {
15
+ singular: 'Achievement Log',
16
+ plural: 'Achievement Logs',
17
+ },
18
+ access: { ...access },
19
+ hooks,
20
+ admin: collectionAdmin({
21
+ useAsTitle: 'id',
22
+ defaultColumns: ['type', 'user', 'actor', 'metric', 'change', 'createdAt'],
23
+ description: 'A running history of what happened — grants, score changes, tier moves, and anything else you log.',
24
+ }),
25
+ fields: [
26
+ ...optionalFields(scopeField()),
27
+ userField(),
28
+ {
29
+ type: 'row',
30
+ fields: [
31
+ {
32
+ name: 'type',
33
+ type: 'relationship',
34
+ relationTo: collectionOf('eventTypes'),
35
+ required: true,
36
+ index: true,
37
+ filterOptions: catalogFilterOptions,
38
+ admin: { width: '50%', description: 'What kind of thing happened.' },
39
+ },
40
+ {
41
+ name: 'actor',
42
+ type: 'relationship',
43
+ relationTo: users,
44
+ index: true,
45
+ validate: validateActor,
46
+ admin: {
47
+ width: '50%',
48
+ description: 'Who caused it.',
49
+ components: {
50
+ Field: {
51
+ path: '@dappermountain/payload-plugin-achievements/client#ActorField',
52
+ clientProps: { eventTypesSlug },
53
+ },
54
+ },
55
+ },
56
+ },
57
+ {
58
+ name: 'metric',
59
+ type: 'relationship',
60
+ relationTo: collectionOf('metrics'),
61
+ filterOptions: catalogFilterOptions,
62
+ validate: validateMetric,
63
+ admin: {
64
+ width: '50%',
65
+ description: 'Which score moved.',
66
+ components: {
67
+ Field: {
68
+ path: '@dappermountain/payload-plugin-achievements/client#MetricField',
69
+ clientProps: { eventTypesSlug },
70
+ },
71
+ },
72
+ },
73
+ },
74
+ {
75
+ name: 'change',
76
+ type: 'number',
77
+ validate: validateChange,
78
+ admin: {
79
+ width: '50%',
80
+ description: 'How much it changed (for example 10 or −3).',
81
+ components: {
82
+ Field: {
83
+ path: '@dappermountain/payload-plugin-achievements/client#MetricChangeField',
84
+ clientProps: { eventTypesSlug },
85
+ },
86
+ },
87
+ },
88
+ },
89
+ ],
90
+ },
91
+ {
92
+ type: 'collapsible',
93
+ label: 'Details',
94
+ admin: {
95
+ initCollapsed: true,
96
+ description: 'Extra detail if you need it.',
97
+ },
98
+ fields: [
99
+ {
100
+ name: 'reason',
101
+ type: 'text',
102
+ admin: { description: 'A short note about why this was recorded.' },
103
+ },
104
+ {
105
+ name: 'data',
106
+ type: 'json',
107
+ admin: { description: 'Any extra structured details.' },
108
+ },
109
+ ],
110
+ },
111
+ ],
112
+ };
113
+ }
@@ -0,0 +1,12 @@
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
+ };
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Metrics/access/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM;;;;;;;;;;CAA8B,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { definitionAccess } from '../../access/definitions';
2
+ export const access = definitionAccess('metrics');
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ export declare const hooks: CollectionConfig['hooks'];
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Metrics/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAK/C,eAAO,MAAM,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAG3C,CAAA"}
@@ -0,0 +1,6 @@
1
+ import { lockSystemCatalogFields } from '../../hooks/lockSystemCatalogFields';
2
+ import { preventCatalogDelete } from '../../hooks/preventCatalogDelete';
3
+ export const hooks = {
4
+ beforeChange: [lockSystemCatalogFields],
5
+ beforeDelete: [preventCatalogDelete({ kind: 'metric' })],
6
+ };
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ export declare function buildMetricsCollection(): CollectionConfig;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/Metrics/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAO/C,wBAAgB,sBAAsB,IAAI,gBAAgB,CA8BzD"}
@@ -0,0 +1,35 @@
1
+ import { collectionAdmin, columnsWithOptionalScope, optionalFields, slugOf } from '../helpers';
2
+ import { localizedNameField, scopeField } from '../fields';
3
+ import { access } from './access';
4
+ import { hooks } from './hooks';
5
+ export function buildMetricsCollection() {
6
+ return {
7
+ slug: slugOf('metrics'),
8
+ access: { ...access },
9
+ hooks,
10
+ admin: collectionAdmin({
11
+ useAsTitle: 'name',
12
+ defaultColumns: columnsWithOptionalScope(['name', 'slug', 'system', 'scope']),
13
+ description: 'Scores and totals you track for someone — like points or a streak.',
14
+ }),
15
+ fields: [
16
+ ...optionalFields(scopeField()),
17
+ {
18
+ type: 'row',
19
+ fields: [
20
+ localizedNameField({ width: '50%' }),
21
+ { name: 'slug', type: 'text', required: true, unique: true, index: true, admin: { width: '50%' } },
22
+ ],
23
+ },
24
+ {
25
+ name: 'system',
26
+ type: 'checkbox',
27
+ defaultValue: false,
28
+ admin: {
29
+ readOnly: true,
30
+ description: 'Built-in metric. It can’t be deleted.',
31
+ },
32
+ },
33
+ ],
34
+ };
35
+ }
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ export declare const access: NonNullable<CollectionConfig['access']>;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Tiers/access/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAI/C,eAAO,MAAM,MAAM,EAAE,WAAW,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAA6B,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { definitionAccess } from '../../access/definitions';
2
+ export const access = definitionAccess('tiers');
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ export declare const hooks: CollectionConfig['hooks'];
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Tiers/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAI/C,eAAO,MAAM,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAE3C,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { validateUnlockRules } from './validateUnlockRules';
2
+ export const hooks = {
3
+ beforeValidate: [validateUnlockRules],
4
+ };
@@ -0,0 +1,3 @@
1
+ import type { CollectionBeforeValidateHook } from 'payload';
2
+ export declare const validateUnlockRules: CollectionBeforeValidateHook;
3
+ //# sourceMappingURL=validateUnlockRules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateUnlockRules.d.ts","sourceRoot":"","sources":["../../../../src/collections/Tiers/hooks/validateUnlockRules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAA;AAI3D,eAAO,MAAM,mBAAmB,EAAE,4BAOjC,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { assertRuleGroupValid } from '../../../services/achievement/validateRules';
2
+ export const validateUnlockRules = ({ data }) => {
3
+ if (!data)
4
+ return data;
5
+ assertRuleGroupValid(data.unlockRules, 'Unlock rules');
6
+ return data;
7
+ };
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ export declare function buildTiersCollection(): CollectionConfig;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,CA+EvD"}
@@ -0,0 +1,81 @@
1
+ import { collectionAdmin, columnsWithOptionalScope, optionalFields, slugOf } from '../helpers';
2
+ import { buildRuleTreeFields, localizedDescriptionField, localizedNameField, scopeField } from '../fields';
3
+ import { getAchievementOptions } from '../../options-store';
4
+ import { access } from './access';
5
+ import { hooks } from './hooks';
6
+ export function buildTiersCollection() {
7
+ const mediaCollection = getAchievementOptions().mediaCollection?.trim();
8
+ return {
9
+ slug: slugOf('tiers'),
10
+ access: { ...access },
11
+ hooks,
12
+ admin: collectionAdmin({
13
+ useAsTitle: 'name',
14
+ defaultColumns: columnsWithOptionalScope(['name', 'slug', 'rank', 'scope']),
15
+ description: 'Unlock rules decide when someone reaches this step. Current tier is derived by walking the ladder (lowest rank first) — there is no stored progress row.',
16
+ }),
17
+ fields: [
18
+ ...optionalFields(scopeField()),
19
+ {
20
+ type: 'row',
21
+ fields: [
22
+ localizedNameField({ width: '40%' }),
23
+ {
24
+ name: 'slug',
25
+ type: 'text',
26
+ required: true,
27
+ unique: true,
28
+ index: true,
29
+ admin: { width: '35%' },
30
+ },
31
+ {
32
+ name: 'rank',
33
+ type: 'number',
34
+ required: true,
35
+ defaultValue: 0,
36
+ admin: {
37
+ width: '25%',
38
+ description: 'Lower numbers are earlier on the ladder (0 is the first step).',
39
+ },
40
+ },
41
+ ],
42
+ },
43
+ localizedDescriptionField(),
44
+ {
45
+ type: 'row',
46
+ fields: [
47
+ {
48
+ name: 'icon',
49
+ type: 'text',
50
+ admin: {
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.',
53
+ },
54
+ },
55
+ ...(mediaCollection
56
+ ? [
57
+ {
58
+ name: 'image',
59
+ type: 'upload',
60
+ relationTo: mediaCollection,
61
+ admin: {
62
+ width: '50%',
63
+ description: 'Optional badge image. Wins over icon when both are set.',
64
+ },
65
+ },
66
+ ]
67
+ : []),
68
+ ],
69
+ },
70
+ {
71
+ name: 'unlockRules',
72
+ type: 'group',
73
+ label: 'Unlock rules',
74
+ admin: {
75
+ description: 'Rules that must pass to unlock this tier (all-of or any-of). Empty rules mean this step is open. The plugin derives current tier by walking ranks; optional tier.changed logs record moves.',
76
+ },
77
+ fields: buildRuleTreeFields(),
78
+ },
79
+ ],
80
+ };
81
+ }
@@ -0,0 +1,5 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ import type { AchievementCollectionKey } from '../../types';
3
+ /** Catalog / definition collections (writes gated by `canReview`). */
4
+ export declare function definitionAccess(collectionKey: AchievementCollectionKey): NonNullable<CollectionConfig['access']>;
5
+ //# sourceMappingURL=definitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../../src/collections/access/definitions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAI3D,sEAAsE;AACtE,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,wBAAwB,GACtC,WAAW,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAOzC"}
@@ -0,0 +1,11 @@
1
+ import { isAuthenticated, requireOne } from '../../access/index';
2
+ import { isReviewer } from '../../access/roles/index';
3
+ /** Catalog / definition collections (writes gated by `canReview`). */
4
+ export function definitionAccess(collectionKey) {
5
+ return {
6
+ read: isAuthenticated,
7
+ create: requireOne(isReviewer(collectionKey)),
8
+ update: requireOne(isReviewer(collectionKey)),
9
+ delete: requireOne(isReviewer(collectionKey)),
10
+ };
11
+ }
@@ -0,0 +1,9 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ import type { AchievementCollectionOverride } from '../types';
3
+ /**
4
+ * Layer host overrides on a plugin collection:
5
+ * - `access` / `admin` / `labels`: shallow merge (host wins per key)
6
+ * - `hooks`: append host hook arrays after the plugin’s (plugin runs first)
7
+ */
8
+ export declare function applyCollectionOverrides(collection: CollectionConfig, override?: AchievementCollectionOverride): CollectionConfig;
9
+ //# sourceMappingURL=applyOverrides.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyOverrides.d.ts","sourceRoot":"","sources":["../../src/collections/applyOverrides.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAE7D;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,gBAAgB,EAC5B,QAAQ,CAAC,EAAE,6BAA6B,GACvC,gBAAgB,CA6BlB"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Layer host overrides on a plugin collection:
3
+ * - `access` / `admin` / `labels`: shallow merge (host wins per key)
4
+ * - `hooks`: append host hook arrays after the plugin’s (plugin runs first)
5
+ */
6
+ export function applyCollectionOverrides(collection, override) {
7
+ if (!override)
8
+ return collection;
9
+ const hooks = { ...(collection.hooks ?? {}) };
10
+ if (override.hooks) {
11
+ for (const key of Object.keys(override.hooks)) {
12
+ const hostHooks = override.hooks[key];
13
+ if (hostHooks == null)
14
+ continue;
15
+ const existing = hooks[key];
16
+ const base = (Array.isArray(existing) ? existing : existing ? [existing] : []);
17
+ const extra = (Array.isArray(hostHooks) ? hostHooks : [hostHooks]);
18
+ hooks[key] = [...base, ...extra];
19
+ }
20
+ }
21
+ return {
22
+ ...collection,
23
+ access: {
24
+ ...collection.access,
25
+ ...override.access,
26
+ },
27
+ admin: {
28
+ ...collection.admin,
29
+ ...override.admin,
30
+ },
31
+ ...(override.labels ? { labels: override.labels } : {}),
32
+ hooks,
33
+ };
34
+ }
@@ -0,0 +1,8 @@
1
+ import type { Where } from 'payload';
2
+ /** Relationship picker: same scope as the parent doc, or unscoped (global) catalog rows. */
3
+ export declare function catalogFilterOptions({ data }: {
4
+ data?: {
5
+ scope?: unknown;
6
+ };
7
+ }): Where | true;
8
+ //# sourceMappingURL=catalogFilter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogFilter.d.ts","sourceRoot":"","sources":["../../../src/collections/fields/catalogFilter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAIpC,4FAA4F;AAC5F,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAAG,KAAK,GAAG,IAAI,CAM3F"}
@@ -0,0 +1,10 @@
1
+ import { relationId } from '../../services/achievement/relationId';
2
+ /** Relationship picker: same scope as the parent doc, or unscoped (global) catalog rows. */
3
+ export function catalogFilterOptions({ data }) {
4
+ const scopeId = relationId(data?.scope);
5
+ if (!scopeId)
6
+ return true;
7
+ return {
8
+ or: [{ scope: { equals: scopeId } }, { scope: { exists: false } }, { scope: { equals: null } }],
9
+ };
10
+ }