ecoportal-api-graphql 1.3.13 → 1.3.16

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 (275) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -0
  3. data/lib/ecoportal/api/common/graphql/client.rb +0 -2
  4. data/lib/ecoportal/api/common/graphql/http_client.rb +6 -0
  5. data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -17
  6. data/lib/ecoportal/api/graphql/base/file_container.rb +14 -14
  7. data/lib/ecoportal/api/graphql/base/location_classification_type.rb +15 -15
  8. data/lib/ecoportal/api/graphql/base/location_structure.rb +27 -27
  9. data/lib/ecoportal/api/graphql/base/organization.rb +15 -15
  10. data/lib/ecoportal/api/graphql/base/page/task.rb +24 -0
  11. data/lib/ecoportal/api/graphql/base/page.rb +17 -16
  12. data/lib/ecoportal/api/graphql/base/template.rb +35 -0
  13. data/lib/ecoportal/api/graphql/base.rb +37 -35
  14. data/lib/ecoportal/api/graphql/builder/contractor_entity.rb +41 -41
  15. data/lib/ecoportal/api/graphql/compat/response.rb +35 -35
  16. data/lib/ecoportal/api/graphql/connection/action.rb +11 -11
  17. data/lib/ecoportal/api/graphql/connection/action_category.rb +11 -11
  18. data/lib/ecoportal/api/graphql/connection/contractor_entity.rb +11 -11
  19. data/lib/ecoportal/api/graphql/connection/page.rb +11 -11
  20. data/lib/ecoportal/api/graphql/connection/person_member.rb +11 -11
  21. data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -11
  22. data/lib/ecoportal/api/graphql/connection.rb +17 -17
  23. data/lib/ecoportal/api/graphql/file_upload/client.rb +21 -10
  24. data/lib/ecoportal/api/graphql/fragment/field_configuration.rb +38 -0
  25. data/lib/ecoportal/api/graphql/fragment/force.rb +14 -10
  26. data/lib/ecoportal/api/graphql/fragment/location_node.rb +26 -26
  27. data/lib/ecoportal/api/graphql/fragment/page_task.rb +39 -0
  28. data/lib/ecoportal/api/graphql/fragment/template.rb +51 -0
  29. data/lib/ecoportal/api/graphql/fragment.rb +3 -0
  30. data/lib/ecoportal/api/graphql/input/contractor_entity.rb +14 -14
  31. data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -16
  32. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -18
  33. data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -21
  34. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -28
  35. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -18
  36. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -23
  37. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -20
  38. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -20
  39. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -24
  40. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -20
  41. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -20
  42. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -20
  43. data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -18
  44. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -18
  45. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -18
  46. data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -251
  47. data/lib/ecoportal/api/graphql/input.rb +24 -24
  48. data/lib/ecoportal/api/graphql/logic/connection.rb +34 -34
  49. data/lib/ecoportal/api/graphql/model/page/task.rb +16 -0
  50. data/lib/ecoportal/api/graphql/model/page.rb +16 -15
  51. data/lib/ecoportal/api/graphql/model/person_member.rb +15 -15
  52. data/lib/ecoportal/api/graphql/model/template.rb +15 -0
  53. data/lib/ecoportal/api/graphql/model.rb +32 -30
  54. data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -45
  55. data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -40
  56. data/lib/ecoportal/api/graphql/mutation/contractor_entity.rb +14 -14
  57. data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -40
  58. data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +38 -40
  59. data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +38 -40
  60. data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +38 -40
  61. data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +7 -7
  62. data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +8 -7
  63. data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +38 -40
  64. data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -40
  65. data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -40
  66. data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -40
  67. data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +34 -35
  68. data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -35
  69. data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -36
  70. data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -40
  71. data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -13
  72. data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -46
  73. data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +1 -1
  74. data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +1 -1
  75. data/lib/ecoportal/api/graphql/mutation.rb +20 -20
  76. data/lib/ecoportal/api/graphql/payload/contractor_entity.rb +14 -14
  77. data/lib/ecoportal/api/graphql/payload/ok_payload.rb +36 -21
  78. data/lib/ecoportal/api/graphql/payload/page/draft.rb +26 -13
  79. data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -13
  80. data/lib/ecoportal/api/graphql/payload/page/review_task_batch.rb +23 -0
  81. data/lib/ecoportal/api/graphql/payload/page.rb +20 -19
  82. data/lib/ecoportal/api/graphql/payload/preset_view.rb +15 -11
  83. data/lib/ecoportal/api/graphql/payload/register.rb +15 -11
  84. data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +1 -1
  85. data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +1 -1
  86. data/lib/ecoportal/api/graphql/payload/template/update_information.rb +1 -1
  87. data/lib/ecoportal/api/graphql/payload.rb +23 -23
  88. data/lib/ecoportal/api/graphql/query/location_structures.rb +57 -57
  89. data/lib/ecoportal/api/graphql/query/page.rb +45 -45
  90. data/lib/ecoportal/api/graphql/query/page_with_forces.rb +9 -3
  91. data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +9 -3
  92. data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -78
  93. data/lib/ecoportal/api/graphql/query.rb +27 -27
  94. data/lib/ecoportal/api/graphql.rb +9 -5
  95. data/lib/ecoportal/api/graphql_version.rb +1 -1
  96. metadata +9 -180
  97. data/.ai-assistance/bridge/CLAUDE.md +0 -338
  98. data/.ai-assistance/bridge/archive/.gitkeep +0 -0
  99. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +0 -29
  100. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.outbox.md +0 -18
  101. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +0 -42
  102. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.outbox.md +0 -115
  103. data/.ai-assistance/bridge/context/gemini-review-prompt.txt +0 -48
  104. data/.ai-assistance/bridge/context/gemini-review-response.md +0 -104
  105. data/.ai-assistance/bridge/context/project.md +0 -42
  106. data/.ai-assistance/bridge/inbox/.gitkeep +0 -0
  107. data/.ai-assistance/bridge/outbox/.gitkeep +0 -0
  108. data/.ai-assistance/bridge/outbox/request-for-standards-discovery.md +0 -48
  109. data/.ai-assistance/bridge/queue/.gitkeep +0 -1
  110. data/.ai-assistance/capabilities/CLAUDE.md +0 -27
  111. data/.ai-assistance/capabilities/assumptions-log.md +0 -80
  112. data/.ai-assistance/capabilities/code.md +0 -47
  113. data/.ai-assistance/capabilities/connectors.md +0 -37
  114. data/.ai-assistance/capabilities/cowork.md +0 -55
  115. data/.ai-assistance/code/OVERVIEW.md +0 -155
  116. data/.ai-assistance/code/data_fields.md +0 -242
  117. data/.ai-assistance/code/dependencies.md +0 -151
  118. data/.ai-assistance/code/diff_as_input.md +0 -234
  119. data/.ai-assistance/code/diff_pairing_engine.md +0 -243
  120. data/.ai-assistance/code/diff_service_deep_dive.md +0 -192
  121. data/.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md +0 -55
  122. data/.ai-assistance/code/ecoPortal_architecture/01_terminology_dictionary.md +0 -181
  123. data/.ai-assistance/code/ecoPortal_architecture/02_data_model.md +0 -192
  124. data/.ai-assistance/code/ecoPortal_architecture/03_api_layers.md +0 -147
  125. data/.ai-assistance/code/ecoPortal_architecture/04_graphql_queries_mutations.md +0 -277
  126. data/.ai-assistance/code/ecoPortal_architecture/05_page_workflows.md +0 -200
  127. data/.ai-assistance/code/ecoPortal_architecture/06_search_and_filters.md +0 -228
  128. data/.ai-assistance/code/ecoPortal_architecture/07_data_fields.md +0 -197
  129. data/.ai-assistance/code/ecoPortal_architecture/08_stages_sections.md +0 -243
  130. data/.ai-assistance/code/ecoPortal_architecture/09_people_contractors_locations.md +0 -196
  131. data/.ai-assistance/code/ecoPortal_architecture/10_forces_workflow_builder.md +0 -132
  132. data/.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md +0 -187
  133. data/.ai-assistance/code/ecoPortal_architecture/12_ai_documentation_sources_gaps.md +0 -236
  134. data/.ai-assistance/code/ecoPortal_architecture/13_ai_infrastructure.md +0 -183
  135. data/.ai-assistance/code/ecoportal_schema_reference.md +0 -240
  136. data/.ai-assistance/code/filter_contract_matrix.md +0 -177
  137. data/.ai-assistance/code/graphql_domain_knowledge.md +0 -240
  138. data/.ai-assistance/code/refactoring/datafield-readwrite-shape-asymmetry.md +0 -71
  139. data/.ai-assistance/code/refactoring/opportunities.md +0 -251
  140. data/.ai-assistance/code/schema_analysis.md +0 -321
  141. data/.ai-assistance/code/search_filters.md +0 -868
  142. data/.ai-assistance/code/spec_coverage.md +0 -73
  143. data/.ai-assistance/code/template_diff_pairing_domain.md +0 -175
  144. data/.ai-assistance/code/workflow-command-guide.md +0 -466
  145. data/.ai-assistance/code/workflow-space.md +0 -353
  146. data/.ai-assistance/conventions/CLAUDE.md +0 -30
  147. data/.ai-assistance/conventions/code-working-tree-protocol.md +0 -199
  148. data/.ai-assistance/conventions/gitignore-rules.md +0 -42
  149. data/.ai-assistance/conventions/permission-guidance.md +0 -120
  150. data/.ai-assistance/integrations/README.md +0 -70
  151. data/.ai-assistance/integrations/gitkraken-mcp.md +0 -107
  152. data/.ai-assistance/integrations/gitlab-mcp.md +0 -123
  153. data/.ai-assistance/integrations/local-git.md +0 -60
  154. data/.ai-assistance/local_paths.example.md +0 -17
  155. data/.ai-assistance/projects/TODO.md +0 -118
  156. data/.ai-assistance/projects/api-v2-to-graphql-migration/DECISIONS.md +0 -168
  157. data/.ai-assistance/projects/api-v2-to-graphql-migration/INTENT.md +0 -60
  158. data/.ai-assistance/projects/api-v2-to-graphql-migration/TODO.md +0 -267
  159. data/.ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md +0 -53
  160. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md +0 -102
  161. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md +0 -139
  162. data/.ai-assistance/projects/compat-layer-audit/COMPAT_AUDIT.md +0 -244
  163. data/.ai-assistance/projects/dynamic-model-generation/INTENT.md +0 -93
  164. data/.ai-assistance/projects/eco-helpers-compat/INTENT.md +0 -244
  165. data/.ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md +0 -266
  166. data/.ai-assistance/projects/eco-helpers-compat/TODO.md +0 -86
  167. data/.ai-assistance/projects/ecoportal-api-v2-doublemodel-review/INTENT.md +0 -101
  168. data/.ai-assistance/projects/graphql-agent/GAP_ANALYSIS.md +0 -177
  169. data/.ai-assistance/projects/ooze-graphql-native-migration/DECISIONS.md +0 -161
  170. data/.ai-assistance/projects/ooze-graphql-native-migration/INTENT.md +0 -125
  171. data/.ai-assistance/projects/ooze-graphql-native-migration/INVENTORY.md +0 -136
  172. data/.ai-assistance/projects/ooze-graphql-native-migration/RISKS.md +0 -126
  173. data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +0 -261
  174. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-06-30-cutover-workflow-deep-review.md +0 -122
  175. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-07-01-forces-via-workflow-commands-miss-rca.md +0 -148
  176. data/.ai-assistance/projects/page-model/DECISIONS.md +0 -245
  177. data/.ai-assistance/projects/page-model/TODO.md +0 -190
  178. data/.ai-assistance/projects/qa-services-delivery/DECISIONS.md +0 -93
  179. data/.ai-assistance/projects/qa-services-delivery/INTENT.md +0 -76
  180. data/.ai-assistance/projects/qa-services-delivery/PHASE3-SCOPE.md +0 -115
  181. data/.ai-assistance/projects/qa-services-delivery/ROADMAP.md +0 -99
  182. data/.ai-assistance/projects/qa-services-delivery/TODO.md +0 -81
  183. data/.ai-assistance/projects/search-filter-builder/INTENT.md +0 -107
  184. data/.ai-assistance/projects/search-filter-builder/TODO.md +0 -131
  185. data/.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md +0 -87
  186. data/.ai-assistance/projects/template-automatic-build-maintenance/TODO.md +0 -108
  187. data/.ai-assistance/projects/template-diff-deploy/INTENT.md +0 -12
  188. data/.ai-assistance/projects/template-diff-deploy/TODO.md +0 -9
  189. data/.ai-assistance/projects/template-maintenance/DESIGN.md +0 -134
  190. data/.ai-assistance/projects/template-maintenance/PHASE0-FINDINGS.md +0 -93
  191. data/.ai-assistance/projects/template-maintenance/README.md +0 -14
  192. data/.ai-assistance/projects/template-model-logic/CATEGORY_CATALOG.md +0 -236
  193. data/.ai-assistance/projects/template-model-logic/CLASSIFICATION_SPIKE.md +0 -243
  194. data/.ai-assistance/projects/template-model-logic/DESIGN_NOTE.md +0 -154
  195. data/.ai-assistance/projects/workflow-space/TODO.md +0 -213
  196. data/.ai-assistance/reinstall-claude-desktop-windows.md +0 -136
  197. data/.ai-assistance/scripts/CLAUDE.md +0 -150
  198. data/.ai-assistance/scripts/bridge-inbox-check.sh +0 -75
  199. data/.ai-assistance/scripts/bridge-init.sh +0 -86
  200. data/.ai-assistance/scripts/bridge-status.sh +0 -44
  201. data/.ai-assistance/scripts/capabilities-check.ts +0 -104
  202. data/.ai-assistance/scripts/check-outbox.sh +0 -43
  203. data/.ai-assistance/scripts/dep_graph.rb +0 -91
  204. data/.ai-assistance/scripts/lock-acquire.sh +0 -103
  205. data/.ai-assistance/scripts/lock-multi.sh +0 -124
  206. data/.ai-assistance/scripts/lock-queue.sh +0 -94
  207. data/.ai-assistance/scripts/setup-mcps.test.ts +0 -188
  208. data/.ai-assistance/scripts/setup-mcps.ts +0 -234
  209. data/.ai-assistance/scripts/task-complete.ts +0 -74
  210. data/.ai-assistance/scripts/task-create.ts +0 -75
  211. data/.ai-assistance/scripts/task-read.ts +0 -125
  212. data/.ai-assistance/scripts/token-logger.js +0 -220
  213. data/.ai-assistance/scripts/token-report.ts +0 -158
  214. data/.ai-assistance/scripts/token-session-start.js +0 -66
  215. data/.ai-assistance/skills/ai-instructions/SKILL.md +0 -48
  216. data/.ai-assistance/skills/code-specs/SKILL.md +0 -69
  217. data/.ai-assistance/skills/corporate-policies/SKILL.md +0 -201
  218. data/.ai-assistance/skills/dep-graph/SKILL.md +0 -139
  219. data/.ai-assistance/skills/ep-ai-manager/SKILL.md +0 -417
  220. data/.ai-assistance/skills/gemini-assist/SKILL.md +0 -63
  221. data/.ai-assistance/skills/gemini-assist/gemini-mcp-server.js +0 -205
  222. data/.ai-assistance/skills/gemini-assist/gemini_ask.py +0 -1
  223. data/.ai-assistance/skills/gemini-assist/gemini_ask.rb +0 -240
  224. data/.ai-assistance/skills/gemini-assist/prompts/cycle_end_review.txt +0 -25
  225. data/.ai-assistance/skills/graphql-schema-analysis/SKILL.md +0 -261
  226. data/.ai-assistance/skills/procedural-memory/SKILL.md +0 -319
  227. data/.ai-assistance/skills/project-cycle/SKILL.md +0 -177
  228. data/.ai-assistance/skills/project-self-docs/SKILL.md +0 -181
  229. data/.ai-assistance/skills/project-self-docs/scripts/self_docs_scan.py +0 -378
  230. data/.ai-assistance/skills/refactor/SKILL.md +0 -62
  231. data/.ai-assistance/skills/rubocop/SKILL.md +0 -93
  232. data/.ai-assistance/skills/ruby-scripting/SKILL.md +0 -215
  233. data/.ai-assistance/skills/spec-generation/SKILL.md +0 -72
  234. data/.ai-assistance/standards-version.json +0 -22
  235. data/.ai-assistance/token-budget.json +0 -32
  236. data/.ai-assistance/version.json +0 -39
  237. data/.claude/settings.json +0 -150
  238. data/.env.example +0 -18
  239. data/.gitattributes +0 -15
  240. data/.gitignore +0 -37
  241. data/.gitlab-ci.yml +0 -45
  242. data/.markdownlint.json +0 -4
  243. data/.rspec +0 -3
  244. data/.rubocop.yml +0 -121
  245. data/.ruby-version +0 -1
  246. data/.yardopts +0 -10
  247. data/CLAUDE.md +0 -243
  248. data/Gemfile +0 -30
  249. data/Rakefile +0 -90
  250. data/bin/console +0 -14
  251. data/bin/setup +0 -8
  252. data/bin/setup.ps1 +0 -6
  253. data/docs/self-docs/ARCHITECTURE.md +0 -88
  254. data/docs/self-docs/CHANGES.jsonl +0 -12
  255. data/docs/self-docs/COMPLIANCE.md +0 -79
  256. data/docs/self-docs/CONVENTIONS.md +0 -74
  257. data/docs/self-docs/INTEGRATIONS.md +0 -65
  258. data/docs/self-docs/OPERATIONS.md +0 -76
  259. data/docs/self-docs/OVERVIEW.md +0 -64
  260. data/docs/self-docs/STATUS.md +0 -73
  261. data/docs/self-docs/self-docs-index.json +0 -51
  262. data/docs/worklog.md +0 -977
  263. data/ecoportal-api-graphql.gemspec +0 -40
  264. data/scripts/auto-worker-scheduler.sh +0 -386
  265. data/tests/actions_get.rb +0 -7
  266. data/tests/contractor_entities_get.rb +0 -20
  267. data/tests/contractor_entity_create.rb +0 -19
  268. data/tests/contractor_entity_udpate.rb +0 -20
  269. data/tests/dump_page_model.rb +0 -74
  270. data/tests/dump_template_model.rb +0 -90
  271. data/tests/loc_structure_get.rb +0 -10
  272. data/tests/loc_structure_update.rb +0 -51
  273. data/tests/loc_structures_get.rb +0 -15
  274. data/tests/local_libs.rb +0 -20
  275. data/tests/validate_queries.rb +0 -127
@@ -1,197 +0,0 @@
1
- # EcoPortal Data Fields
2
-
3
- ---
4
-
5
- ## Overview
6
-
7
- Data fields are the form inputs within a page. Each field is an INSTANCE of a field
8
- definition from the template. Field instances have:
9
- - A MongoDB ObjectId (`id`) assigned on page creation — NOT predictable from template
10
- - The field type (`__typename` in GraphQL)
11
- - Label (the question/field name shown to users)
12
- - Value(s) — the user's response
13
- - `ref` — the ES index key (Field ID / System ID future)
14
- - `deindex: Boolean` — if true, field is excluded from Elasticsearch index
15
- - Linked field configuration (cross-reference from another register)
16
-
17
- ---
18
-
19
- ## All Field Types (`DataFieldUnion`)
20
-
21
- The GraphQL `DataFieldUnion` has 20+ concrete types:
22
-
23
- | GraphQL type | eco-helpers type | Description |
24
- |---|---|---|
25
- | `PlainText` | `:plain_text` | Single or multi-line text |
26
- | `RichText` | `:rich_text` | HTML rich text editor |
27
- | `Date` | `:date` | Date/datetime picker |
28
- | `Number` | `:number` | Numeric input |
29
- | `Gauge` | `:gauge` | Numeric with min/max (risk matrix output) |
30
- | `Select` | `:select` | Single or multi-select dropdown |
31
- | `Checklist` | `:checklist` | Checkbox list (each item: label + checked) |
32
- | `Signature` | `:signature` | Digital signature capture |
33
- | `TagField` | `:tag_field` | Location tag selector |
34
- | `People` | `:people` | Person selector (linked to PersonMember) |
35
- | `ContractorEntities` | `:contractor_entities` | Contractor selector |
36
- | `CrossReference` | `:cross_reference` | Reference to pages in another register |
37
- | `Geo` | `:geo` | Geographic coordinates + address |
38
- | `ImageGallery` | `:image_gallery` | Image upload gallery |
39
- | `File` | `:file` | File attachment |
40
- | `Mailbox` | `:mailbox` | Email inbox linked to the field |
41
- | `ActionsList` | `:actions_list` | Embedded actions list |
42
- | `Law` | `:law` | Legal obligation snippets |
43
- | `AiSummary` | `:ai_summary` | AI-generated summary field |
44
- | `EmbeddedStructure` | `:embedded_structure` | Embedded reporting structure |
45
-
46
- ---
47
-
48
- ## DataFieldInput Structure
49
-
50
- When updating or adding field values, each field uses a typed input within `DataFieldInput`:
51
-
52
- ```json
53
- {
54
- "plainText": { "id": "FIELD_ID", "value": "Job description text" },
55
- "richText": { "id": "FIELD_ID", "content": "<p>HTML content</p>" },
56
- "date": { "id": "FIELD_ID", "value": "2024-11-25T08:30:00+13:00" },
57
- "number": { "id": "FIELD_ID", "value": 42.5 },
58
- "gauge": { "id": "FIELD_ID", "value": 14.0 },
59
- "select": { "id": "FIELD_ID", "options": [{ "id": "OPT_ID", "selected": true }] },
60
- "people": { "id": "FIELD_ID", "peopleIds": ["PERSON_ID_1", "PERSON_ID_2"] },
61
- "geo": { "id": "FIELD_ID", "address": "...", "coordinates": { "lat": -36.86, "lon": 174.76 } },
62
- "checklist": { "id": "FIELD_ID", "items": [{ "id": "ITEM_ID", "checked": true }] },
63
- "locationField": { "id": "FIELD_ID", "locationIds": ["LOC_ID"] }
64
- }
65
- ```
66
-
67
- Only ONE field type key is used per `DataFieldInput` object.
68
-
69
- ---
70
-
71
- ## DataFieldOneToManyInput
72
-
73
- Used in both `createPageFromTemplate` and `updatePage`:
74
-
75
- ```json
76
- {
77
- "additions": [<DataFieldInput>, ...],
78
- "updates": [<DataFieldInput>, ...],
79
- "deletions": ["FIELD_ID_TO_DELETE", ...]
80
- }
81
- ```
82
-
83
- - **`updates`** — change values of existing fields (require their current `id`)
84
- - **`additions`** — add new field data (for fields that exist in template but have no instance data yet)
85
- - **`deletions`** — remove field instances
86
-
87
- **For `createFromTemplate`:** Use `updates` — the `buildFromTemplate` response gives
88
- instance field IDs, and `updates` sets their values on creation.
89
-
90
- ---
91
-
92
- ## Field Access in the Gem
93
-
94
- ### Getting fields from a page
95
-
96
- ```ruby
97
- page = graphql.pages.get(page_id) # fetches CommonPageUnion with $fields: true
98
-
99
- # As DataField::Collection
100
- fields = page.components # or page.field_collection
101
-
102
- # Filter by type
103
- plain_texts = fields.get_by_type(:plain_text)
104
- selects = fields.get_by_type('Select')
105
-
106
- # Find by label
107
- field = fields.get_by_name('Summary')
108
-
109
- # Access raw docs (v2 compat)
110
- fields.doc # → [raw_hash, ...]
111
- ```
112
-
113
- ### Modifying fields
114
-
115
- ```ruby
116
- # PlainText
117
- field = page.components.get_by_name('Summary')
118
- field.value = 'New summary text'
119
-
120
- # Select — by option name, value, or id
121
- field = page.components.get_by_name('Status')
122
- field.select_option('Active') # deselects all others
123
-
124
- # People
125
- field = page.components.get_by_name('Assigned To')
126
- field.people_ids = ['person_id_1', 'person_id_2']
127
-
128
- # Date (ISO8601 string)
129
- field = page.components.get_by_name('Due Date')
130
- field.value = '2025-06-30T09:00:00+12:00'
131
-
132
- # Checklist
133
- field = page.components.get_by_name('Checklist')
134
- field.check('Step A')
135
- field.check('Step B', checked: false)
136
-
137
- # Rich text (HTML)
138
- field = page.components.get_by_name('Description')
139
- field.content = '<p>Updated description</p>'
140
- ```
141
-
142
- ### Adding a new field (additions)
143
-
144
- ```ruby
145
- # When a field exists in the template but has no data in this instance:
146
- draft = graphql.pages.get_new(template_id)
147
- draft.components.add(id: 'f1_from_draft', type: 'PlainText') { |f| f.value = 'X' }
148
- ```
149
-
150
- ---
151
-
152
- ## Stage-Level Field Access
153
-
154
- For PhasedPage, fields are within stages → sections → fields:
155
-
156
- ```ruby
157
- # Get stage by name
158
- stage = page.stages['Risk Assessment']
159
-
160
- # Stage sections
161
- stage.sections.each do |section|
162
- puts "Section: #{section.heading}"
163
- section.components.each { |f| puts " Field: #{f.label}" }
164
- end
165
-
166
- # All fields in stage (flat)
167
- stage.components.get_by_name('Hazard Description').value = 'Exposure to...'
168
- ```
169
-
170
- ---
171
-
172
- ## Field Visibility: deindex and Hidden
173
-
174
- - `deindex: Boolean` — if true, field value is NOT sent to Elasticsearch. Used for
175
- sensitive data. Affects: search, filters, analytics, exports.
176
- - Fields can be hidden (template configuration). `showHiddenData: true` in queries
177
- includes hidden fields in the response.
178
-
179
- ---
180
-
181
- ## Field Reference (ref) — ES Index Key
182
-
183
- The `ref` property identifies a field in the search index (ES). It is:
184
- - Composed of: `<type_shorthand>.<hash_of_label>`
185
- - Used in: sorters (`key:` field), filters, chart configurations, linked field config
186
- - Different from the MongoDB instance `id`
187
- - Breaks if the field label changes (hash of label changes)
188
-
189
- **Formula:** `Ecoportal::API::Common::Content::StringDigest` in `ecoportal-api-v2`
190
-
191
- **Examples:**
192
- - Plain text field "Job Number": `plain_text.job_nu` (approximate hash)
193
- - Date field "Due Date": `date.due_da`
194
-
195
- **Known issue:** If a field was added via a tech script without copying from the template,
196
- its `ref` may be unique per instance rather than consistent across all instances of the
197
- same template. This causes analytics issues (missing data, duplicate columns in exports).
@@ -1,243 +0,0 @@
1
- # EcoPortal Stages and Sections
2
-
3
- ---
4
-
5
- ## Stage Lifecycle
6
-
7
- ### Stage States (`StageStateEnum`)
8
-
9
- ```
10
- pending → inprogress → complete
11
- ```
12
-
13
- - **`pending`** — Stage not yet started. No fill-in task exists yet.
14
- - **`inprogress`** — Stage is active. A fill-in task (`CompletePage`) exists and is
15
- assigned to someone (per workflow rules). The assignee can enter data.
16
- - **`complete`** — All required tasks are done. Stage is locked.
17
-
18
- **Transition triggers:**
19
- - `pending → inprogress`: Previous stage completes (its task is done/signed off)
20
- - `inprogress → complete`: Fill-in task completed, OR review task signed off (if review configured)
21
-
22
- ### Active vs Started
23
-
24
- - `active: Boolean` — Stage is currently active for input (the "current" stage)
25
- - `started: Boolean` — Stage has had at least some input (includes all past stages)
26
-
27
- For a PhasedPage: typically ONE stage is `active` at a time. Previous stages are
28
- `started` but not `active`. Future stages are neither.
29
-
30
- ### Stage-Specific Submit
31
-
32
- When submitting a stage via GraphQL, ALWAYS include `stageId`:
33
-
34
- ```graphql
35
- mutation SubmitStage($input: UpdatePageInput!) {
36
- updatePage(input: $input) {
37
- item { ...PhasedPageFields }
38
- errors { details fullMessages }
39
- }
40
- }
41
- ```
42
-
43
- ```json
44
- {
45
- "input": {
46
- "id": "PAGE_ID",
47
- "patchVer": 5,
48
- "stageId": "STAGE_ID",
49
- "submit": true,
50
- "dataFields": {
51
- "updates": [
52
- { "plainText": { "id": "FIELD_ID", "value": "Final value" } }
53
- ]
54
- }
55
- }
56
- }
57
- ```
58
-
59
- Without `stageId`, the submit may not correctly associate with the right stage and
60
- may not trigger the expected task assignments in the next stage.
61
-
62
- **Why a stage is mandatory (verified — Oscar, 2026-06-30):** stage tasks, and the
63
- permissions granted through them, are **stage-scoped**. A people field can grant the user
64
- attached to it permission to *only the stage where they were attached* — so a save / submit /
65
- sign-off MUST be executed against that specific stage as the target. The web/mobile front-ends
66
- always operate within a stage for this reason. Inferring a "current/active" stage from the
67
- page is therefore not just unreliable, it is semantically wrong: on a multi-stage page it may
68
- name a different stage than the one the work was done on. The submit must carry the stage the
69
- caller actually worked on. ⇒ **submit / sign_off without a target stage is invalid.**
70
-
71
- ### Gem enforcement & stageId resolution (compat layer)
72
-
73
- The gem treats "a submit must name its stage" as a hard invariant:
74
-
75
- - `Input::Page::Update.from_model` **raises** (`ArgumentError`) when `submit: true` or a stage
76
- `task` is requested with no `stage_id`. This replaced a silent failure: the operation hash's
77
- nil-guard used to *drop* a nil `stageId`, so a stage-less submit went out unnoticed (the
78
- act-gov TOOCS bug, 2026-06-30). A plain field update / publish needs no stage and is exempt.
79
- - `Compat::Pages#compat_stage_id` resolves the target most-explicit-first:
80
- 1. **pinned** — `submit!(stage_id:)` / `sign_off!(stage_id:)` from the script;
81
- 2. **active** — `page._compat_active_stage_id`, recorded when a `Compat::StageView` is taken
82
- over the page (`pages.get(id, stage_id:)` or eco-helpers `OozeRedirect#stage`). This is the
83
- reliable source on the OozeSamples path, where the base loop enqueues and updates the FULL
84
- page (first-wins by id — the StageView is never the queued object), so the chosen stage must
85
- live on the page itself;
86
- 3. **current** — `Phased#current_stage_id` server-state inference (`state=='inprogress'` /
87
- `active==true`). Last resort only, and guarded: if it yields nil the `from_model` guard fires.
88
-
89
- So the canonical way to submit a specific stage from a script is to work *through a stage view*
90
- (`with_stage` → `stage()` → `StageView`), which records the stage; the later `target.submit!`
91
- then resolves to it automatically.
92
-
93
- ---
94
-
95
- ## Stage Permissions
96
-
97
- Each stage can have restricted access:
98
- - **User group restrictions** — only members of specified groups can edit the stage
99
- - **People field access** — if a stage has a people field configured to "grant stage access",
100
- the people assigned to that field get editing access to that stage only
101
-
102
- **Key insight:** The configuration enum on a people field determines access scope:
103
- - `stage_only` — grant access only to the stage where the people field is embedded
104
- - `page_wide` — grant access to the entire page
105
-
106
- **GraphQL behaviour:** If a user doesn't have access to a stage, the stage is simply
107
- ABSENT from the response (`stages`, `stagesIndex`, `activeStages`). No error is thrown.
108
- This is different from APIv2 which returned explicit access errors per stage.
109
-
110
- **Scripting implication:** Scripts should check whether an expected stage is present
111
- in the response rather than catching errors. If a stage is missing, the authenticated
112
- user may not have access to it.
113
-
114
- ---
115
-
116
- ## Section Types (HasSectionsInterface)
117
-
118
- Both BasicPage (directly) and Stage (within PhasedPage) implement `HasSectionsInterface`.
119
-
120
- ### `ContentSection`
121
-
122
- Standard section — one column of fields:
123
- ```graphql
124
- fragment on ContentSection {
125
- id
126
- heading
127
- dataFields { ...dataField }
128
- }
129
- ```
130
-
131
- ### `SplitSection`
132
-
133
- Two-column section (left + right panels):
134
- ```graphql
135
- fragment on SplitSection {
136
- id
137
- heading
138
- leftDataFields { ...dataField }
139
- rightDataFields { ...dataField }
140
- }
141
- ```
142
-
143
- When accessing a split section's fields, always merge left + right:
144
- ```ruby
145
- section.components # → DataField::Collection merging left + right
146
- ```
147
-
148
- ---
149
-
150
- ## Stage-Level Field Access (PhasedPage)
151
-
152
- In GraphQL, all stage data is returned in one request via `CommonPageUnion`.
153
- The response has stages → sections → fields.
154
-
155
- ```ruby
156
- # Gem access pattern
157
- page = graphql.pages.get(page_id)
158
-
159
- # All stages
160
- page.stages # → StageCollection
161
- page.stages['Risk Assessment'] # → Stage (by name or ID)
162
- page.stages.ordered # → [Stage, ...] sorted by ordering
163
-
164
- # Stage contents
165
- stage = page.stages['Risk Assessment']
166
- stage.sections # → SectionCollection
167
- stage.components # → DataField::Collection (all fields in stage)
168
- stage.sections.get_by_type(:content_section)
169
- stage.sections.get_by_heading('Details')
170
-
171
- # Field access in a stage
172
- stage.components.get_by_name('Hazard Description').value = 'Exposure to...'
173
- stage.components.get_by_type(:select).find { |f| f.label == 'Risk Level' }.select_option('High')
174
- ```
175
-
176
- ---
177
-
178
- ## APIv2 vs GraphQL Stage Rendering Comparison
179
-
180
- | Concern | APIv2 | GraphQL |
181
- |---------|-------|---------|
182
- | Request count for PhasedPage | One per stage | One for entire page |
183
- | Stage data isolation | Strict — each request scoped to one stage | Full page returned, client filters |
184
- | Stage-specific mutations | Via stage_id on URL | Via `stageId:` in `UpdatePageInput` |
185
- | Permission handling | Access error per forbidden stage | Forbidden stages absent from response |
186
- | Template stages | Full render | Full render |
187
-
188
- **Compat layer approach:**
189
- `graphql.pages.get(page_id, stage_id: 's1')` returns a `StageView` — a client-side
190
- wrapper that shows only the specified stage's sections and components, while delegating
191
- all other page accessors (id, name, patchVer, as_update, etc.) to the full page.
192
- This matches what v2 scripts expect from a per-stage fetch.
193
-
194
- ---
195
-
196
- ## StageIndex (Lightweight Stage Summary)
197
-
198
- For displaying workflow progress without loading full stage content:
199
-
200
- ```graphql
201
- fragment PhasedPageFields on PhasedPage {
202
- stagesIndex {
203
- id
204
- name
205
- ordering
206
- state
207
- started
208
- hasCompleteTask
209
- hasReviewTask
210
- }
211
- }
212
- ```
213
-
214
- `hasCompleteTask` — whether a fill-in task is configured for this stage
215
- `hasReviewTask` — whether a review/sign-off task is configured for this stage
216
-
217
- ---
218
-
219
- ## Task Types on Stages
220
-
221
- | Task type | GraphQL type | Trigger | When complete |
222
- |-----------|-------------|---------|---------------|
223
- | Fill-in | `CompletePage` | Stage becomes active | Moves to review (if configured) or completes stage |
224
- | Review/sign-off | `ReviewPage` | Fill-in task completed | Completes stage → activates next stage |
225
-
226
- Task fields: `id`, `complete`, `completedAt`, `completedBy`, `due`, `overdue`, `users`
227
-
228
- **Stage close-out sequence:**
229
- 1. Fill-in task assigned to user(s) (per workflow rules)
230
- 2. User submits: `updatePage(stageId:, submit: true)` triggers `CompletePage` completion
231
- 3. If review configured: `ReviewPage` task created and assigned
232
- 4. Review approved: stage state → `complete`, next stage activated
233
- 5. Next fill-in task created per workflow rules
234
-
235
- **Scripting a stage submit:**
236
- ```ruby
237
- input = Input::Page::Update.from_model(page,
238
- stage_id: 'STAGE_ID',
239
- submit: true
240
- )
241
- # Input will include stageId + submit even if no page/field changes
242
- Builder::Page.new(client).update(input: input)
243
- ```
@@ -1,196 +0,0 @@
1
- # EcoPortal People, Contractors, and Locations
2
-
3
- ---
4
-
5
- ## People (PersonMember)
6
-
7
- ### GraphQL Type
8
-
9
- `PersonMember` — represents a user in the ecoPortal platform.
10
-
11
- Key fields: `id`, `name`, `email`, `externalId`, `archived`, `publicFields`, `privateFields`,
12
- `supervisor_id`, `organizationHierarchy`
13
-
14
- ### Query
15
-
16
- ```graphql
17
- query GetPeople($filters: [SearchFilter!], $includeArchived: Boolean) {
18
- currentOrganization {
19
- personMembers(
20
- first: 20,
21
- includeArchived: $includeArchived,
22
- searchConf: {
23
- filters: $filters,
24
- sorters: [{ key: "created_at", direction: "desc" }]
25
- }
26
- ) {
27
- totalCount
28
- pageInfo { hasNextPage endCursor }
29
- nodes { id name email externalId publicFields privateFields }
30
- }
31
- }
32
- }
33
- ```
34
-
35
- ### Finding a Person by externalId
36
-
37
- ```json
38
- {
39
- "filters": [
40
- { "operation": "exact_filter", "params": { "key": "external_id", "value": "FRODO" } }
41
- ]
42
- }
43
- ```
44
-
45
- Note: The `searchConf` for `personMembers` uses the ARRAY format for `filters` and
46
- sorters use `key:` not `fieldName:`.
47
-
48
- ---
49
-
50
- ## Contractor Entities
51
-
52
- ### GraphQL Type
53
-
54
- `ContractorEntity` — represents an external contractor/supplier in the system.
55
-
56
- Key fields: `id`, `externalId`, `name`, `schemaId`, `active`, `approved`,
57
- `associatedPeopleIds`, `leadContractorIds`
58
-
59
- ### Query
60
-
61
- ```graphql
62
- query Contractors($searchConf: Search) {
63
- currentOrganization {
64
- contractorEntities(searchConf: $searchConf) {
65
- totalCount
66
- nodes { id externalId name active approved associatedPeopleIds leadContractorIds }
67
- pageInfo { endCursor hasNextPage }
68
- }
69
- }
70
- }
71
- ```
72
-
73
- ### Find by externalId
74
-
75
- ```json
76
- { "searchConf": { "filters": [{ "operation": "exact_filter", "params": { "key": "external_id", "value": "9429041280072" } }] } }
77
- ```
78
-
79
- ### Update: IdDiffInput Pattern
80
-
81
- Contractor's `associatedPeopleIds` and `leadContractorIds` use the **IdDiff pattern**
82
- for updates — instead of sending the full array, send additions and removals:
83
-
84
- ```json
85
- {
86
- "associatedPeopleIds": { "additions": ["new_person_id"], "removals": ["old_person_id"] }
87
- }
88
- ```
89
-
90
- **Gem class:** `Input::ContractorEntity::Update` handles this via `from_model`
91
- which reads `doc` vs `original_doc` to compute the diff.
92
-
93
- ### Mutations
94
-
95
- ```graphql
96
- mutation CreateContractor($input: CreateContractorEntityInput!) {
97
- createContractorEntity(input: $input) {
98
- item { id externalId name schemaId active approved associatedPeopleIds leadContractorIds }
99
- errors { details fullMessages }
100
- }
101
- }
102
-
103
- mutation UpdateContractor($input: UpdateContractorEntityInput!) {
104
- updateContractorEntity(input: $input) {
105
- item { ... }
106
- errors { ... }
107
- }
108
- }
109
- ```
110
-
111
- ---
112
-
113
- ## Location Structure (Reporting Structure / RS)
114
-
115
- ### Concept
116
-
117
- The location structure is a hierarchical tree used to tag pages and scope analytics.
118
- Also called "Reporting Structure" (RS) internally.
119
-
120
- Used for:
121
- - Assigning pages to locations/departments (`locations: [LocationNodeInterface]`)
122
- - Filtering searches by location (`location_ids` filter)
123
- - Analytics and dashboard scoping
124
- - Permission scoping (some permissions are location-based)
125
-
126
- ### GraphQL Access
127
-
128
- ```graphql
129
- query GetLocationStructure($id: ID!) {
130
- currentOrganization {
131
- locationStructure(id: $id) {
132
- id name
133
- nodes {
134
- id name weight archived
135
- classifications { name }
136
- ancestors { id name }
137
- }
138
- }
139
- }
140
- }
141
- ```
142
-
143
- ### Draft-Based Edits
144
-
145
- Location structure changes go through a **draft** system:
146
- 1. `createLocationDraft(...)` — create an edit draft
147
- 2. Apply commands: `insertNode`, `moveNode`, `archiveNode`, `renameNode`, etc.
148
- 3. `publishDraft(...)` — apply to production
149
-
150
- This is the most complex part of the GraphQL API. The gem has full coverage in
151
- `Mutation::LocationStructure::Draft::*` and `Input::LocationStructure::Draft::*`.
152
-
153
- ### LocationNode Type
154
-
155
- ```graphql
156
- type LocationNodeInterface {
157
- id: ID!
158
- name: String!
159
- weight: Int!
160
- archived: Boolean!
161
- archivedToken: ID
162
- parent: LocationNodeInterface
163
- children: [LocationNodeInterface!]!
164
- classifications: [LocationClassificationType!]!
165
- ancestors: [LocationNodeInterface!]!
166
- }
167
- ```
168
-
169
- ### TagField (Location Picker Data Field)
170
-
171
- `TagField` on a page is the location tag input. It stores `locations: [LocationNodeInterface]`
172
- — the selected location nodes for that field. Multiple pages can have different location
173
- tags from the same RS tree.
174
-
175
- ---
176
-
177
- ## IdDiff Pattern
178
-
179
- Used whenever an array relationship is updated. Instead of sending the full new array,
180
- send only what changed:
181
-
182
- ```graphql
183
- input IdDiffInput {
184
- additions: [ID!]!
185
- removals: [ID!]!
186
- }
187
- ```
188
-
189
- Used in:
190
- - `ContractorEntity` `associatedPeopleIds` and `leadContractorIds` updates
191
- - Potentially other relationship updates
192
-
193
- **Gem support:**
194
- - `Input::IdDiff` — the input class
195
- - `Input::ContractorEntity::Update.from_model` — auto-computes additions/removals
196
- from `doc` vs `original_doc` comparison