ember-tribe 2.6.7 → 2.6.9

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 CHANGED
@@ -4,6 +4,46 @@ An addon that connects EmberJS to Tribe API, bridging the gap between backend da
4
4
 
5
5
  Tribe is a project management framework built for ease of collaboration - https://github.com/tribe-framework/tribe
6
6
 
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ - [Installation and Setup](#installation-and-setup)
12
+ - [Folder Structure](#folder-structure)
13
+ - [Best Practices for AI Generated Code](#best-practices-for-ai-generated-code)
14
+ - [Code Rules](#code-rules)
15
+ - [Storylang Rules](#storylang-rules)
16
+ - [Types](#types)
17
+ - [Routes](#routes)
18
+ - [Controllers](#controllers)
19
+ - [Helpers](#helpers)
20
+ - [Modifiers](#modifiers)
21
+ - [Services](#services)
22
+ - [Components](#components)
23
+ - [Storylang](#storylang)
24
+ - [Storylang CLI](#storylang-cli)
25
+ - [Storylang.json Documentation](#storylangjson-documentation)
26
+ - [Types](#1-types)
27
+ - [Routes](#2-routes)
28
+ - [Helpers](#3-helpers)
29
+ - [Modifiers](#4-modifiers)
30
+ - [Services](#5-services)
31
+ - [Components](#6-components)
32
+ - [Data Types Reference](#data-types-reference)
33
+ - [Integration with Other Files](#integration-with-other-files)
34
+ - [EmberJS](#emberjs)
35
+ - [Ember-Tribe Development Guide](#ember-tribe-development-guide)
36
+ - [EmberData Integration](#emberdata-integration)
37
+ - [Route Generation](#route-generation)
38
+ - [Helper System](#helper-system)
39
+ - [Modifier System](#modifier-system)
40
+ - [Services Integration](#services-integration)
41
+ - [Component Architecture](#component-architecture)
42
+ - [Forms and Input Fields](#forms-and-input-fields)
43
+ - [Deploying to Junction (Self-Hosted)](#deploying-to-junction-self-hosted)
44
+
45
+ ---
46
+
7
47
  ## Installation and Setup
8
48
 
9
49
  ### Prerequisites
@@ -26,11 +66,22 @@ The addon automatically configures following essential packages:
26
66
 
27
67
  **Ember Addons:** `ember-cli-dotenv`, `ember-cli-sass`, `ember-modifier`, `ember-composable-helpers`, `ember-truth-helpers`, `ember-file-upload` , `ember-power-select`
28
68
 
29
- **NPM Packages:** `bootstrap`, `@popperjs/core`, `animate.css`, `video.js`, `swiper`, `howler`
69
+ **NPM Packages:** `bootstrap`, `@popperjs/core`, `animate.css`, `video.js`, `swiper`, `howler`, `sortablejs`, `papaparse`
70
+
71
+ **Built-in features that can be used in routes and components**:
72
+
73
+ - **Layout**: `table`, `figure`, `accordion`, `card`, `list-group`, `navbar`, `nav`, `tab`, `breadcrumb`
74
+ - **Interactive**: `button`, `button-group`, `dropdown`, `modal`, `collapse`, `offcanvas`, `pagination`, `popover`, `tooltip`, `swiper-carousel`, `videojs-player`, `howlerjs-player`, `input-field`, `input-group`, `textarea`, `checkbox`, `radio`, `range`, `select`, `multi-select`, `date`, `file-uploader`, `alert`, `badge`, `toast`, `placeholder`, `progress`, `spinner`, `scrollspy`
75
+
76
+ **Preinstalled services in ember-tribe**:
77
+
78
+ - `store`: Ember Data store for CRUD operations
79
+ - `router`: Ember router service for navigation
80
+ - `types`: Automatic model generation from backend tracks
30
81
 
31
82
  ---
32
83
 
33
- ## Core File Structure
84
+ ## Folder Structure
34
85
 
35
86
  ```
36
87
  app/
@@ -50,11 +101,125 @@ installer.sh
50
101
 
51
102
  ---
52
103
 
53
- ## Storylang CLI
104
+ ## Best Practices for AI generated code
105
+
106
+ These rules are **mandatory** for all Tribe-compatible code. Follow them strictly and do not deviate unless explicitly instructed.
107
+
108
+ ### Code Rules
109
+
110
+ 1. **EmberJS 6.x Compatibility — Strictly Required**
111
+ All generated code must be strictly compatible with EmberJS 6.x.
112
+
113
+ 2. **Bootstrap 5.x — Required Foundation**
114
+ Use Bootstrap 5.x as the sole design system for all layout, spacing, and responsive behaviour. Do not introduce custom CSS frameworks or utility libraries that conflict with Bootstrap. Follow Bootstrap conventions strictly.
115
+
116
+ 3. **Backend Field Access**
117
+ Always access backend fields through the `modules` object — e.g. `object.modules.field_name`. Never access backend fields directly.
118
+
119
+ 4. **npm Packages over Ember Addons**
120
+ When an npm package and an Ember addon offer equivalent functionality, always prefer the npm package for better long-term compatibility.
121
+
122
+ 5. **Icons — FontAwesome 6.x Only**
123
+ Use FontAwesome 6.x for all icons. Do not use any other icon library unless the project description explicitly specifies one.
124
+
125
+ 6. **Animations — Subtle and Purposeful**
126
+ If animations are needed, use `animate.css`. Keep animations subtle — prefer fades and minimal slides. Avoid anything that feels flashy or distracting.
127
+
128
+ 7. **EmberData Caching**
129
+ When data has already been loaded into the store, retrieve it with `peekRecord` instead of making a new network request.
130
+
131
+ 8. **Backend Filtering over Frontend Filtering**
132
+ For sorting and filtering data, always use `this.store.query` with backend query parameters. Do not filter or sort arrays on the frontend when the backend can do it.
133
+
134
+ ---
135
+
136
+ ### Storylang Rules
137
+
138
+ Follow this strict order of thinking when designing any feature:
139
+
140
+ > **Understand Types → Routes → Controllers → Helpers → Modifiers → Services → Components**
141
+
142
+ Always begin by understanding your data types, then define the routes that load that data, then wire up controllers to handle user actions, then extract reusable template logic into helpers, then isolate DOM behaviour into modifiers, then move app-wide logic into services, and finally — only when the project's scale warrants it — extract repeatable UI into components.
143
+
144
+ ---
145
+
146
+ **Types**
147
+
148
+ 8. **Start by Understanding Your Data**
149
+ Before writing any code, read the project description and `types.json` to understand the data model. Every architectural decision that follows — which routes to create, which services to build, whether components are even needed — depends on a clear understanding of the underlying types.
150
+
151
+ ---
152
+
153
+ **Routes**
154
+
155
+ 9. **Route Naming**
156
+ Match route names to user mental models. Use consistent, predictable naming conventions so that routes are self-documenting.
157
+
158
+ 10. **Routes Are for Fetching, Not Logic**
159
+ Routes should primarily perform read/fetch operations and pass data down to components or services. Keep JavaScript in routes to a minimum — business logic belongs in components and services, not routes.
160
+
161
+ 11. **Route Parameters**
162
+ Keep `get_vars` minimal and meaningful. Load only the data types that each specific route actually needs — avoid over-fetching.
163
+
164
+ ---
165
+
166
+ **Controllers**
167
+
168
+ 12. **Controllers Bridge Routes and Templates**
169
+ Controllers sit between routes and templates, handling query parameters, user actions, and transient UI state that belongs to a specific route. Keep controllers focused — they are not a place for business logic or data fetching.
170
+
171
+ 13. **Keep Controllers Thin**
172
+ Delegate complex logic to services. A controller should primarily expose tracked properties and actions that the corresponding template needs directly.
173
+
174
+ ---
175
+
176
+ **Helpers**
177
+
178
+ 14. **Helpers Must Be Pure and Stateless**
179
+ A helper receives input and returns output — nothing else. Helpers must have no side effects and must not interact with the store, services, or DOM.
180
+
181
+ ---
182
+
183
+ **Modifiers**
184
+
185
+ 15. **Modifiers Own All DOM Interaction**
186
+ Any direct DOM manipulation or third-party library initialisation must live in a modifier.
187
+
188
+ ---
189
+
190
+ **Services**
191
+
192
+ 16. **Services Are the Core Logic Layer**
193
+ Services hold the primary business logic of the application. They interact with both routes and components and are the single source of truth for app-wide behaviour.
194
+
195
+ 17. **Keep Services Stateless When Possible**
196
+ Avoid storing transient state in services. Where services must depend on one another, use dependency injection.
197
+
198
+ 18. **All Backend Calls Must Live Inside `@action` Functions**
199
+ Any call to a `/custom/*.php` script must be made from within an `@action` function. This rule makes backend interactions explicit, traceable, and user-initiated by design.
200
+
201
+ 19. **Plain Methods Belong in `functions`, Not `actions`**
202
+ Internal methods, which carry no special Ember semantics, should be traced automatically under the `functions` key in `storylang.json`. Do not decorate them with `@action`. Keeping `actions` reserved for user-facing interactions makes the intent of each method immediately clear to anyone reading the spec.
203
+
204
+ 20. **Getters Are Derived Properties — Not Actions or Functions**
205
+ Native JavaScript `get` accessors (e.g. `get trustScoreColor()`) derive a display value, CSS class, or conditional flag from `this.args` or other state. They carry no side effects and require no `@action` decorator. Storylang captures them under the `getters` key, separate from `actions` and `functions`, so the distinction between user-facing interactions, internal logic, and pure derived state is always explicit in the spec.
206
+
207
+ ---
208
+
209
+ **Components**
210
+
211
+ 21. **Components are not always required**
212
+ Before creating components, assess the scale of the project from its description. On small projects, fewer files means higher code readability — collapsing template logic directly into route templates is often the right call. On larger projects, the opposite is true: extracting repeatable UI into named components improves clarity, maintainability, and testability. Make this decision deliberately at the start, not as an afterthought.
213
+
214
+ ---
215
+
216
+ ## Storylang
217
+
218
+ ### Storylang CLI
54
219
 
55
220
  ember-tribe ships with a command-line tool called `storylang` that synchronises the `config/storylang.json` specification with the actual Ember project files.
56
221
 
57
- ### Usage
222
+ #### Usage
58
223
 
59
224
  ```bash
60
225
  node storylang
@@ -71,58 +236,44 @@ node storylang
71
236
  # => config/storylang.json updated from project files
72
237
  ```
73
238
 
74
- ### Typical Workflow
239
+ #### Typical Workflow
75
240
 
76
241
  Run `node storylang` periodically to keep `config/storylang.json` in sync as the project evolves.
77
242
 
78
243
  ---
79
244
 
80
- ## Storylang.json Documentation
245
+ ### Storylang.json Documentation
81
246
 
82
- ### Overview
247
+ #### Overview
83
248
 
84
249
  Storylang.json is a structured configuration file used in the ember-tribe ecosystem to define the frontend implementation of your application. It is found at `config/storylang.json`. It works in conjunction with your `types.json` (which defines your data types) to create a complete frontend specification.
85
250
 
86
- ### Purpose
251
+ #### Purpose
87
252
 
88
253
  The storylang.json file serves as a blueprint for frontend developers to understand:
89
254
 
90
255
  - What routes, components, services, helpers, modifiers and types are required
91
256
  - How data flows through the application
92
257
 
93
- ### File Structure
258
+ #### File Structure
94
259
 
95
260
  The storylang.json file contains seven main sections:
96
261
 
97
262
  ```json
98
263
  {
99
- "implementation_approach": "...",
100
264
  "types": [...],
101
- "components": [...],
102
265
  "routes": [...],
103
- "services": [...],
104
266
  "helpers": [...],
105
- "modifiers": [...]
267
+ "modifiers": [...],
268
+ "services": [...],
269
+ "components": [...]
106
270
  }
107
271
  ```
108
272
 
109
- ### Section Definitions
110
273
 
111
- ### 1. Implementation Approach
274
+ #### Section Definitions
112
275
 
113
- **Purpose**: Provides a high-level technical overview of how the frontend interface would work.
114
-
115
- **Format**:
116
-
117
- ```json
118
- {
119
- "implementation_approach": "Two-paragraph description explaining technical approach and key functionality."
120
- }
121
- ```
122
-
123
- ---
124
-
125
- ### 2. Types
276
+ #### 1. Types
126
277
 
127
278
  **Purpose**: Declares which data types from `types.json` and maps them to the components, routes, services, helpers and modifiers that consume them. This creates a traceable link between your data layer and your UI implementation.
128
279
 
@@ -147,77 +298,72 @@ The storylang.json file contains seven main sections:
147
298
 
148
299
  ---
149
300
 
150
- ### 3. Components
301
+ #### 2. Routes
151
302
 
152
- **Purpose**: Defines reusable UI components that will be built for the application.
303
+ **Purpose**: Defines the application's routes and their requirements.
304
+
305
+ > **Note on controllers**: In `storylang.json`, controllers are not listed as a separate top-level section. Instead, each controller is considered part of its corresponding route — just as a component's backing JavaScript class is part of its component entry. Controller actions, tracked variables, and query parameters should be specified within the route definition they belong to.
153
306
 
154
307
  **Format**:
155
308
 
156
309
  ```json
157
310
  {
158
- "components": [
311
+ "routes": [
159
312
  {
160
- "name": "component-name",
161
- "type": "component-type",
313
+ "name": "route-name", //should match Ember router.js
162
314
  "tracked_vars": [{ "<variableName>": "<dataType>" }],
163
- "inherited_args": [{ "<argumentName>": "<argType>" }],
164
- "actions": ["action1", "action2"],
165
- "helpers": ["helper1", "helper2"],
166
- "modifiers": ["modifier1"],
167
- "services": ["service1", "service2"]
315
+ "get_vars": [{ "<paramName>": "<dataType>" }],
316
+ "getters": ["derived-property-name"],
317
+ "actions": ["frontend-action", { "backend-action": ["custom/path/file.php"] }],
318
+ "functions": ["helper-method", "compute-something"],
319
+ "helpers": ["helper1"],
320
+ "services": ["service1"],
321
+ "components": ["component1", "component2"],
322
+ "types": ["type1", "type2"]
168
323
  }
169
324
  ]
170
325
  }
171
326
  ```
172
327
 
173
- **Built-in Component Types in ember-tribe**:
328
+ > **Actions format**: `actions` is a mixed array. Pure frontend actions are plain strings. Actions that call one or more `/custom/*.php` scripts are objects whose key is the action name and whose value is the list of PHP paths called — e.g. `{ "save-record": ["custom/records/save.php"] }`.
174
329
 
175
- - **Layout**: `table`, `figure`, `accordion`, `card`, `list-group`, `navbar`, `nav`, `tab`, `breadcrumb`
176
- - **Interactive**: `button`, `button-group`, `dropdown`, `modal`, `collapse`, `offcanvas`, `pagination`, `popover`, `tooltip`, `swiper-carousel`, `videojs-player`, `howlerjs-player`, `input-field`, `input-group`, `textarea`, `checkbox`, `radio`, `range`, `select`, `multi-select`, `date`, `file-uploader`, `alert`, `badge`, `toast`, `placeholder`, `progress`, `spinner`, `scrollspy`
330
+ ---
177
331
 
178
- **Example**:
332
+ #### 3. Helpers
333
+
334
+ **Purpose**: Defines custom template helpers — pure functions used in templates to format, compute or transform data for display.
335
+
336
+ **Format**:
179
337
 
180
338
  ```json
181
339
  {
182
- "components": [
340
+ "helpers": [
183
341
  {
184
- "name": "file-summary-card",
185
- "type": "card",
186
- "tracked_vars": [{ "isSelected": "bool" }, { "isExpanded": "bool" }],
187
- "inherited_args": [
188
- { "file": "var" },
189
- { "onEdit": "action" },
190
- { "onDelete": "action" }
191
- ],
192
- "actions": ["toggleSelection", "expandDetails", "editFile", "deleteFile"],
193
- "helpers": ["formatDate", "truncateText"],
194
- "modifiers": ["tooltip"],
195
- "services": ["store", "router"]
342
+ "name": "helper-name",
343
+ "description": "What this helper does",
344
+ "input_args": [{ "<argumentName>": "<dataType>" }],
345
+ "return": "<dataType>"
196
346
  }
197
347
  ]
198
348
  }
199
349
  ```
200
350
 
201
- ---
202
-
203
- ### 4. Routes
204
-
205
- **Purpose**: Defines the application's routes and their requirements.
206
-
207
- **Format**:
351
+ **Example**:
208
352
 
209
353
  ```json
210
354
  {
211
- "routes": [
355
+ "helpers": [
212
356
  {
213
- "name": "route-name", //should match Ember router.js
214
- "tracked_vars": [{ "<variableName>": "<dataType>" }],
215
- "get_vars": [{ "<paramName>": "<dataType>" }],
216
- "actions": ["action1", "action2"],
217
- "helpers": ["helper1"],
218
- "services": ["service1"],
219
- "components": ["component1", "component2"],
220
- "types": ["type1", "type2"]
357
+ "name": "format-date",
358
+ "description": "Formats a raw ISO date string into a human-readable date",
359
+ "input_args": [{ "isoString": "string" }, { "format": "string" }],
360
+ "return": "string"
361
+ },
362
+ {
363
+ "name": "truncate-text",
364
+ "description": "Truncates a string to a given character limit and appends an ellipsis",
365
+ "input_args": [{ "text": "string" }, { "limit": "int" }],
366
+ "return": "string"
221
367
  }
222
368
  ]
223
369
  }
@@ -225,56 +371,41 @@ The storylang.json file contains seven main sections:
225
371
 
226
372
  ---
227
373
 
228
- ### 5. Services
374
+ #### 4. Modifiers
229
375
 
230
- **Purpose**: Defines custom Ember services needed by the application.
376
+ **Purpose**: Defines custom Ember modifiers functions that directly interact with DOM elements to attach behaviour, third-party libraries or event listeners.
231
377
 
232
378
  **Format**:
233
379
 
234
380
  ```json
235
381
  {
236
- "services": [
382
+ "modifiers": [
237
383
  {
238
- "name": "service-name",
239
- "tracked_vars": [{ "<variableName>": "<dataType>" }],
240
- "actions": ["action1", "action2"],
241
- "helpers": ["helper1"],
242
- "services": ["dependency1", "dependency2"]
384
+ "name": "modifier-name",
385
+ "description": "What DOM behaviour this modifier applies",
386
+ "input_args": [{ "<argumentName>": "<dataType>" }],
387
+ "services": ["service1"]
243
388
  }
244
389
  ]
245
390
  }
246
391
  ```
247
392
 
248
- **Built-in Services in ember-tribe**:
249
-
250
- - `store`: Ember Data store for CRUD operations
251
- - `router`: Ember router service for navigation
252
- - `types`: Automatic model generation from backend tracks
253
- - `bootstrap`: Bootstrap CSS framework with Popper.js
254
- - `papaparse`: CSV parsing library
255
- - `sortablejs`: Drag-and-drop sorting
256
- - `swiperjs`: Touch slider/carousel
257
- - `videojs`: Video player
258
- - `howlerjs`: Audio player
259
-
260
393
  **Example**:
261
394
 
262
395
  ```json
263
396
  {
264
- "services": [
397
+ "modifiers": [
265
398
  {
266
- "name": "visualization-builder",
267
- "tracked_vars": [
268
- { "currentVisualization": "object" },
269
- { "availableTypes": "array" }
270
- ],
271
- "actions": [
272
- "createVisualization",
273
- "updateVisualization",
274
- "deleteVisualization"
275
- ],
276
- "helpers": ["validateConfig", "generatePreview"],
277
- "services": ["store", "router"]
399
+ "name": "tooltip",
400
+ "description": "Initialises a Bootstrap tooltip on the target element using the provided label",
401
+ "input_args": [{ "label": "string" }, { "placement": "string" }],
402
+ "services": []
403
+ },
404
+ {
405
+ "name": "autofocus",
406
+ "description": "Sets focus on the target element when it is inserted into the DOM",
407
+ "input_args": [],
408
+ "services": []
278
409
  }
279
410
  ]
280
411
  }
@@ -282,48 +413,47 @@ The storylang.json file contains seven main sections:
282
413
 
283
414
  ---
284
415
 
285
- ### 6. Helpers
416
+ #### 5. Services
286
417
 
287
- **Purpose**: Defines custom template helpers pure functions used in templates to format, compute or transform data for display.
418
+ **Purpose**: Defines custom Ember services needed by the application.
288
419
 
289
420
  **Format**:
290
421
 
291
422
  ```json
292
423
  {
293
- "helpers": [
424
+ "services": [
294
425
  {
295
- "name": "helper-name",
296
- "description": "What this helper does",
297
- "args": [{ "<argumentName>": "<dataType>" }],
298
- "return": "<dataType>"
426
+ "name": "service-name",
427
+ "tracked_vars": [{ "<variableName>": "<dataType>" }],
428
+ "getters": ["derived-property-name"],
429
+ "actions": ["frontend-action", { "backend-action": ["custom/path/file.php"] }],
430
+ "functions": ["internal-method"],
431
+ "helpers": ["helper1"],
432
+ "services": ["dependency1", "dependency2"]
299
433
  }
300
434
  ]
301
435
  }
302
436
  ```
303
437
 
304
- **Helper Properties**:
305
-
306
- - `name`: Kebab-case name of the helper
307
- - `description`: Brief description of what the helper computes or transforms
308
- - `args`: Input arguments the helper accepts
309
- - `return`: The data type the helper outputs
310
-
311
438
  **Example**:
312
439
 
313
440
  ```json
314
441
  {
315
- "helpers": [
316
- {
317
- "name": "format-date",
318
- "description": "Formats a raw ISO date string into a human-readable date",
319
- "args": [{ "isoString": "string" }, { "format": "string" }],
320
- "return": "string"
321
- },
442
+ "services": [
322
443
  {
323
- "name": "truncate-text",
324
- "description": "Truncates a string to a given character limit and appends an ellipsis",
325
- "args": [{ "text": "string" }, { "limit": "int" }],
326
- "return": "string"
444
+ "name": "visualization-builder",
445
+ "tracked_vars": [
446
+ { "dataMatrix": "object" },
447
+ { "availableTypes": "array" }
448
+ ],
449
+ "functions": ["build-visualization"],
450
+ "actions": [
451
+ "reset-state",
452
+ { "assemble-data-for-visualization": ["custom/visualizations/assemble-data.php"] },
453
+ "updateVisualization"
454
+ ],
455
+ "helpers": ["validateConfig", "generatePreview"],
456
+ "services": ["store", "router"]
327
457
  }
328
458
  ]
329
459
  }
@@ -331,48 +461,51 @@ The storylang.json file contains seven main sections:
331
461
 
332
462
  ---
333
463
 
334
- ### 7. Modifiers
464
+ #### 6. Components
335
465
 
336
- **Purpose**: Defines custom Ember modifiers — functions that directly interact with DOM elements to attach behaviour, third-party libraries or event listeners.
466
+ **Purpose**: Defines reusable UI components that will be built for the application.
337
467
 
338
468
  **Format**:
339
469
 
340
470
  ```json
341
471
  {
342
- "modifiers": [
472
+ "components": [
343
473
  {
344
- "name": "modifier-name",
345
- "description": "What DOM behaviour this modifier applies",
346
- "args": [{ "<argumentName>": "<dataType>" }],
347
- "services": ["service1"]
474
+ "name": "component-name",
475
+ "type": "component-type",
476
+ "tracked_vars": [{ "<variableName>": "<dataType>" }],
477
+ "inherited_args": [{ "<argumentName>": "<argType>" }],
478
+ "getters": ["derived-property-name"],
479
+ "actions": ["frontend-action", { "backend-action": ["custom/path/file.php"] }],
480
+ "functions": ["internal-method"],
481
+ "helpers": ["helper1", "helper2"],
482
+ "modifiers": ["modifier1"],
483
+ "services": ["service1", "service2"]
348
484
  }
349
485
  ]
350
486
  }
351
487
  ```
352
488
 
353
- **Modifier Properties**:
354
-
355
- - `name`: Kebab-case name of the modifier
356
- - `description`: Brief description of the DOM behaviour it attaches
357
- - `args`: Arguments passed into the modifier from the template
358
- - `services`: Ember services injected if needed
359
489
 
360
490
  **Example**:
361
491
 
362
492
  ```json
363
493
  {
364
- "modifiers": [
365
- {
366
- "name": "tooltip",
367
- "description": "Initialises a Bootstrap tooltip on the target element using the provided label",
368
- "args": [{ "label": "string" }, { "placement": "string" }],
369
- "services": []
370
- },
494
+ "components": [
371
495
  {
372
- "name": "autofocus",
373
- "description": "Sets focus on the target element when it is inserted into the DOM",
374
- "args": [],
375
- "services": []
496
+ "name": "file-summary-card",
497
+ "type": "card",
498
+ "tracked_vars": [{ "isSelected": "bool" }, { "isExpanded": "bool" }],
499
+ "inherited_args": [
500
+ { "file": "var" },
501
+ { "onEdit": "action" },
502
+ { "onDelete": "action" }
503
+ ],
504
+ "getters": ["trust-score-color", "display-label"],
505
+ "actions": ["toggle-selection", "expand-details", { "save-file": ["custom/files/save.php"] }, { "delete-file": ["custom/files/delete.php"] }],
506
+ "helpers": ["formatDate", "truncateText"],
507
+ "modifiers": ["tooltip"],
508
+ "services": ["store", "router"]
376
509
  }
377
510
  ]
378
511
  }
@@ -380,9 +513,9 @@ The storylang.json file contains seven main sections:
380
513
 
381
514
  ---
382
515
 
383
- ### Data Types Reference
516
+ #### Data Types Reference
384
517
 
385
- ### Data Types (dataType)
518
+ #### Data Types (dataType)
386
519
 
387
520
  - `string`: Text values
388
521
  - `int`: Integer numbers
@@ -390,51 +523,35 @@ The storylang.json file contains seven main sections:
390
523
  - `array`: List of items
391
524
  - `object`: Complex data structure
392
525
 
393
- ### Argument Types (argType)
526
+ #### Argument Types (argType)
394
527
 
395
528
  - `var`: Passed data/state
396
- - `fn`: Callback function
529
+ - `function`: Callback function
397
530
  - `get`: Get function
398
531
  - `action`: User interaction handler
399
532
 
400
533
  ---
401
534
 
402
- ### Integration with Other Files
535
+ #### Integration with Other Files
403
536
 
404
- ### With types.json
537
+ #### With types.json
405
538
 
406
539
  - Type names used in routes should match type names from `types.json`
407
540
  - The `types` section in storylang.json is the explicit bridge between your data types and your UI — always keep it in sync with `types.json`
408
541
  - Types are the gateway to persistent storage on the backend
542
+ - For a full reference on the `types.json` format and its field definitions, see the official documentation at [https://github.com/tribe-framework/types.json](https://github.com/tribe-framework/types.json)
409
543
 
410
544
  ---
411
545
 
412
- ### Storylang Best Practices
413
-
414
- **Always begin your thought process with routes → then move repeatable template logic into components → then move repeatable app-wide logic from components and routes to services → then extract reusable template and component functions into helpers → then extract template and component DOM behaviour into modifiers**
415
-
416
- 1. **Start with Routes**: Match route names to user mental models and use consistent naming conventions
417
- 2. **Minimize Route Logic**: Preferably fetch (read) type data in routes and then pass that data to components or services. Other than fetching type data, minimize the use of javascript in routes — Javascript is meant to be in components and services more than in routes
418
- 3. **Route Parameters**: Keep get_vars minimal and meaningful, and load only necessary types for each route
419
- 4. **Component Focus**: Keep components focused on single responsibilities and use descriptive, kebab-case names. Names should indicate which built-in ember-tribe component to use.
420
- 5. **Data Flow**: Receive backend data down from routes (via inherited_args) rather than fetching (reading) in components
421
- 6. **Component Actions**: Non-read functions — create, update, delete — can all happen well at component-level
422
- 7. **Service Integration**: Use services directly in both components and routes for app-wide logic
423
- 8. **Service Architecture**: Keep services stateless when possible and use dependency injection for service composition
424
- 9. **Service Role**: Services interact with both routes and components and store the core logic of the application
425
- 10. **Helpers**: Keep helpers pure and stateless — they should only receive input and return output with no side effects
426
- 11. **Modifiers**: Use modifiers to isolate all direct DOM manipulation and third-party library initialisation from component logic
427
- 12. **Types Mapping**: Populate the `types` section as you define your routes and components — it is your traceability layer between data types and UI
428
-
429
- ---
546
+ ## EmberJS
430
547
 
431
- ## Ember-Tribe Development Guide
548
+ ### Ember-Tribe Development Guide
432
549
 
433
- ### Required File Outputs
550
+ #### Required File Outputs
434
551
 
435
552
  Make separate, complete code files for each category:
436
553
 
437
- ### Example installer.sh
554
+ #### Example installer.sh
438
555
 
439
556
  ```bash
440
557
  npm i chart.js
@@ -448,7 +565,7 @@ ember g modifier tooltip
448
565
  ember g service visualization-builder
449
566
  ```
450
567
 
451
- ### Default styling
568
+ #### Default styling
452
569
 
453
570
  Following is the default style that comes with tribe. Use the app.scss file for all style code. Change this based on your design styling requirements.
454
571
 
@@ -490,7 +607,7 @@ $spacers: (
490
607
  @import 'node_modules/animate.css/animate';
491
608
  ```
492
609
 
493
- ### Default application structure
610
+ #### Default application structure
494
611
 
495
612
  ```app/templates/application.hbs
496
613
  {{page-title 'Your Application Name'}}
@@ -528,11 +645,11 @@ export default class ApplicationRoute extends Route {
528
645
 
529
646
  ---
530
647
 
531
- ## EmberData Integration
648
+ ### EmberData Integration
532
649
 
533
650
  ember-tribe automatically generates models from backend track definitions through the `types` service:
534
651
 
535
- ### Data Access Patterns
652
+ #### Data Access Patterns
536
653
 
537
654
  EmberData operations always use a "modules" key for field access, except for `.id` and `.slug` properties. All field names from backend storage use underscore notation: `modules.any_field`.
538
655
 
@@ -545,7 +662,7 @@ console.log(post.modules.title); // Field access
545
662
  console.log(post.modules.content_privacy); // Universal field
546
663
  ```
547
664
 
548
- ### Query Operations
665
+ #### Query Operations
549
666
 
550
667
  ```javascript
551
668
  // Complex queries
@@ -558,6 +675,25 @@ this.store.query('post', {
558
675
  });
559
676
  ```
560
677
 
678
+ #### `modules` vs `filter`: AND vs OR Queries
679
+
680
+ When querying records, `modules` and `filter` serve distinct purposes that map directly to how the backend constructs its SQL or query logic.
681
+
682
+ **`modules`** applies **AND** logic: every key-value pair in the object must match for a record to be included. Use this when you want to narrow results to records that simultaneously satisfy all of the given conditions — for example, posts that are both `published` and belong to a specific `author_id`.
683
+
684
+ **`filter`** applies **OR** logic: a record is included if it matches *any* of the key-value pairs. Use this when you want to broaden results across multiple values of a field — for example, items whose `category` is either `tech` or `design`.
685
+
686
+ The two can be combined in the same query. For instance, to find all published posts that are tagged as either `news` or `feature`:
687
+
688
+ ```javascript
689
+ this.store.query('post', {
690
+ modules: { status: 'published' }, // must be published
691
+ filter: { tag: 'news', section: 'feature' }, // tagged news OR in feature section
692
+ });
693
+ ```
694
+
695
+ Always prefer expressing these constraints via `modules` and `filter` over post-processing results in JavaScript — the backend handles this far more efficiently.
696
+
561
697
  Smart use of EmberData can significantly reduce size of the codebase. Make sure you take advantage of that.
562
698
 
563
699
  **Universal Default Module:**
@@ -584,9 +720,9 @@ this.store.findRecord('post', 1).then((post) => {
584
720
  ```javascript
585
721
  this.store
586
722
  .query('person', {
587
- modules: { name: 'Peter', location: 'delhi' }, //results with AND
723
+ modules: { name: 'Peter', location: 'delhi' }, //AND: both conditions must match
588
724
  /*
589
- filter: { name: 'Peter', location: 'delhi' } //results with OR
725
+ filter: { name: 'Peter', location: 'delhi' } //OR: either condition can match
590
726
  sort: "location,-age,name", //minus for descending order of that field, default is -id
591
727
  page: { offset:0, limit:-1 }, //for pagination or smart uses, -1 means everything
592
728
  ignore_ids: [10,14] //excludes these IDs from results
@@ -615,69 +751,9 @@ post.destroyRecord(); // => DELETE request
615
751
 
616
752
  ---
617
753
 
618
- ## Component Architecture
619
-
620
- ### Component Structure
621
-
622
- ```javascript
623
- // Component class
624
- import Component from '@glimmer/component';
625
- import { tracked } from '@glimmer/tracking';
626
- import { action } from '@ember/object';
627
- import { service } from '@ember/service';
628
-
629
- export default class FileCardComponent extends Component {
630
- @service store;
631
- @tracked isSelected = false;
632
-
633
- @action
634
- toggleSelection() {
635
- this.isSelected = !this.isSelected;
636
- }
637
- }
638
- ```
639
-
640
- ### Template Patterns
641
-
642
- ```handlebars
643
- <div
644
- class='card {{if this.isSelected "border-primary"}}'
645
- {{on 'click' this.toggleSelection}}
646
- >
647
- <div class='card-body'>
648
- <h5 class='card-title'>{{@file.modules.title}}</h5>
649
- <p class='card-text'>{{@file.modules.description}}</p>
650
- </div>
651
- </div>
652
- ```
653
-
654
- ---
655
-
656
- ## Services Integration
657
-
658
- Make services based on storylang.json service definitions:
659
-
660
- ```javascript
661
- // app/services/visualization-builder.js
662
- import Service from '@ember/service';
663
- import { tracked } from '@glimmer/tracking';
664
- import { service } from '@ember/service';
665
-
666
- export default class VisualizationBuilderService extends Service {
667
- @service store;
668
- @tracked supportedTypes = ['network', 'tree', 'sankey'];
669
-
670
- buildVisualization(files, type, config) {
671
- // Service logic implementation
672
- }
673
- }
674
- ```
675
-
676
- ---
677
-
678
- ## Route Generation
754
+ ### Route Generation
679
755
 
680
- ### Route Creation
756
+ #### Route Creation
681
757
 
682
758
  Make routes based on storylang.json route definitions:
683
759
 
@@ -704,9 +780,9 @@ export default class FilesRoute extends Route {
704
780
 
705
781
  ---
706
782
 
707
- ## Helper System
783
+ ### Helper System
708
784
 
709
- ### Global Helpers
785
+ #### Global Helpers
710
786
 
711
787
  Make helpers based on storylang.json helper requirements:
712
788
 
@@ -721,7 +797,7 @@ export default helper(function formatDate([date, format = 'short']) {
721
797
  });
722
798
  ```
723
799
 
724
- ### Template Usage
800
+ #### Template Usage
725
801
 
726
802
  ```handlebars
727
803
  <span class='text-muted'>
@@ -731,9 +807,9 @@ export default helper(function formatDate([date, format = 'short']) {
731
807
 
732
808
  ---
733
809
 
734
- ## Modifier System
810
+ ### Modifier System
735
811
 
736
- ### DOM Interaction Modifiers
812
+ #### DOM Interaction Modifiers
737
813
 
738
814
  Make modifiers for specific DOM manipulation needs:
739
815
 
@@ -751,7 +827,7 @@ export default modifier((element, [content]) => {
751
827
  });
752
828
  ```
753
829
 
754
- ## Writing Helpers
830
+ #### Writing Helpers
755
831
 
756
832
  Helper functions are JavaScript functions callable from Ember templates that perform computations or operations beyond basic template syntax, keeping templates clean while adding dynamic functionality.
757
833
 
@@ -806,7 +882,7 @@ export default function formatCurrency(amount, currency = 'USD') {
806
882
  - Local: Component-specific logic, simple transformations
807
883
  - Global: Reusable functionality across multiple components (formatting, calculations)
808
884
 
809
- ### Component Architecture & Principle of Substitution
885
+ #### Component Architecture & Principle of Substitution
810
886
 
811
887
  Ember components should be thought of as templates that re-execute from scratch whenever data changes. Write templates that produce correct output for any given input; Ember efficiently updates only what has changed.
812
888
 
@@ -852,7 +928,7 @@ export default class CounterComponent extends Component {
852
928
  }
853
929
  ```
854
930
 
855
- ### Component Communication & Modifiers
931
+ #### Component Communication & Modifiers
856
932
 
857
933
  **Design Pattern:**
858
934
 
@@ -900,7 +976,7 @@ Modifiers applied to components pass through via `...attributes`:
900
976
  <div ...attributes>...</div>
901
977
  ```
902
978
 
903
- ### Services
979
+ #### Services
904
980
 
905
981
  Services are Ember objects that persist for the entire application duration, providing shared state or persistent connections across different parts of your app.
906
982
 
@@ -936,9 +1012,69 @@ export default class CartContentsComponent extends Component {
936
1012
 
937
1013
  ---
938
1014
 
939
- ## Code Generation Process
1015
+ ### Services Integration
1016
+
1017
+ Make services based on storylang.json service definitions:
1018
+
1019
+ ```javascript
1020
+ // app/services/visualization-builder.js
1021
+ import Service from '@ember/service';
1022
+ import { tracked } from '@glimmer/tracking';
1023
+ import { service } from '@ember/service';
1024
+
1025
+ export default class VisualizationBuilderService extends Service {
1026
+ @service store;
1027
+ @tracked supportedTypes = ['network', 'tree', 'sankey'];
1028
+
1029
+ buildVisualization(files, type, config) {
1030
+ // Service logic implementation
1031
+ }
1032
+ }
1033
+ ```
1034
+
1035
+ ---
1036
+
1037
+ ### Component Architecture
1038
+
1039
+ #### Component Structure
1040
+
1041
+ ```javascript
1042
+ // Component class
1043
+ import Component from '@glimmer/component';
1044
+ import { tracked } from '@glimmer/tracking';
1045
+ import { action } from '@ember/object';
1046
+ import { service } from '@ember/service';
1047
+
1048
+ export default class FileCardComponent extends Component {
1049
+ @service store;
1050
+ @tracked isSelected = false;
1051
+
1052
+ @action
1053
+ toggleSelection() {
1054
+ this.isSelected = !this.isSelected;
1055
+ }
1056
+ }
1057
+ ```
1058
+
1059
+ #### Template Patterns
1060
+
1061
+ ```handlebars
1062
+ <div
1063
+ class='card {{if this.isSelected "border-primary"}}'
1064
+ {{on 'click' this.toggleSelection}}
1065
+ >
1066
+ <div class='card-body'>
1067
+ <h5 class='card-title'>{{@file.modules.title}}</h5>
1068
+ <p class='card-text'>{{@file.modules.description}}</p>
1069
+ </div>
1070
+ </div>
1071
+ ```
1072
+
1073
+ ---
1074
+
1075
+ ### Forms and Input Fields
940
1076
 
941
- ### File Upload Javascript Example
1077
+ #### File upload javascript example
942
1078
 
943
1079
  ```javascript
944
1080
  import ENV from '<your-application-name>/config/environment';
@@ -968,9 +1104,156 @@ async uploadFile(file) {
968
1104
  }
969
1105
  ```
970
1106
 
1107
+ #### Input and Textarea fields
1108
+
1109
+ Use Ember's built-in `<Input>` component instead of a raw `<input>` tag — it automatically updates bound state via `@value`.
1110
+
1111
+ ```handlebars
1112
+ <div class="mb-3">
1113
+ <label for="input-name" class="form-label">Name:</label>
1114
+ <Input
1115
+ id="input-name"
1116
+ class="form-control"
1117
+ @type="text"
1118
+ @value={{this.name}}
1119
+ disabled={{this.isReadOnly}}
1120
+ maxlength="50"
1121
+ placeholder="Enter your name"
1122
+ />
1123
+ </div>
1124
+ ```
1125
+
1126
+ ```javascript
1127
+ import Component from '@glimmer/component';
1128
+ import { tracked } from '@glimmer/tracking';
1129
+
1130
+ export default class ExampleComponent extends Component {
1131
+ @tracked name = '';
1132
+ @tracked isReadOnly = false;
1133
+ }
1134
+ ```
1135
+
1136
+ ```handlebars
1137
+ <div class="form-check mb-3">
1138
+ <Input
1139
+ id="admin-checkbox"
1140
+ class="form-check-input"
1141
+ @type="checkbox"
1142
+ @checked={{this.isAdmin}}
1143
+ {{on "input" this.validateRole}}
1144
+ />
1145
+ <label for="admin-checkbox" class="form-check-label">Is Admin?</label>
1146
+ </div>
1147
+ ```
1148
+
1149
+ ```handlebars
1150
+ <div class="mb-3">
1151
+ <label for="user-comment" class="form-label">Comment:</label>
1152
+ <Textarea
1153
+ id="user-comment"
1154
+ class="form-control"
1155
+ @value={{this.userComment}}
1156
+ rows="6"
1157
+ />
1158
+ </div>
1159
+ ```
1160
+
1161
+ **Key rules for `<Input>` and `<Textarea>`:**
1162
+ - `@value`, `@type`, and `@checked` must be passed as **arguments** (with `@`).
1163
+ - Use the `{{on}}` modifier for event handling (e.g. `{{on "input" this.handler}}`).
1164
+ - Bootstrap styles `form-control` correctly when `disabled` is present
1165
+
1166
+ #### ember-power-select example
1167
+
1168
+ `ember-power-select` is the recommended way to implement searchable, single, and multi-select dropdowns in ember-tribe. It is pre-installed and works alongside Bootstrap 5.x. Use it wherever a native `<select>` would be insufficient — e.g. when you need search/filter, async options, or multi-select.
1169
+
1170
+ **Single select (Bootstrap-compatible wrapper):**
1171
+
1172
+ ```handlebars
1173
+ <div class="mb-3">
1174
+ <label class="form-label">Assign Category:</label>
1175
+ <div class="form-control p-0 border-0">
1176
+ <PowerSelect
1177
+ @options={{this.categories}}
1178
+ @selected={{this.selectedCategory}}
1179
+ @onChange={{this.handleCategoryChange}}
1180
+ @placeholder="Select a category"
1181
+ as |category|
1182
+ >
1183
+ {{category.name}}
1184
+ </PowerSelect>
1185
+ </div>
1186
+ </div>
1187
+ ```
1188
+
1189
+ ```javascript
1190
+ import Component from '@glimmer/component';
1191
+ import { tracked } from '@glimmer/tracking';
1192
+ import { action } from '@ember/object';
1193
+
1194
+ export default class ExampleComponent extends Component {
1195
+ @tracked selectedCategory = null;
1196
+
1197
+ categories = [
1198
+ { id: 1, name: 'Design' },
1199
+ { id: 2, name: 'Engineering' },
1200
+ { id: 3, name: 'Marketing' },
1201
+ ];
1202
+
1203
+ @action
1204
+ handleCategoryChange(category) {
1205
+ this.selectedCategory = category;
1206
+ }
1207
+ }
1208
+ ```
1209
+
1210
+ **Multi-select variant:**
1211
+
1212
+ ```handlebars
1213
+ <div class="mb-3">
1214
+ <label class="form-label">Assign Tags:</label>
1215
+ <div class="form-control p-0 border-0">
1216
+ <PowerSelectMultiple
1217
+ @options={{this.availableTags}}
1218
+ @selected={{this.selectedTags}}
1219
+ @onChange={{this.handleTagsChange}}
1220
+ @placeholder="Select tags"
1221
+ as |tag|
1222
+ >
1223
+ {{tag.label}}
1224
+ </PowerSelectMultiple>
1225
+ </div>
1226
+ </div>
1227
+ ```
1228
+
1229
+ **Async options loaded from the store:**
1230
+
1231
+ ```handlebars
1232
+ <div class="mb-3">
1233
+ <label class="form-label">Select Project:</label>
1234
+ <div class="form-control p-0 border-0">
1235
+ <PowerSelect
1236
+ @options={{this.projects}}
1237
+ @selected={{this.selectedProject}}
1238
+ @onChange={{this.handleProjectChange}}
1239
+ @searchField="name"
1240
+ @placeholder="Search projects..."
1241
+ as |project|
1242
+ >
1243
+ {{project.modules.name}}
1244
+ </PowerSelect>
1245
+ </div>
1246
+ </div>
1247
+ ```
1248
+
1249
+ **Key rules for `<PowerSelect>`:**
1250
+ - `@options`, `@selected`, and `@onChange` are always required arguments.
1251
+ - Use `@searchField` to specify which object property drives the built-in search filter.
1252
+ - For multi-select, use `<PowerSelectMultiple>` — the `@onChange` callback receives the full updated array, so assign it directly to your tracked property.
1253
+
971
1254
  ---
972
1255
 
973
- ## Deploying to Junction (Self-Hosted)
1256
+ ### Deploying to Junction (Self-Hosted)
974
1257
 
975
1258
  After building your Ember app, run `php-dist` to prepare the `dist/` folder for PHP middleware:
976
1259
 
@@ -985,19 +1268,6 @@ You can then upload the `dist/` folder to [Junction (open source)](http://localh
985
1268
 
986
1269
  ---
987
1270
 
988
- ## Best Practices
989
-
990
- 1. **Module Access**: Remember to use `modules.field_name` for backend fields
991
- 2. **npm packages vs ember addons**: Prioritize npm packages over ember addons for better compatibility, when both are offering similar functionality
992
- 3. **Minimal Controllers**: Logic should ideally reside in components and services
993
- 4. **Bootstrap 5.x Foundation**: Responsive, accessible design system
994
- 5. **Use FontAwesome 6.x**: Comprehensive icon library
995
- 6. **Animations**: If required, use animate.css for enhanced user experience. Prefer subtle animations (fadeIn, slideIn). Avoid overwhelming users with excessive animation
996
- 7. **Access Cache**: Leverage EmberData caching with `peekRecord`
997
- 8. **Avoid array manipulations of backend data**: Use backend filtering over frontend array manipulation
998
-
999
- ---
1000
-
1001
1271
  # License
1002
1272
 
1003
1273
  This project is licensed under the [GNU GPL v3 License](LICENSE.md).