ember-tribe 2.5.2 → 2.5.3

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
@@ -1,135 +1,475 @@
1
1
  # ember-tribe
2
2
 
3
- An addon that connects EmberJS to Tribe API.
4
- Tribe is a collaborative project management framework - https://github.com/tribe-framework/tribe
3
+ An addon that connects EmberJS to Tribe API, bridging the gap between backend data structures and frontend application development. It helps you make an Ember app based on Junction's Blueprint file, also called `types.json`.
5
4
 
6
- https://junction.express provides an interface to build a Tribe compatibale no-code backend in minutes.
5
+ Tribe is a project management framework built for ease of collaboration - https://github.com/tribe-framework/tribe
7
6
 
8
- ## Compatibility
7
+ ## Installation and Setup
9
8
 
10
- - Ember.js v6.0 or above
11
- - Ember CLI v6.0 or above
9
+ ### Prerequisites
12
10
 
13
- ## Installation
11
+ - Ember CLI v6.0 – v6.4
12
+ - Node.js (latest LTS)
13
+ - Junction (optional) - via https://junction.express (cloud version) or https://tribe-framework.org (open source version)
14
14
 
15
- 1. Install
15
+ ### Installation
16
16
 
17
- ```
17
+ ```bash
18
18
  ember install ember-tribe
19
19
  ```
20
20
 
21
- 2. Configure
21
+ - Enter `TRIBE_API_URL` and `TRIBE_API_KEY` in `.env` file (copy of `.env.sample`). You can find these values in the Junction dashboard at [junction.express](https://junction.express) (cloud) or your self-hosted Tribe admin panel.
22
22
 
23
- - Enter TRIBE_API_URL and TRIBE_API_KEY in .env file, copy of .env.sample
23
+ ---
24
24
 
25
- # Ember-Tribe Documentation
25
+ The addon automatically configures following essential packages:
26
26
 
27
- ## Overview
27
+ **Ember Addons:** `ember-cli-dotenv`, `ember-cli-sass`, `ember-modifier`, `ember-composable-helpers`, `ember-truth-helpers`, `ember-file-upload` , `ember-power-select`
28
28
 
29
- ember-tribe is a powerful Ember.js addon that bridges the gap between backend CMS data structures and frontend application development. It helps you make an Ember app based on storylang.json and simplified-types.json files, if and when these files are availble.
29
+ **NPM Packages:** `bootstrap`, `@popperjs/core`, `animate.css`, `video.js`, `swiper`, `howler`
30
30
 
31
- ## Purpose
31
+ ---
32
32
 
33
- Ember-tribe enables rapid application development by:
33
+ ## Core File Structure
34
34
 
35
- - Providing pre-configured addon ecosystem for common functionality
36
- - Implementing standardized patterns for Tribe applications
37
- - Creating responsive, Bootstrap-based interfaces with minimal configuration
38
- - Supporting automatic model generation from backend track definitions in simplified-types.json
35
+ ```
36
+ app/
37
+ ├── routes/
38
+ ├── templates/
39
+ ├── controllers/
40
+ ├── components/
41
+ ├── helpers/
42
+ ├── modifiers/
43
+ ├── services/
44
+ ├── styles/app.scss
45
+ └── router.js
46
+ config/
47
+ └── storylang.json
48
+ installer.sh
49
+ ```
39
50
 
40
- ## Installation and Setup
51
+ ---
41
52
 
42
- ### Prerequisites
53
+ ## Storylang CLI
43
54
 
44
- - Ember.js 6.x
45
- - Node.js (latest LTS)
46
- - Junction backend
55
+ ember-tribe ships with a command-line tool called `storylang` that synchronises the `config/storylang.json` specification with the actual Ember project files.
47
56
 
48
- ### Installation
57
+ ### Usage
49
58
 
50
59
  ```bash
51
- ember install ember-tribe
60
+ storylang <command> [options]
52
61
  ```
53
62
 
54
- The addon automatically configures following essential packages:
63
+ **Commands:**
64
+
65
+ | Command | Description |
66
+ |---|---|
67
+ | `storylang pull` | Updates `config/storylang.json` from current project files |
68
+ | `storylang push` | Generates any missing routes, components, services, helpers, modifiers, and controllers |
69
+ | `storylang push -o` | Regenerates all artefacts in `storylang.json`, overwriting existing files |
55
70
 
56
- **Ember Addons:**
71
+ **Example:**
57
72
 
58
- - `ember-cli-dotenv` - Environment configuration
59
- - `ember-cli-sass` - SCSS support
60
- - `ember-modifier` - DOM manipulation helpers
61
- - `ember-composable-helpers` - Template utilities
62
- - `ember-truth-helpers` - Boolean logic helpers
63
- - `ember-file-upload` - File handling
64
- - `ember-power-select` - Advanced select components
65
- - `ember-table` - Data tables
66
- - `ember-animated` - Smooth animations
73
+ ```bash
74
+ cd /path/to/ember/app
67
75
 
68
- **NPM Packages:**
76
+ storylang pull
77
+ # => config/storylang.json updated from project files
69
78
 
70
- - `bootstrap` - UI framework
71
- - `@popperjs/core` - Bootstrap dependency
72
- - `animate.css` - CSS animations
73
- - `video.js` - Video player
74
- - `swiper` - Touch sliders
75
- - `howler` - Audio management
79
+ storylang push
80
+ # => missing routes, components, services, helpers, modifiers, and controllers are generated
81
+
82
+ storylang push -o
83
+ # => all artefacts in storylang.json are (re)generated, overwriting existing files
84
+ ```
76
85
 
77
- ## Core Architecture
86
+ ### Typical Workflow
78
87
 
79
- ### Think in this order
88
+ 1. Define your `config/storylang.json` spec (manually or with the help of an AI tool), then run `storylang push` to generate the project files.
89
+ 2. Run `storylang pull` periodically to keep the spec in sync as the project evolves.
80
90
 
81
- ember-tribe follows a specific order that includes our learnings and Ember.js best practices, and must be followed exactly:
91
+ ---
82
92
 
83
- 1. **router.js** - Application routing structure
84
- 2. **app/routes** - Route handlers and data loading
85
- 3. **app/templates & controllers** - UI templates and minimal controllers
86
- 4. **app/components** - Reusable UI components with component specific logic
87
- 5. **app/services** - Application-wide logic and state
93
+ ## Storylang.json Documentation
88
94
 
89
- This order ensures proper dependency resolution and optimal code organization.
95
+ ### Overview
90
96
 
91
- ### Package Philosophy
97
+ 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.
92
98
 
93
- **Try using npm packages over ember addons because ember addons are sometimes outdated.** Ember-tribe prioritizes npm packages for better compatibility and maintenance.
99
+ ### Purpose
100
+
101
+ The storylang.json file serves as a blueprint for frontend developers to understand:
102
+
103
+ - What routes, components, services, helpers, modifiers and types are required
104
+ - How data flows through the application
94
105
 
95
106
  ### File Structure
96
107
 
108
+ The storylang.json file contains seven main sections:
109
+
110
+ ```json
111
+ {
112
+ "implementation_approach": "...",
113
+ "types": [...],
114
+ "components": [...],
115
+ "routes": [...],
116
+ "services": [...],
117
+ "helpers": [...],
118
+ "modifiers": [...]
119
+ }
97
120
  ```
98
- app/
99
- ├── routes/
100
- ├── templates/
101
- ├── controllers/
102
- ├── components/
103
- ├── helpers/
104
- ├── modifiers/
105
- ├── services/
106
- ├── styles/app.scss
107
- └── router.js
108
- installer.sh
121
+
122
+ ### Section Definitions
123
+
124
+ ### 1. Implementation Approach
125
+
126
+ **Purpose**: Provides a high-level technical overview of how the frontend interface would work.
127
+
128
+ **Format**:
129
+
130
+ ```json
131
+ {
132
+ "implementation_approach": "Two-paragraph description explaining technical approach and key functionality."
133
+ }
134
+ ```
135
+
136
+ ---
137
+
138
+ ### 2. Types
139
+
140
+ **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.
141
+
142
+ **Format**:
143
+
144
+ ```json
145
+ {
146
+ "types": [
147
+ {
148
+ "slug": "type-slug", //type slug as defined in `types.json` blueprint
149
+ "used_in": { //where this type is used
150
+ "routes": ["route-name"],
151
+ "components": ["component-name"],
152
+ "services": ["service-name"],
153
+ "helpers": ["helper-name"],
154
+ "modifiers": ["modifier-name"]
155
+ }
156
+ }
157
+ ]
158
+ }
109
159
  ```
110
160
 
111
- ## Required File Outputs
161
+ ---
162
+
163
+ ### 3. Components
164
+
165
+ **Purpose**: Defines reusable UI components that will be built for the application.
166
+
167
+ **Format**:
168
+
169
+ ```json
170
+ {
171
+ "components": [
172
+ {
173
+ "name": "component-name",
174
+ "type": "component-type",
175
+ "tracked_vars": [{ "<variableName>": "<dataType>" }],
176
+ "inherited_args": [{ "<argumentName>": "<argType>" }],
177
+ "actions": ["action1", "action2"],
178
+ "helpers": ["helper1", "helper2"],
179
+ "modifiers": ["modifier1"],
180
+ "services": ["service1", "service2"]
181
+ }
182
+ ]
183
+ }
184
+ ```
185
+
186
+ **Built-in Component Types in ember-tribe**:
187
+
188
+ - **Layout**: `table`, `figure`, `accordion`, `card`, `list-group`, `navbar`, `nav`, `tab`, `breadcrumb`
189
+ - **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`
190
+
191
+ **Example**:
192
+
193
+ ```json
194
+ {
195
+ "components": [
196
+ {
197
+ "name": "file-summary-card",
198
+ "type": "card",
199
+ "tracked_vars": [{ "isSelected": "bool" }, { "isExpanded": "bool" }],
200
+ "inherited_args": [
201
+ { "file": "var" },
202
+ { "onEdit": "action" },
203
+ { "onDelete": "action" }
204
+ ],
205
+ "actions": ["toggleSelection", "expandDetails", "editFile", "deleteFile"],
206
+ "helpers": ["formatDate", "truncateText"],
207
+ "modifiers": ["tooltip"],
208
+ "services": ["store", "router"]
209
+ }
210
+ ]
211
+ }
212
+ ```
213
+
214
+ ---
215
+
216
+ ### 4. Routes
217
+
218
+ **Purpose**: Defines the application's routes and their requirements.
219
+
220
+ **Format**:
221
+
222
+ ```json
223
+ {
224
+ "routes": [
225
+ {
226
+ "name": "route-name", //should match Ember router.js
227
+ "tracked_vars": [{ "<variableName>": "<dataType>" }],
228
+ "get_vars": [{ "<paramName>": "<dataType>" }],
229
+ "actions": ["action1", "action2"],
230
+ "helpers": ["helper1"],
231
+ "services": ["service1"],
232
+ "components": ["component1", "component2"],
233
+ "types": ["type1", "type2"]
234
+ }
235
+ ]
236
+ }
237
+ ```
238
+
239
+ ---
240
+
241
+ ### 5. Services
242
+
243
+ **Purpose**: Defines custom Ember services needed by the application.
244
+
245
+ **Format**:
246
+
247
+ ```json
248
+ {
249
+ "services": [
250
+ {
251
+ "name": "service-name",
252
+ "tracked_vars": [{ "<variableName>": "<dataType>" }],
253
+ "actions": ["action1", "action2"],
254
+ "helpers": ["helper1"],
255
+ "services": ["dependency1", "dependency2"]
256
+ }
257
+ ]
258
+ }
259
+ ```
260
+
261
+ **Built-in Services in ember-tribe**:
262
+
263
+ - `store`: Ember Data store for CRUD operations
264
+ - `router`: Ember router service for navigation
265
+ - `types`: Automatic model generation from backend tracks
266
+ - `bootstrap`: Bootstrap CSS framework with Popper.js
267
+ - `papaparse`: CSV parsing library
268
+ - `sortablejs`: Drag-and-drop sorting
269
+ - `swiperjs`: Touch slider/carousel
270
+ - `videojs`: Video player
271
+ - `howlerjs`: Audio player
272
+
273
+ **Example**:
274
+
275
+ ```json
276
+ {
277
+ "services": [
278
+ {
279
+ "name": "visualization-builder",
280
+ "tracked_vars": [
281
+ { "currentVisualization": "object" },
282
+ { "availableTypes": "array" }
283
+ ],
284
+ "actions": [
285
+ "createVisualization",
286
+ "updateVisualization",
287
+ "deleteVisualization"
288
+ ],
289
+ "helpers": ["validateConfig", "generatePreview"],
290
+ "services": ["store", "router"]
291
+ }
292
+ ]
293
+ }
294
+ ```
295
+
296
+ ---
297
+
298
+ ### 6. Helpers
299
+
300
+ **Purpose**: Defines custom template helpers — pure functions used in templates to format, compute or transform data for display.
301
+
302
+ **Format**:
303
+
304
+ ```json
305
+ {
306
+ "helpers": [
307
+ {
308
+ "name": "helper-name",
309
+ "description": "What this helper does",
310
+ "args": [{ "<argumentName>": "<dataType>" }],
311
+ "return": "<dataType>"
312
+ }
313
+ ]
314
+ }
315
+ ```
316
+
317
+ **Helper Properties**:
318
+
319
+ - `name`: Kebab-case name of the helper
320
+ - `description`: Brief description of what the helper computes or transforms
321
+ - `args`: Input arguments the helper accepts
322
+ - `return`: The data type the helper outputs
323
+
324
+ **Example**:
325
+
326
+ ```json
327
+ {
328
+ "helpers": [
329
+ {
330
+ "name": "format-date",
331
+ "description": "Formats a raw ISO date string into a human-readable date",
332
+ "args": [{ "isoString": "string" }, { "format": "string" }],
333
+ "return": "string"
334
+ },
335
+ {
336
+ "name": "truncate-text",
337
+ "description": "Truncates a string to a given character limit and appends an ellipsis",
338
+ "args": [{ "text": "string" }, { "limit": "int" }],
339
+ "return": "string"
340
+ }
341
+ ]
342
+ }
343
+ ```
344
+
345
+ ---
346
+
347
+ ### 7. Modifiers
348
+
349
+ **Purpose**: Defines custom Ember modifiers — functions that directly interact with DOM elements to attach behaviour, third-party libraries or event listeners.
350
+
351
+ **Format**:
352
+
353
+ ```json
354
+ {
355
+ "modifiers": [
356
+ {
357
+ "name": "modifier-name",
358
+ "description": "What DOM behaviour this modifier applies",
359
+ "args": [{ "<argumentName>": "<dataType>" }],
360
+ "services": ["service1"]
361
+ }
362
+ ]
363
+ }
364
+ ```
365
+
366
+ **Modifier Properties**:
367
+
368
+ - `name`: Kebab-case name of the modifier
369
+ - `description`: Brief description of the DOM behaviour it attaches
370
+ - `args`: Arguments passed into the modifier from the template
371
+ - `services`: Ember services injected if needed
372
+
373
+ **Example**:
374
+
375
+ ```json
376
+ {
377
+ "modifiers": [
378
+ {
379
+ "name": "tooltip",
380
+ "description": "Initialises a Bootstrap tooltip on the target element using the provided label",
381
+ "args": [{ "label": "string" }, { "placement": "string" }],
382
+ "services": []
383
+ },
384
+ {
385
+ "name": "autofocus",
386
+ "description": "Sets focus on the target element when it is inserted into the DOM",
387
+ "args": [],
388
+ "services": []
389
+ }
390
+ ]
391
+ }
392
+ ```
393
+
394
+ ---
395
+
396
+ ### Data Types Reference
397
+
398
+ ### Data Types (dataType)
399
+
400
+ - `string`: Text values
401
+ - `int`: Integer numbers
402
+ - `bool`: Boolean true/false
403
+ - `array`: List of items
404
+ - `object`: Complex data structure
405
+
406
+ ### Argument Types (argType)
407
+
408
+ - `var`: Passed data/state
409
+ - `fn`: Callback function
410
+ - `get`: Get function
411
+ - `action`: User interaction handler
412
+
413
+ ---
414
+
415
+ ### Integration with Other Files
416
+
417
+ ### With types.json
418
+
419
+ - Type names used in routes should match type names from `types.json`
420
+ - 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`
421
+ - Types are the gateway to persistent storage on the backend
422
+
423
+ ---
424
+
425
+ ### Storylang Best Practices
426
+
427
+ **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**
428
+
429
+ 1. **Start with Routes**: Match route names to user mental models and use consistent naming conventions
430
+ 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
431
+ 3. **Route Parameters**: Keep get_vars minimal and meaningful, and load only necessary types for each route
432
+ 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.
433
+ 5. **Data Flow**: Receive backend data down from routes (via inherited_args) rather than fetching (reading) in components
434
+ 6. **Component Actions**: Non-read functions — create, update, delete — can all happen well at component-level
435
+ 7. **Service Integration**: Use services directly in both components and routes for app-wide logic
436
+ 8. **Service Architecture**: Keep services stateless when possible and use dependency injection for service composition
437
+ 9. **Service Role**: Services interact with both routes and components and store the core logic of the application
438
+ 10. **Helpers**: Keep helpers pure and stateless — they should only receive input and return output with no side effects
439
+ 11. **Modifiers**: Use modifiers to isolate all direct DOM manipulation and third-party library initialisation from component logic
440
+ 12. **Types Mapping**: Populate the `types` section as you define your routes and components — it is your traceability layer between data types and UI
441
+
442
+ ---
443
+
444
+ ## Ember-Tribe Development Guide
445
+
446
+ ### Required File Outputs
112
447
 
113
448
  Make separate, complete code files for each category:
114
449
 
115
- ### installer.sh
450
+ ### Example installer.sh
116
451
 
117
452
  ```bash
118
- ember g route <name>;
119
- ember g controller <name>;
120
- ember g component <name> -gc;
121
- ember g helper <name>;
122
- ember g modifier <name>;
123
- ember g service <name>;
453
+ npm i chart.js
454
+ npm i lodash
455
+ ember install ember-table
456
+ ember g route files
457
+ ember g controller files
458
+ ember g component file-card -gc
459
+ ember g helper format-date
460
+ ember g modifier tooltip
461
+ ember g service visualization-builder
124
462
  ```
125
463
 
126
- ### app/styles/app.scss
464
+ ### Default styling
127
465
 
128
- ```scss
466
+ 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.
467
+
468
+ ```app/styles/app.scss
129
469
  @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
130
470
 
131
- $font-family-sans-serif: 'IBM Plex Mono', serif !default;
132
- $display-font-family: 'IBM Plex Mono', serif !default;
471
+ $font-family-sans-serif: 'IBM Plex Mono', monospace !default;
472
+ $display-font-family: 'IBM Plex Mono', monospace !default;
133
473
 
134
474
  $primary: #000000 !default;
135
475
  $secondary: #cccccc !default;
@@ -146,59 +486,68 @@ $enable-cssgrid: true !default;
146
486
 
147
487
  $spacer: 1rem !default;
148
488
  $spacers: (
149
- 0: 0,
150
- 1: $spacer * 0.25,
151
- 2: $spacer * 0.5,
152
- 3: $spacer,
153
- 4: $spacer * 1.5,
154
- 5: $spacer * 3,
155
- 6: $spacer * 4.5,
156
- 7: $spacer * 6,
157
- 8: $spacer * 7.5,
158
- 9: $spacer * 9,
159
- 10: $spacer * 12,
489
+ 0: 0,
490
+ 1: $spacer * 0.25,
491
+ 2: $spacer * 0.5,
492
+ 3: $spacer,
493
+ 4: $spacer * 1.5,
494
+ 5: $spacer * 3,
495
+ 6: $spacer * 4.5,
496
+ 7: $spacer * 6,
497
+ 8: $spacer * 7.5,
498
+ 9: $spacer * 9,
499
+ 10: $spacer * 12,
160
500
  ) !default;
161
501
 
162
502
  @import 'node_modules/bootstrap/scss/bootstrap';
163
503
  @import 'node_modules/animate.css/animate';
164
504
  ```
165
505
 
166
- ### app/templates/application.hbs
506
+ ### Default application structure
167
507
 
168
- ```handlebars
508
+ ```app/templates/application.hbs
169
509
  {{page-title 'Your Application Name'}}
170
510
  {{outlet}}
171
511
  <BasicDropdownWormhole />
172
512
  ```
173
513
 
174
- **Application.hbs Extension Guidelines:**
514
+ ```app/routes/application.js
515
+ import Route from '@ember/routing/route';
516
+ import * as bootstrap from 'bootstrap';
517
+ import { service } from '@ember/service';
518
+ import { later } from '@ember/runloop';
519
+ import { action } from '@ember/object';
520
+
521
+ export default class ApplicationRoute extends Route {
522
+ @service types;
523
+
524
+ //auto-sync backend types
525
+ async beforeModel() { await this.types.fetchAgain() }
526
+
527
+ @action
528
+ didTransition() { later( this, () => { document.querySelector('#loading').classList.add('d-none') }, 50 ) }
529
+
530
+ @action
531
+ willTransition() { document.querySelector('#loading').classList.remove('d-none') }
532
+ }
533
+ ```
534
+
535
+ **Application Extension Guidelines:**
175
536
 
176
537
  - Extend when adding global navigation components
177
538
  - Include shared modals or dropdowns
178
539
  - Add application-wide notification systems
179
540
  - Insert global loading states or overlays
180
541
 
181
- ## EmberData Integration
542
+ ---
182
543
 
183
- ### Automatic Model Generation
544
+ ## EmberData Integration
184
545
 
185
- Ember-tribe automatically generates models from backend track definitions through the `types` service:
186
-
187
- ```javascript
188
- // app/routes/application.js
189
- export default class ApplicationRoute extends Route {
190
- @service types;
191
-
192
- async beforeModel() {
193
- // Auto-generates models from backend
194
- await this.types.fetchAgain();
195
- }
196
- }
197
- ```
546
+ ember-tribe automatically generates models from backend track definitions through the `types` service:
198
547
 
199
548
  ### Data Access Patterns
200
549
 
201
- All backend data uses the `modules` key for field access:
550
+ 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`.
202
551
 
203
552
  ```javascript
204
553
  // Accessing track data
@@ -214,61 +563,72 @@ console.log(post.modules.content_privacy); // Universal field
214
563
  ```javascript
215
564
  // Complex queries
216
565
  this.store.query('post', {
217
- modules: { status: 'published' }, // AND conditions
218
- filter: { category: 'tech' }, // OR conditions
219
- sort: 'title,-created_date', // Sort (- for desc)
220
- page: { offset: 0, limit: 10 }, // Pagination
221
- show_public_objects_only: false, // Include drafts
566
+ modules: { status: 'published' }, // AND conditions
567
+ filter: { category: 'tech' }, // OR conditions
568
+ sort: 'title,-created_date', // Sort (- for desc)
569
+ page: { offset: 0, limit: 10 }, // Pagination
570
+ show_public_objects_only: false, // Include drafts
222
571
  });
223
572
  ```
224
573
 
225
- ## Styling System
574
+ Smart use of EmberData can significantly reduce size of the codebase. Make sure you take advantage of that.
226
575
 
227
- ### Default Configuration (app.scss)
576
+ **Universal Default Module:**
577
+ All objects include: `"content_privacy": "string | public, private, pending, draft"`
228
578
 
229
- ```scss
230
- // Typography
231
- @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono...');
232
- $font-family-sans-serif: 'IBM Plex Mono', serif !default;
579
+ **Single Record Operations:**
233
580
 
234
- // Color Palette
235
- $primary: #000000 !default;
236
- $secondary: #cccccc !default;
237
- $success: #00ff00 !default;
238
- // ... additional colors
581
+ ```javascript
582
+ // Find by ID or slug
583
+ this.store.findRecord('track', 30);
584
+ this.store.findRecord('track', 'some-slug-here');
239
585
 
240
- // Bootstrap Configuration
241
- $enable-rounded: false !default;
242
- $enable-negative-margins: true !default;
243
- $enable-cssgrid: true !default;
586
+ // Access without network request (if already in store)
587
+ let post = this.store.peekRecord('post', 1);
244
588
 
245
- @import 'node_modules/bootstrap/scss/bootstrap';
246
- @import 'node_modules/animate.css/animate';
589
+ // Usage pattern
590
+ this.store.findRecord('post', 1).then((post) => {
591
+ // Access: post.id, post.slug, post.modules.<field_name>
592
+ });
247
593
  ```
248
594
 
249
- ### Design Principles
250
-
251
- - **Minimal Controllers**: Logic should reside in components and services
252
- - **Bootstrap 5.x Foundation**: Responsive, accessible design system
253
- - **Subtle Animations**: animate.css for enhanced user experience
254
- - **FontAwesome 6.x**: Comprehensive icon library
595
+ **Multiple Records:**
255
596
 
256
- ## Component Architecture
597
+ ```javascript
598
+ this.store
599
+ .query('person', {
600
+ modules: { name: 'Peter', location: 'delhi' }, //results with AND
601
+ /*
602
+ filter: { name: 'Peter', location: 'delhi' } //results with OR
603
+ sort: "location,-age,name", //minus for descending order of that field, default is -id
604
+ page: { offset:0, limit:-1 }, //for pagination or smart uses, -1 means everything
605
+ ignore_ids: [10,14] //excludes these IDs from results
606
+ show_public_objects_only: false, //default = true, if set false results include content_privacy = drafts, private or pending
607
+ */
608
+ })
609
+ .then((results) => {
610
+ // Process results
611
+ });
612
+ ```
257
613
 
258
- ### Component Types
614
+ Prefer using backend (this.store.query) for search, filter and sort, over front-end JS functions to achieve the same thing. Avoid using this.store.findAll altogether, use this.store.query with page: { offset:0, limit:-1 } instead.
259
615
 
260
- Based on storylang.json component definitions:
616
+ **CRUD Operations:**
261
617
 
262
- **Layout Components:**
618
+ ```javascript
619
+ // Update
620
+ let post = await this.store.findRecord('post', 1);
621
+ post.modules.title = 'A new title';
622
+ await post.save(); // => PATCH request
263
623
 
264
- - `table`, `figure`, `accordion`, `card`, `list-group`
265
- - `navbar`, `nav`, `tab`, `breadcrumb`
624
+ // Delete
625
+ let post = this.store.peekRecord('post', 2);
626
+ post.destroyRecord(); // => DELETE request
627
+ ```
266
628
 
267
- **Interactive Components:**
629
+ ---
268
630
 
269
- - `button`, `button-group`, `dropdown`, `modal`
270
- - `input-field`, `select`, `file-uploader`
271
- - `pagination`, `popover`, `tooltip`
631
+ ## Component Architecture
272
632
 
273
633
  ### Component Structure
274
634
 
@@ -280,13 +640,13 @@ import { action } from '@ember/object';
280
640
  import { service } from '@ember/service';
281
641
 
282
642
  export default class FileCardComponent extends Component {
283
- @service store;
284
- @tracked isSelected = false;
643
+ @service store;
644
+ @tracked isSelected = false;
285
645
 
286
- @action
287
- toggleSelection() {
288
- this.isSelected = !this.isSelected;
289
- }
646
+ @action
647
+ toggleSelection() {
648
+ this.isSelected = !this.isSelected;
649
+ }
290
650
  }
291
651
  ```
292
652
 
@@ -294,26 +654,19 @@ export default class FileCardComponent extends Component {
294
654
 
295
655
  ```handlebars
296
656
  <div
297
- class='card {{if this.isSelected "border-primary"}}'
298
- {{on 'click' this.toggleSelection}}
657
+ class='card {{if this.isSelected "border-primary"}}'
658
+ {{on 'click' this.toggleSelection}}
299
659
  >
300
- <div class='card-body'>
301
- <h5 class='card-title'>{{@file.modules.title}}</h5>
302
- <p class='card-text'>{{@file.modules.description}}</p>
303
- </div>
660
+ <div class='card-body'>
661
+ <h5 class='card-title'>{{@file.modules.title}}</h5>
662
+ <p class='card-text'>{{@file.modules.description}}</p>
663
+ </div>
304
664
  </div>
305
665
  ```
306
666
 
307
- ## Services Integration
308
-
309
- ### Built-in Services
667
+ ---
310
668
 
311
- - `store` - EmberData for CRUD operations
312
- - `router` - Navigation and route management
313
- - `types` - Automatic model generation
314
- - `bootstrap` - Bootstrap component initialization
315
-
316
- ### Custom Services
669
+ ## Services Integration
317
670
 
318
671
  Make services based on storylang.json service definitions:
319
672
 
@@ -324,15 +677,17 @@ import { tracked } from '@glimmer/tracking';
324
677
  import { service } from '@ember/service';
325
678
 
326
679
  export default class VisualizationBuilderService extends Service {
327
- @service store;
328
- @tracked supportedTypes = ['network', 'tree', 'sankey'];
680
+ @service store;
681
+ @tracked supportedTypes = ['network', 'tree', 'sankey'];
329
682
 
330
- buildVisualization(files, type, config) {
331
- // Service logic implementation
332
- }
683
+ buildVisualization(files, type, config) {
684
+ // Service logic implementation
685
+ }
333
686
  }
334
687
  ```
335
688
 
689
+ ---
690
+
336
691
  ## Route Generation
337
692
 
338
693
  ### Route Creation
@@ -344,22 +699,24 @@ import Route from '@ember/routing/route';
344
699
  import { service } from '@ember/service';
345
700
 
346
701
  export default class FilesRoute extends Route {
347
- @service store;
348
-
349
- queryParams = {
350
- page: { refreshModel: true },
351
- search: { refreshModel: true },
352
- };
353
-
354
- model(params) {
355
- return this.store.query('json_file', {
356
- page: { offset: (params.page - 1) * 10, limit: 10 },
357
- modules: params.search ? { title: params.search } : {},
358
- });
359
- }
702
+ @service store;
703
+
704
+ queryParams = {
705
+ page: { refreshModel: true },
706
+ search: { refreshModel: true },
707
+ };
708
+
709
+ async model(params) {
710
+ return await this.store.query('json_file', {
711
+ page: { offset: (params.page - 1) * 10, limit: 10 },
712
+ modules: params.search ? { title: params.search } : {},
713
+ });
714
+ }
360
715
  }
361
716
  ```
362
717
 
718
+ ---
719
+
363
720
  ## Helper System
364
721
 
365
722
  ### Global Helpers
@@ -371,9 +728,9 @@ Make helpers based on storylang.json helper requirements:
371
728
  import { helper } from '@ember/component/helper';
372
729
 
373
730
  export default helper(function formatDate([date, format = 'short']) {
374
- return new Intl.DateTimeFormat('en-US', {
375
- dateStyle: format,
376
- }).format(new Date(date));
731
+ return new Intl.DateTimeFormat('en-US', {
732
+ dateStyle: format,
733
+ }).format(new Date(date));
377
734
  });
378
735
  ```
379
736
 
@@ -381,10 +738,12 @@ export default helper(function formatDate([date, format = 'short']) {
381
738
 
382
739
  ```handlebars
383
740
  <span class='text-muted'>
384
- {{format-date @post.modules.created_date 'medium'}}
741
+ {{format-date @post.modules.created_date 'medium'}}
385
742
  </span>
386
743
  ```
387
744
 
745
+ ---
746
+
388
747
  ## Modifier System
389
748
 
390
749
  ### DOM Interaction Modifiers
@@ -397,76 +756,15 @@ import { modifier } from 'ember-modifier';
397
756
  import { Tooltip } from 'bootstrap';
398
757
 
399
758
  export default modifier((element, [content]) => {
400
- const tooltip = new bootstrap.Tooltip(element, {
401
- title: content,
402
- });
403
-
404
- return () => tooltip.dispose();
405
- });
406
- ```
407
-
408
- ## Ember.js Reference Guide
409
-
410
- ### EmberData Patterns
411
-
412
- Smart use of EmberData can significantly reduce size of the codebase. Make sure you take advantage of that.
413
-
414
- 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`.
415
-
416
- **Universal Default Module:**
417
- All objects include: `"content_privacy": "string | public, private, pending, draft"`
418
-
419
- **Single Record Operations:**
759
+ const tooltip = new Tooltip(element, {
760
+ title: content,
761
+ });
420
762
 
421
- ```javascript
422
- // Find by ID or slug
423
- this.store.findRecord('track', 30);
424
- this.store.findRecord('track', 'some-slug-here');
425
-
426
- // Access without network request (if already in store)
427
- let post = this.store.peekRecord('post', 1);
428
-
429
- // Usage pattern
430
- this.store.findRecord('post', 1).then((post) => {
431
- // Access: post.id, post.slug, post.modules.<field_name>
763
+ return () => tooltip.dispose();
432
764
  });
433
765
  ```
434
766
 
435
- **Multiple Records:**
436
-
437
- ```javascript
438
- this.store
439
- .query('person', {
440
- modules: { name: 'Peter', location: 'delhi' }, //results with AND
441
- /*
442
- filter: { name: 'Peter', location: 'delhi' } //results with OR
443
- sort: "location,-age,name", //minus for descending order of that field, default is -id
444
- page: { offset:0, limit:-1 }, //for pagination or smart uses, -1 means everything
445
- ignore_ids: [10,14] //excludes these IDs from results
446
- show_public_objects_only: false, //default = true, if set false results include content_privacy = drafts, private or pending
447
- */
448
- })
449
- .then((results) => {
450
- // Process results
451
- });
452
- ```
453
-
454
- Prefer using backend (this.store.query) for search, filter and sort, over front-end JS functions to achieve the same thing. Avoid using this.store.findAll altogether, use this.store.query with page: { offset:0, limit:-1 } instead.
455
-
456
- **CRUD Operations:**
457
-
458
- ```javascript
459
- // Update
460
- let post = await this.store.findRecord('post', 1);
461
- post.modules.title = 'A new title';
462
- await post.save(); // => PATCH request
463
-
464
- // Delete
465
- let post = this.store.peekRecord('post', 2);
466
- post.destroyRecord(); // => DELETE request
467
- ```
468
-
469
- ### Helpers
767
+ ## Writing Helpers
470
768
 
471
769
  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.
472
770
 
@@ -479,8 +777,8 @@ Helper functions are JavaScript functions callable from Ember templates that per
479
777
  ```javascript
480
778
  // app/components/user-card.js
481
779
  export default class UserCard extends Component {
482
- formatName = (firstName, lastName) =>
483
- `${firstName} ${lastName}`.toUpperCase();
780
+ formatName = (firstName, lastName) =>
781
+ `${firstName} ${lastName}`.toUpperCase();
484
782
  }
485
783
  ```
486
784
 
@@ -529,8 +827,8 @@ Ember components should be thought of as templates that re-execute from scratch
529
827
 
530
828
  ```handlebars
531
829
  <article title='{{@article.modules.title}}'>
532
- <header><h1>{{@article.modules.title}}</h1></header>
533
- <section>{{@article.modules.body}}</section>
830
+ <header><h1>{{@article.modules.title}}</h1></header>
831
+ <section>{{@article.modules.body}}</section>
534
832
  </article>
535
833
  ```
536
834
 
@@ -541,7 +839,7 @@ Ember components should be thought of as templates that re-execute from scratch
541
839
 
542
840
  ```handlebars
543
841
  <div class={{if @user.modules.is_admin 'superuser' 'standard'}}>
544
- Welcome to my app.
842
+ Welcome to my app.
545
843
  </div>
546
844
  ```
547
845
 
@@ -558,12 +856,12 @@ import { action } from '@ember/object';
558
856
  import { tracked } from '@glimmer/tracking';
559
857
 
560
858
  export default class CounterComponent extends Component {
561
- @tracked count = 0;
859
+ @tracked count = 0;
562
860
 
563
- @action
564
- increment() {
565
- this.count++;
566
- }
861
+ @action
862
+ increment() {
863
+ this.count++;
864
+ }
567
865
  }
568
866
  ```
569
867
 
@@ -598,11 +896,11 @@ play() {
598
896
  import { modifier } from 'ember-modifier';
599
897
 
600
898
  export default modifier((element, [isPlaying]) => {
601
- if (isPlaying) {
602
- element.play();
603
- } else {
604
- element.pause();
605
- }
899
+ if (isPlaying) {
900
+ element.play();
901
+ } else {
902
+ element.pause();
903
+ }
606
904
  });
607
905
  ```
608
906
 
@@ -627,19 +925,13 @@ import { TrackedArray } from 'tracked-built-ins';
627
925
  import Service from '@ember/service';
628
926
 
629
927
  export default class ShoppingCartService extends Service {
630
- items = new TrackedArray([]);
928
+ items = new TrackedArray([]);
631
929
 
632
- add(item) {
633
- this.items.push(item);
634
- }
930
+ add(item) { this.items.push(item) }
635
931
 
636
- remove(item) {
637
- this.items.splice(this.items.indexOf(item), 1);
638
- }
932
+ remove(item) { this.items.splice(this.items.indexOf(item), 1) }
639
933
 
640
- empty() {
641
- this.items.splice(0, this.items.length);
642
- }
934
+ empty() { this.items.splice(0, this.items.length) }
643
935
  }
644
936
  ```
645
937
 
@@ -650,16 +942,12 @@ import Component from '@glimmer/component';
650
942
  import { service } from '@ember/service';
651
943
 
652
944
  export default class CartContentsComponent extends Component {
653
- // Loads service from: app/services/shopping-cart.js
654
- @service shoppingCart;
945
+ // Loads service from: app/services/shopping-cart.js
946
+ @service shoppingCart;
655
947
  }
656
948
  ```
657
949
 
658
- **Usage Guidelines:**
659
-
660
- - Use for application-wide state management
661
- - Share functionality across multiple routes/components
662
- - Maintain data that survives route transitions
950
+ ---
663
951
 
664
952
  ## Code Generation Process
665
953
 
@@ -693,113 +981,21 @@ async uploadFile(file) {
693
981
  }
694
982
  ```
695
983
 
696
- ### Installation Commands
697
-
698
- ```bash
699
- # Write all installer.sh commands
700
- ember g route files
701
- ember g controller files
702
- ember g component file-card -gc
703
- ember g helper format-date
704
- ember g modifier tooltip
705
- ember g service visualization-builder
706
- ```
707
-
708
- ### Pre-approval Process
709
-
710
- Before code generation, ember-tribe requests approval for additional packages:
711
-
712
- ```bash
713
- # Example approval request
714
- npm i chart.js
715
- npm i lodash
716
- ember install ember-table
717
- ```
718
-
719
- ## Application Structure
720
-
721
- ### Application Template
722
-
723
- ```handlebars
724
- {{page-title 'Your Application Name'}}
725
- {{outlet}}
726
- <BasicDropdownWormhole />
727
- ```
984
+ ---
728
985
 
729
986
  ## Best Practices
730
987
 
731
- ### Controller Minimization
732
-
733
- - Keep controllers minimal - prefer component logic
734
- - Use controllers only for query params and route-level actions
735
- - Move business logic to services
736
-
737
- ### Animation Guidelines
738
-
739
- - Use animate.css for enhanced UX
740
- - Prefer subtle animations (fadeIn, slideIn)
741
- - Avoid overwhelming users with excessive animation
742
-
743
- ### Data Flow
744
-
745
- 1. **Routes**: Fetch and prepare data
746
- 2. **Components**: Display and interact with data
747
- 3. **Services**: Handle business logic and state
748
- 4. **Helpers**: Transform data for display
749
-
750
- ### Performance Considerations
751
-
752
- - Leverage EmberData caching with `peekRecord`
753
- - Use backend filtering over frontend array manipulation
754
- - Implement pagination for large datasets
755
- - Minimize controller file count
756
-
757
- ## Integration Examples
988
+ 1. **Module Access**: Remember to use `modules.field_name` for backend fields
989
+ 2. **npm packages vs ember addons**: Prioritize npm packages over ember addons for better compatibility, when both are offering similar functionality
990
+ 3. **Minimal Controllers**: Logic should ideally reside in components and services
991
+ 4. **Bootstrap 5.x Foundation**: Responsive, accessible design system
992
+ 5. **Use FontAwesome 6.x**: Comprehensive icon library
993
+ 6. **Animations**: If required, use animate.css for enhanced user experience. Prefer subtle animations (fadeIn, slideIn). Avoid overwhelming users with excessive animation
994
+ 7. **Access Cache**: Leverage EmberData caching with `peekRecord`
995
+ 8. **Avoid array manipulations of backend data**: Use backend filtering over frontend array manipulation
758
996
 
759
- ### With Junction CMS
760
-
761
- ```javascript
762
- // Automatic sync with Junction tracks
763
- async beforeModel() {
764
- await this.types.fetchAgain(); // Syncs with backend types
765
- }
766
- ```
767
-
768
- ### With External APIs
769
-
770
- ```javascript
771
- // Service for external integrations
772
- @service externalApi;
773
-
774
- async model() {
775
- const localData = await this.store.query('post', {});
776
- const externalData = await this.externalApi.fetch('/posts');
777
- return { local: localData, external: externalData };
778
- }
779
- ```
780
-
781
- ## Troubleshooting
782
-
783
- ### Common Issues
784
-
785
- 1. **Model Not Found**: Ensure `types.fetchAgain()` completes in application route
786
- 2. **Module Access**: Remember to use `modules.field_name` for backend fields
787
- 3. **Bootstrap Components**: Initialize through modifier or service
788
- 4. **Animation Conflicts**: Check animate.css class conflicts
789
-
790
- ### Debug Commands
791
-
792
- ```javascript
793
- // Check loaded models
794
- this.store.peekAll('your-model-name');
795
-
796
- // Verify service registration
797
- this.owner.lookup('service:your-service');
798
-
799
- // Route debugging
800
- console.log(this.router.currentRouteName);
801
- ```
997
+ ---
802
998
 
803
999
  # License
804
1000
 
805
- This project is licensed under the [GNU GPL v3 License](LICENSE.md).
1001
+ This project is licensed under the [GNU GPL v3 License](LICENSE.md).
@@ -0,0 +1,11 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class WelcomeFlameComponent extends Component {
4
+ <template>
5
+ <section class="flame-bg d-flex align-items-center justify-content-center">
6
+ <div class="py-6 container px-0 text-center text-dark">
7
+ <img src="/assets/img/flame.png" width="200">
8
+ </div>
9
+ </section>
10
+ </template>
11
+ }
@@ -0,0 +1,6 @@
1
+ import { pageTitle } from 'ember-page-title';
2
+
3
+ <template>
4
+ {{pageTitle "<%= classifiedPackageName %>"}}
5
+ {{outlet}}
6
+ </template>
@@ -0,0 +1,11 @@
1
+ import WelcomeFlame from '../components/welcome-flame';
2
+
3
+ <template>
4
+ {{! Remove the WelcomeFlame component and write your HTML code here }}
5
+
6
+ <WelcomeFlame />
7
+
8
+ {{! / }}
9
+
10
+ {{outlet}}
11
+ </template>
@@ -17,20 +17,19 @@ module.exports = {
17
17
  { name: 'ember-math-helpers' },
18
18
  { name: 'ember-cli-string-helpers' },
19
19
  { name: 'ember-promise-helpers' },
20
- { name: '@ember/test-waiters', target: '^3.1.0' },
20
+ { name: '@ember/test-waiters' },
21
21
  { name: 'ember-tag-input' },
22
22
  { name: 'ember-file-upload' },
23
23
  { name: 'ember-toggle' },
24
- { name: 'ember-basic-dropdown' },
25
- { name: 'ember-power-select' },
24
+ { name: 'ember-concurrency' },
26
25
  { name: 'ember-click-outside' },
27
26
  { name: 'ember-web-app' },
28
27
  { name: 'tracked-built-ins' },
29
28
  { name: 'ember-keyboard' },
30
29
  { name: 'ember-router-scroll' },
31
- { name: 'ember-concurrency' },
32
30
  { name: 'ember-table' },
33
31
  { name: 'ember-animated' },
32
+ { name: 'ember-power-select' },
34
33
  ],
35
34
  }).then(() => {
36
35
  return this.addPackagesToProject([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-tribe",
3
- "version": "2.5.2",
3
+ "version": "2.5.3",
4
4
  "description": "The default blueprint for using Tribe API and Junction within EmberJS.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -1,5 +0,0 @@
1
- <section class="flame-bg d-flex align-items-center justify-content-center">
2
- <div class="py-6 container px-0 text-center text-dark">
3
- <img src="/assets/img/flame.png" width="200">
4
- </div>
5
- </section>
@@ -1,2 +0,0 @@
1
- {{page-title "<%= classifiedPackageName %>"}}
2
- {{outlet}}
@@ -1,7 +0,0 @@
1
- {{!-- Remove the WelcomeFlame component and write your HTML code here --}}
2
-
3
- <WelcomeFlame />
4
-
5
- {{!-- / --}}
6
-
7
- {{outlet}}