ember-tribe 2.6.8 → 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
@@ -41,7 +81,7 @@ The addon automatically configures following essential packages:
41
81
 
42
82
  ---
43
83
 
44
- ## Core File Structure
84
+ ## Folder Structure
45
85
 
46
86
  ```
47
87
  app/
@@ -65,32 +105,35 @@ installer.sh
65
105
 
66
106
  These rules are **mandatory** for all Tribe-compatible code. Follow them strictly and do not deviate unless explicitly instructed.
67
107
 
68
- ### General Rules
108
+ ### Code Rules
109
+
110
+ 1. **EmberJS 6.x Compatibility — Strictly Required**
111
+ All generated code must be strictly compatible with EmberJS 6.x.
69
112
 
70
- 1. **Bootstrap 5.x — Required Foundation**
113
+ 2. **Bootstrap 5.x — Required Foundation**
71
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.
72
115
 
73
- 2. **Backend Field Access**
116
+ 3. **Backend Field Access**
74
117
  Always access backend fields through the `modules` object — e.g. `object.modules.field_name`. Never access backend fields directly.
75
118
 
76
- 3. **npm Packages over Ember Addons**
119
+ 4. **npm Packages over Ember Addons**
77
120
  When an npm package and an Ember addon offer equivalent functionality, always prefer the npm package for better long-term compatibility.
78
121
 
79
- 4. **Icons — FontAwesome 6.x Only**
122
+ 5. **Icons — FontAwesome 6.x Only**
80
123
  Use FontAwesome 6.x for all icons. Do not use any other icon library unless the project description explicitly specifies one.
81
124
 
82
- 5. **Animations — Subtle and Purposeful**
125
+ 6. **Animations — Subtle and Purposeful**
83
126
  If animations are needed, use `animate.css`. Keep animations subtle — prefer fades and minimal slides. Avoid anything that feels flashy or distracting.
84
127
 
85
- 6. **EmberData Caching**
128
+ 7. **EmberData Caching**
86
129
  When data has already been loaded into the store, retrieve it with `peekRecord` instead of making a new network request.
87
130
 
88
- 7. **Backend Filtering over Frontend Filtering**
131
+ 8. **Backend Filtering over Frontend Filtering**
89
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.
90
133
 
91
134
  ---
92
135
 
93
- ### Storylang Architecture Rules
136
+ ### Storylang Rules
94
137
 
95
138
  Follow this strict order of thinking when designing any feature:
96
139
 
@@ -102,20 +145,20 @@ Always begin by understanding your data types, then define the routes that load
102
145
 
103
146
  **Types**
104
147
 
105
- 11. **Start by Understanding Your Data**
148
+ 8. **Start by Understanding Your Data**
106
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.
107
150
 
108
151
  ---
109
152
 
110
153
  **Routes**
111
154
 
112
- 8. **Route Naming**
155
+ 9. **Route Naming**
113
156
  Match route names to user mental models. Use consistent, predictable naming conventions so that routes are self-documenting.
114
157
 
115
- 9. **Routes Are for Fetching, Not Logic**
158
+ 10. **Routes Are for Fetching, Not Logic**
116
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.
117
160
 
118
- 10. **Route Parameters**
161
+ 11. **Route Parameters**
119
162
  Keep `get_vars` minimal and meaningful. Load only the data types that each specific route actually needs — avoid over-fetching.
120
163
 
121
164
  ---
@@ -132,40 +175,51 @@ Always begin by understanding your data types, then define the routes that load
132
175
 
133
176
  **Helpers**
134
177
 
135
- 17. **Helpers Must Be Pure and Stateless**
178
+ 14. **Helpers Must Be Pure and Stateless**
136
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.
137
180
 
138
181
  ---
139
182
 
140
183
  **Modifiers**
141
184
 
142
- 18. **Modifiers Own All DOM Interaction**
185
+ 15. **Modifiers Own All DOM Interaction**
143
186
  Any direct DOM manipulation or third-party library initialisation must live in a modifier.
144
187
 
145
188
  ---
146
189
 
147
190
  **Services**
148
191
 
149
- 15. **Services Are the Core Logic Layer**
192
+ 16. **Services Are the Core Logic Layer**
150
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.
151
194
 
152
- 16. **Keep Services Stateless When Possible**
195
+ 17. **Keep Services Stateless When Possible**
153
196
  Avoid storing transient state in services. Where services must depend on one another, use dependency injection.
154
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
+
155
207
  ---
156
208
 
157
209
  **Components**
158
210
 
159
- 14. **Components are not always required**
211
+ 21. **Components are not always required**
160
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.
161
213
 
162
214
  ---
163
215
 
164
- ## Storylang CLI
216
+ ## Storylang
217
+
218
+ ### Storylang CLI
165
219
 
166
220
  ember-tribe ships with a command-line tool called `storylang` that synchronises the `config/storylang.json` specification with the actual Ember project files.
167
221
 
168
- ### Usage
222
+ #### Usage
169
223
 
170
224
  ```bash
171
225
  node storylang
@@ -182,26 +236,26 @@ node storylang
182
236
  # => config/storylang.json updated from project files
183
237
  ```
184
238
 
185
- ### Typical Workflow
239
+ #### Typical Workflow
186
240
 
187
241
  Run `node storylang` periodically to keep `config/storylang.json` in sync as the project evolves.
188
242
 
189
243
  ---
190
244
 
191
- ## Storylang.json Documentation
245
+ ### Storylang.json Documentation
192
246
 
193
- ### Overview
247
+ #### Overview
194
248
 
195
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.
196
250
 
197
- ### Purpose
251
+ #### Purpose
198
252
 
199
253
  The storylang.json file serves as a blueprint for frontend developers to understand:
200
254
 
201
255
  - What routes, components, services, helpers, modifiers and types are required
202
256
  - How data flows through the application
203
257
 
204
- ### File Structure
258
+ #### File Structure
205
259
 
206
260
  The storylang.json file contains seven main sections:
207
261
 
@@ -216,9 +270,10 @@ The storylang.json file contains seven main sections:
216
270
  }
217
271
  ```
218
272
 
219
- ### Section Definitions
220
273
 
221
- ### 1. Types
274
+ #### Section Definitions
275
+
276
+ #### 1. Types
222
277
 
223
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.
224
279
 
@@ -243,10 +298,12 @@ The storylang.json file contains seven main sections:
243
298
 
244
299
  ---
245
300
 
246
- ### 2. Routes
301
+ #### 2. Routes
247
302
 
248
303
  **Purpose**: Defines the application's routes and their requirements.
249
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.
306
+
250
307
  **Format**:
251
308
 
252
309
  ```json
@@ -256,7 +313,9 @@ The storylang.json file contains seven main sections:
256
313
  "name": "route-name", //should match Ember router.js
257
314
  "tracked_vars": [{ "<variableName>": "<dataType>" }],
258
315
  "get_vars": [{ "<paramName>": "<dataType>" }],
259
- "actions": ["action1", "action2"],
316
+ "getters": ["derived-property-name"],
317
+ "actions": ["frontend-action", { "backend-action": ["custom/path/file.php"] }],
318
+ "functions": ["helper-method", "compute-something"],
260
319
  "helpers": ["helper1"],
261
320
  "services": ["service1"],
262
321
  "components": ["component1", "component2"],
@@ -266,9 +325,11 @@ The storylang.json file contains seven main sections:
266
325
  }
267
326
  ```
268
327
 
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"] }`.
329
+
269
330
  ---
270
331
 
271
- ### 3. Helpers
332
+ #### 3. Helpers
272
333
 
273
334
  **Purpose**: Defines custom template helpers — pure functions used in templates to format, compute or transform data for display.
274
335
 
@@ -310,7 +371,7 @@ The storylang.json file contains seven main sections:
310
371
 
311
372
  ---
312
373
 
313
- ### 4. Modifiers
374
+ #### 4. Modifiers
314
375
 
315
376
  **Purpose**: Defines custom Ember modifiers — functions that directly interact with DOM elements to attach behaviour, third-party libraries or event listeners.
316
377
 
@@ -352,7 +413,7 @@ The storylang.json file contains seven main sections:
352
413
 
353
414
  ---
354
415
 
355
- ### 5. Services
416
+ #### 5. Services
356
417
 
357
418
  **Purpose**: Defines custom Ember services needed by the application.
358
419
 
@@ -364,7 +425,9 @@ The storylang.json file contains seven main sections:
364
425
  {
365
426
  "name": "service-name",
366
427
  "tracked_vars": [{ "<variableName>": "<dataType>" }],
367
- "actions": ["action1", "action2"],
428
+ "getters": ["derived-property-name"],
429
+ "actions": ["frontend-action", { "backend-action": ["custom/path/file.php"] }],
430
+ "functions": ["internal-method"],
368
431
  "helpers": ["helper1"],
369
432
  "services": ["dependency1", "dependency2"]
370
433
  }
@@ -380,13 +443,14 @@ The storylang.json file contains seven main sections:
380
443
  {
381
444
  "name": "visualization-builder",
382
445
  "tracked_vars": [
383
- { "currentVisualization": "object" },
446
+ { "dataMatrix": "object" },
384
447
  { "availableTypes": "array" }
385
448
  ],
449
+ "functions": ["build-visualization"],
386
450
  "actions": [
387
- "createVisualization",
388
- "updateVisualization",
389
- "deleteVisualization"
451
+ "reset-state",
452
+ { "assemble-data-for-visualization": ["custom/visualizations/assemble-data.php"] },
453
+ "updateVisualization"
390
454
  ],
391
455
  "helpers": ["validateConfig", "generatePreview"],
392
456
  "services": ["store", "router"]
@@ -397,7 +461,7 @@ The storylang.json file contains seven main sections:
397
461
 
398
462
  ---
399
463
 
400
- ### 6. Components
464
+ #### 6. Components
401
465
 
402
466
  **Purpose**: Defines reusable UI components that will be built for the application.
403
467
 
@@ -411,7 +475,9 @@ The storylang.json file contains seven main sections:
411
475
  "type": "component-type",
412
476
  "tracked_vars": [{ "<variableName>": "<dataType>" }],
413
477
  "inherited_args": [{ "<argumentName>": "<argType>" }],
414
- "actions": ["action1", "action2"],
478
+ "getters": ["derived-property-name"],
479
+ "actions": ["frontend-action", { "backend-action": ["custom/path/file.php"] }],
480
+ "functions": ["internal-method"],
415
481
  "helpers": ["helper1", "helper2"],
416
482
  "modifiers": ["modifier1"],
417
483
  "services": ["service1", "service2"]
@@ -420,6 +486,7 @@ The storylang.json file contains seven main sections:
420
486
  }
421
487
  ```
422
488
 
489
+
423
490
  **Example**:
424
491
 
425
492
  ```json
@@ -434,7 +501,8 @@ The storylang.json file contains seven main sections:
434
501
  { "onEdit": "action" },
435
502
  { "onDelete": "action" }
436
503
  ],
437
- "actions": ["toggleSelection", "expandDetails", "editFile", "deleteFile"],
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"] }],
438
506
  "helpers": ["formatDate", "truncateText"],
439
507
  "modifiers": ["tooltip"],
440
508
  "services": ["store", "router"]
@@ -445,9 +513,9 @@ The storylang.json file contains seven main sections:
445
513
 
446
514
  ---
447
515
 
448
- ### Data Types Reference
516
+ #### Data Types Reference
449
517
 
450
- ### Data Types (dataType)
518
+ #### Data Types (dataType)
451
519
 
452
520
  - `string`: Text values
453
521
  - `int`: Integer numbers
@@ -455,32 +523,35 @@ The storylang.json file contains seven main sections:
455
523
  - `array`: List of items
456
524
  - `object`: Complex data structure
457
525
 
458
- ### Argument Types (argType)
526
+ #### Argument Types (argType)
459
527
 
460
528
  - `var`: Passed data/state
461
- - `fn`: Callback function
529
+ - `function`: Callback function
462
530
  - `get`: Get function
463
531
  - `action`: User interaction handler
464
532
 
465
533
  ---
466
534
 
467
- ### Integration with Other Files
535
+ #### Integration with Other Files
468
536
 
469
- ### With types.json
537
+ #### With types.json
470
538
 
471
539
  - Type names used in routes should match type names from `types.json`
472
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`
473
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)
474
543
 
475
544
  ---
476
545
 
477
- ## Ember-Tribe Development Guide
546
+ ## EmberJS
547
+
548
+ ### Ember-Tribe Development Guide
478
549
 
479
- ### Required File Outputs
550
+ #### Required File Outputs
480
551
 
481
552
  Make separate, complete code files for each category:
482
553
 
483
- ### Example installer.sh
554
+ #### Example installer.sh
484
555
 
485
556
  ```bash
486
557
  npm i chart.js
@@ -494,7 +565,7 @@ ember g modifier tooltip
494
565
  ember g service visualization-builder
495
566
  ```
496
567
 
497
- ### Default styling
568
+ #### Default styling
498
569
 
499
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.
500
571
 
@@ -536,7 +607,7 @@ $spacers: (
536
607
  @import 'node_modules/animate.css/animate';
537
608
  ```
538
609
 
539
- ### Default application structure
610
+ #### Default application structure
540
611
 
541
612
  ```app/templates/application.hbs
542
613
  {{page-title 'Your Application Name'}}
@@ -574,11 +645,11 @@ export default class ApplicationRoute extends Route {
574
645
 
575
646
  ---
576
647
 
577
- ## EmberData Integration
648
+ ### EmberData Integration
578
649
 
579
650
  ember-tribe automatically generates models from backend track definitions through the `types` service:
580
651
 
581
- ### Data Access Patterns
652
+ #### Data Access Patterns
582
653
 
583
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`.
584
655
 
@@ -591,7 +662,7 @@ console.log(post.modules.title); // Field access
591
662
  console.log(post.modules.content_privacy); // Universal field
592
663
  ```
593
664
 
594
- ### Query Operations
665
+ #### Query Operations
595
666
 
596
667
  ```javascript
597
668
  // Complex queries
@@ -604,6 +675,25 @@ this.store.query('post', {
604
675
  });
605
676
  ```
606
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
+
607
697
  Smart use of EmberData can significantly reduce size of the codebase. Make sure you take advantage of that.
608
698
 
609
699
  **Universal Default Module:**
@@ -630,9 +720,9 @@ this.store.findRecord('post', 1).then((post) => {
630
720
  ```javascript
631
721
  this.store
632
722
  .query('person', {
633
- modules: { name: 'Peter', location: 'delhi' }, //results with AND
723
+ modules: { name: 'Peter', location: 'delhi' }, //AND: both conditions must match
634
724
  /*
635
- filter: { name: 'Peter', location: 'delhi' } //results with OR
725
+ filter: { name: 'Peter', location: 'delhi' } //OR: either condition can match
636
726
  sort: "location,-age,name", //minus for descending order of that field, default is -id
637
727
  page: { offset:0, limit:-1 }, //for pagination or smart uses, -1 means everything
638
728
  ignore_ids: [10,14] //excludes these IDs from results
@@ -661,9 +751,9 @@ post.destroyRecord(); // => DELETE request
661
751
 
662
752
  ---
663
753
 
664
- ## Route Generation
754
+ ### Route Generation
665
755
 
666
- ### Route Creation
756
+ #### Route Creation
667
757
 
668
758
  Make routes based on storylang.json route definitions:
669
759
 
@@ -690,9 +780,9 @@ export default class FilesRoute extends Route {
690
780
 
691
781
  ---
692
782
 
693
- ## Helper System
783
+ ### Helper System
694
784
 
695
- ### Global Helpers
785
+ #### Global Helpers
696
786
 
697
787
  Make helpers based on storylang.json helper requirements:
698
788
 
@@ -707,7 +797,7 @@ export default helper(function formatDate([date, format = 'short']) {
707
797
  });
708
798
  ```
709
799
 
710
- ### Template Usage
800
+ #### Template Usage
711
801
 
712
802
  ```handlebars
713
803
  <span class='text-muted'>
@@ -717,9 +807,9 @@ export default helper(function formatDate([date, format = 'short']) {
717
807
 
718
808
  ---
719
809
 
720
- ## Modifier System
810
+ ### Modifier System
721
811
 
722
- ### DOM Interaction Modifiers
812
+ #### DOM Interaction Modifiers
723
813
 
724
814
  Make modifiers for specific DOM manipulation needs:
725
815
 
@@ -737,7 +827,7 @@ export default modifier((element, [content]) => {
737
827
  });
738
828
  ```
739
829
 
740
- ## Writing Helpers
830
+ #### Writing Helpers
741
831
 
742
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.
743
833
 
@@ -792,7 +882,7 @@ export default function formatCurrency(amount, currency = 'USD') {
792
882
  - Local: Component-specific logic, simple transformations
793
883
  - Global: Reusable functionality across multiple components (formatting, calculations)
794
884
 
795
- ### Component Architecture & Principle of Substitution
885
+ #### Component Architecture & Principle of Substitution
796
886
 
797
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.
798
888
 
@@ -838,7 +928,7 @@ export default class CounterComponent extends Component {
838
928
  }
839
929
  ```
840
930
 
841
- ### Component Communication & Modifiers
931
+ #### Component Communication & Modifiers
842
932
 
843
933
  **Design Pattern:**
844
934
 
@@ -886,7 +976,7 @@ Modifiers applied to components pass through via `...attributes`:
886
976
  <div ...attributes>...</div>
887
977
  ```
888
978
 
889
- ### Services
979
+ #### Services
890
980
 
891
981
  Services are Ember objects that persist for the entire application duration, providing shared state or persistent connections across different parts of your app.
892
982
 
@@ -922,7 +1012,7 @@ export default class CartContentsComponent extends Component {
922
1012
 
923
1013
  ---
924
1014
 
925
- ## Services Integration
1015
+ ### Services Integration
926
1016
 
927
1017
  Make services based on storylang.json service definitions:
928
1018
 
@@ -944,9 +1034,9 @@ export default class VisualizationBuilderService extends Service {
944
1034
 
945
1035
  ---
946
1036
 
947
- ## Component Architecture
1037
+ ### Component Architecture
948
1038
 
949
- ### Component Structure
1039
+ #### Component Structure
950
1040
 
951
1041
  ```javascript
952
1042
  // Component class
@@ -966,7 +1056,7 @@ export default class FileCardComponent extends Component {
966
1056
  }
967
1057
  ```
968
1058
 
969
- ### Template Patterns
1059
+ #### Template Patterns
970
1060
 
971
1061
  ```handlebars
972
1062
  <div
@@ -982,9 +1072,9 @@ export default class FileCardComponent extends Component {
982
1072
 
983
1073
  ---
984
1074
 
985
- ## Forms and Input Fields
1075
+ ### Forms and Input Fields
986
1076
 
987
- ### File upload javascript example
1077
+ #### File upload javascript example
988
1078
 
989
1079
  ```javascript
990
1080
  import ENV from '<your-application-name>/config/environment';
@@ -1014,7 +1104,7 @@ async uploadFile(file) {
1014
1104
  }
1015
1105
  ```
1016
1106
 
1017
- ### Input and Textarea fields
1107
+ #### Input and Textarea fields
1018
1108
 
1019
1109
  Use Ember's built-in `<Input>` component instead of a raw `<input>` tag — it automatically updates bound state via `@value`.
1020
1110
 
@@ -1073,7 +1163,7 @@ export default class ExampleComponent extends Component {
1073
1163
  - Use the `{{on}}` modifier for event handling (e.g. `{{on "input" this.handler}}`).
1074
1164
  - Bootstrap styles `form-control` correctly when `disabled` is present
1075
1165
 
1076
- ### ember-power-select example
1166
+ #### ember-power-select example
1077
1167
 
1078
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.
1079
1169
 
@@ -1163,7 +1253,7 @@ export default class ExampleComponent extends Component {
1163
1253
 
1164
1254
  ---
1165
1255
 
1166
- ## Deploying to Junction (Self-Hosted)
1256
+ ### Deploying to Junction (Self-Hosted)
1167
1257
 
1168
1258
  After building your Ember app, run `php-dist` to prepare the `dist/` folder for PHP middleware:
1169
1259