ember-tribe 2.4.8 → 2.4.11

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,55 +1,805 @@
1
- ember-tribe
2
- ==============================================================================
1
+ # ember-tribe
3
2
 
4
3
  An addon that connects EmberJS to Tribe API.
5
- Tribe is a project management framework by Postcode - https://github.com/tribe-framework/tribe
4
+ Tribe is a collaborative project management framework - https://github.com/tribe-framework/tribe
6
5
 
6
+ https://junction.express provides an interface to build a Tribe compatibale no-code backend in minutes.
7
7
 
8
- Compatibility
9
- ------------------------------------------------------------------------------
8
+ ## Compatibility
10
9
 
11
- * Ember.js v5.4 or above
12
- * Ember CLI v5.4 or above
10
+ - Ember.js v6.0 or above
11
+ - Ember CLI v6.0 or above
13
12
 
14
-
15
- Installation
16
- ------------------------------------------------------------------------------
13
+ ## Installation
17
14
 
18
15
  1. Install
16
+
19
17
  ```
20
18
  ember install ember-tribe
21
19
  ```
22
20
 
23
21
  2. Configure
24
- - Enter TRIBE_API_URL in .env file in Ember directory.
25
22
 
26
- 3. Sync Tribe's types.json with Ember Models
23
+ - Enter TRIBE_API_URL and TRIBE_API_KEY in .env file, copy of .env.sample
24
+
25
+ # Ember-Tribe Documentation
26
+
27
+ ## Overview
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.
30
+
31
+ ## Purpose
32
+
33
+ Ember-tribe enables rapid application development by:
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
39
+
40
+ ## Installation and Setup
41
+
42
+ ### Prerequisites
43
+
44
+ - Ember.js 6.x
45
+ - Node.js (latest LTS)
46
+ - Junction backend
47
+
48
+ ### Installation
49
+
50
+ ```bash
51
+ ember install ember-tribe
52
+ ```
53
+
54
+ The addon automatically configures following essential packages:
55
+
56
+ **Ember Addons:**
57
+
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
67
+
68
+ **NPM Packages:**
69
+
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
76
+
77
+ ## Core Architecture
78
+
79
+ ### Think in this order
80
+
81
+ ember-tribe follows a specific order that includes our learnings and Ember.js best practices, and must be followed exactly:
82
+
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
88
+
89
+ This order ensures proper dependency resolution and optimal code organization.
90
+
91
+ ### Package Philosophy
92
+
93
+ **Try using npm packages over ember addons because ember addons are sometimes outdated.** Ember-tribe prioritizes npm packages for better compatibility and maintenance.
94
+
95
+ ### File Structure
96
+
97
+ ```
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
109
+ ```
110
+
111
+ ## Required File Outputs
112
+
113
+ Make separate, complete code files for each category:
114
+
115
+ ### installer.sh
116
+
117
+ ```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>;
124
+ ```
125
+
126
+ ### app/styles/app.scss
127
+
128
+ ```scss
129
+ @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
+
131
+ $font-family-sans-serif: 'IBM Plex Mono', serif !default;
132
+ $display-font-family: 'IBM Plex Mono', serif !default;
133
+
134
+ $primary: #000000 !default;
135
+ $secondary: #cccccc !default;
136
+ $success: #00ff00 !default;
137
+ $info: #0000ff !default;
138
+ $warning: #ffff00 !default;
139
+ $danger: #ff0000 !default;
140
+ $light: #eeeeee !default;
141
+ $dark: #333333 !default;
142
+
143
+ $enable-rounded: false !default;
144
+ $enable-negative-margins: true !default;
145
+ $enable-cssgrid: true !default;
146
+
147
+ $spacer: 1rem !default;
148
+ $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,
160
+ ) !default;
161
+
162
+ @import 'node_modules/bootstrap/scss/bootstrap';
163
+ @import 'node_modules/animate.css/animate';
164
+ ```
165
+
166
+ ### app/templates/application.hbs
167
+
168
+ ```handlebars
169
+ {{page-title 'Your Application Name'}}
170
+ {{outlet}}
171
+ <BasicDropdownWormhole />
172
+ ```
173
+
174
+ **Application.hbs Extension Guidelines:**
175
+
176
+ - Extend when adding global navigation components
177
+ - Include shared modals or dropdowns
178
+ - Add application-wide notification systems
179
+ - Insert global loading states or overlays
180
+
181
+ ## EmberData Integration
182
+
183
+ ### Automatic Model Generation
184
+
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
+ ```
198
+
199
+ ### Data Access Patterns
200
+
201
+ All backend data uses the `modules` key for field access:
202
+
203
+ ```javascript
204
+ // Accessing track data
205
+ let post = await this.store.findRecord('post', 1);
206
+ console.log(post.id); // Direct property
207
+ console.log(post.slug); // Direct property
208
+ console.log(post.modules.title); // Field access
209
+ console.log(post.modules.content_privacy); // Universal field
210
+ ```
211
+
212
+ ### Query Operations
213
+
214
+ ```javascript
215
+ // Complex queries
216
+ 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
222
+ });
223
+ ```
224
+
225
+ ## Styling System
226
+
227
+ ### Default Configuration (app.scss)
228
+
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;
233
+
234
+ // Color Palette
235
+ $primary: #000000 !default;
236
+ $secondary: #cccccc !default;
237
+ $success: #00ff00 !default;
238
+ // ... additional colors
239
+
240
+ // Bootstrap Configuration
241
+ $enable-rounded: false !default;
242
+ $enable-negative-margins: true !default;
243
+ $enable-cssgrid: true !default;
244
+
245
+ @import 'node_modules/bootstrap/scss/bootstrap';
246
+ @import 'node_modules/animate.css/animate';
247
+ ```
248
+
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
255
+
256
+ ## Component Architecture
257
+
258
+ ### Component Types
259
+
260
+ Based on storylang.json component definitions:
261
+
262
+ **Layout Components:**
263
+
264
+ - `table`, `figure`, `accordion`, `card`, `list-group`
265
+ - `navbar`, `nav`, `tab`, `breadcrumb`
266
+
267
+ **Interactive Components:**
268
+
269
+ - `button`, `button-group`, `dropdown`, `modal`
270
+ - `input-field`, `select`, `file-uploader`
271
+ - `pagination`, `popover`, `tooltip`
272
+
273
+ ### Component Structure
274
+
275
+ ```javascript
276
+ // Component class
277
+ import Component from '@glimmer/component';
278
+ import { tracked } from '@glimmer/tracking';
279
+ import { action } from '@ember/object';
280
+ import { service } from '@ember/service';
281
+
282
+ export default class FileCardComponent extends Component {
283
+ @service store;
284
+ @tracked isSelected = false;
285
+
286
+ @action
287
+ toggleSelection() {
288
+ this.isSelected = !this.isSelected;
289
+ }
290
+ }
291
+ ```
292
+
293
+ ### Template Patterns
294
+
295
+ ```handlebars
296
+ <div
297
+ class='card {{if this.isSelected "border-primary"}}'
298
+ {{on 'click' this.toggleSelection}}
299
+ >
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>
304
+ </div>
305
+ ```
306
+
307
+ ## Services Integration
308
+
309
+ ### Built-in Services
310
+
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
317
+
318
+ Make services based on storylang.json service definitions:
319
+
320
+ ```javascript
321
+ // app/services/visualization-builder.js
322
+ import Service from '@ember/service';
323
+ import { tracked } from '@glimmer/tracking';
324
+ import { service } from '@ember/service';
325
+
326
+ export default class VisualizationBuilderService extends Service {
327
+ @service store;
328
+ @tracked supportedTypes = ['network', 'tree', 'sankey'];
329
+
330
+ buildVisualization(files, type, config) {
331
+ // Service logic implementation
332
+ }
333
+ }
334
+ ```
335
+
336
+ ## Route Generation
337
+
338
+ ### Route Creation
339
+
340
+ Make routes based on storylang.json route definitions:
341
+
342
+ ```javascript
343
+ import Route from '@ember/routing/route';
344
+ import { service } from '@ember/service';
345
+
346
+ 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
+ }
360
+ }
361
+ ```
362
+
363
+ ## Helper System
364
+
365
+ ### Global Helpers
366
+
367
+ Make helpers based on storylang.json helper requirements:
368
+
369
+ ```javascript
370
+ // app/helpers/format-date.js
371
+ import { helper } from '@ember/component/helper';
372
+
373
+ export default helper(function formatDate([date, format = 'short']) {
374
+ return new Intl.DateTimeFormat('en-US', {
375
+ dateStyle: format,
376
+ }).format(new Date(date));
377
+ });
378
+ ```
379
+
380
+ ### Template Usage
381
+
382
+ ```handlebars
383
+ <span class='text-muted'>
384
+ {{format-date @post.modules.created_date 'medium'}}
385
+ </span>
386
+ ```
387
+
388
+ ## Modifier System
389
+
390
+ ### DOM Interaction Modifiers
391
+
392
+ Make modifiers for specific DOM manipulation needs:
393
+
394
+ ```javascript
395
+ // app/modifiers/tooltip.js
396
+ import { modifier } from 'ember-modifier';
397
+ import { Tooltip } from 'bootstrap';
398
+
399
+ 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:**
420
+
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>
432
+ });
433
+ ```
434
+
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
470
+
471
+ 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
+
473
+ **Local Helpers:**
474
+
475
+ - Defined as methods within component classes
476
+ - Scoped to specific component
477
+ - Called with `this.` prefix in templates
478
+
479
+ ```javascript
480
+ // app/components/user-card.js
481
+ export default class UserCard extends Component {
482
+ formatName = (firstName, lastName) =>
483
+ `${firstName} ${lastName}`.toUpperCase();
484
+ }
485
+ ```
486
+
487
+ ```handlebars
488
+ <!-- app/components/user-card.hbs -->
489
+ <h2>{{this.formatName @user.modules.first_name @user.modules.last_name}}</h2>
27
490
  ```
28
- php sync-types.php
491
+
492
+ **Global Helpers:**
493
+
494
+ - Defined in `app/helpers/` folder as separate files
495
+ - Available across all application templates
496
+ - Called directly by function name
497
+
498
+ ```javascript
499
+ // app/helpers/format-currency.js
500
+ export default function formatCurrency(amount, currency = 'USD') {
501
+ return new Intl.NumberFormat('en-US', {
502
+ style: 'currency',
503
+ currency: currency,
504
+ }).format(amount);
505
+ }
29
506
  ```
30
- 4. To serve front-end webapp from backend server and for using page-wise meta tags
31
- - On local:
507
+
508
+ ```handlebars
509
+ <span>{{format-currency @item.modules.price 'EUR'}}</span>
32
510
  ```
33
- php sync-dist.php
511
+
512
+ **Helper Features:**
513
+
514
+ - Support positional arguments: `{{helper arg1 arg2}}`
515
+ - Support named arguments: `{{helper arg1 key=value}}`
516
+ - Can be nested: `{{outer-helper (inner-helper @value)}}`
517
+ - Built-in helpers available: `{{get}}`, `{{concat}}`, `{{let}}`, `{{array}}`, `{{hash}}`
518
+
519
+ **Usage Guidelines:**
520
+
521
+ - Local: Component-specific logic, simple transformations
522
+ - Global: Reusable functionality across multiple components (formatting, calculations)
523
+
524
+ ### Component Architecture & Principle of Substitution
525
+
526
+ 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.
527
+
528
+ **Template Patterns:**
529
+
530
+ ```handlebars
531
+ <article title='{{@article.modules.title}}'>
532
+ <header><h1>{{@article.modules.title}}</h1></header>
533
+ <section>{{@article.modules.body}}</section>
534
+ </article>
34
535
  ```
35
- - On server:
536
+
537
+ **Dynamic Updates:**
538
+
539
+ - Text and Attributes: Use `{{}}` syntax for automatic DOM updates
540
+ - Conditional Logic: Use helpers for conditional attributes
541
+
542
+ ```handlebars
543
+ <div class={{if @user.modules.is_admin 'superuser' 'standard'}}>
544
+ Welcome to my app.
545
+ </div>
36
546
  ```
37
- bash -c "$(wget --no-cache --no-cookie https://raw.githubusercontent.com/tribe-framework/tribe/master/install/ember.sh -O -)"
547
+
548
+ **Event Handling:**
549
+ Use `{{on}}` element modifier for event handlers:
550
+
551
+ ```handlebars
552
+ <button type='button' {{on 'click' this.increment}}>+</button>
553
+ ```
554
+
555
+ ```javascript
556
+ import Component from '@glimmer/component';
557
+ import { action } from '@ember/object';
558
+ import { tracked } from '@glimmer/tracking';
559
+
560
+ export default class CounterComponent extends Component {
561
+ @tracked count = 0;
562
+
563
+ @action
564
+ increment() {
565
+ this.count++;
566
+ }
567
+ }
568
+ ```
569
+
570
+ ### Component Communication & Modifiers
571
+
572
+ **Design Pattern:**
573
+
574
+ 1. Component manages state
575
+ 2. Modifiers handle DOM interactions
576
+ 3. Separation enables better reusability and testing
577
+
578
+ **Complex Interaction Example:**
579
+
580
+ ```handlebars
581
+ <audio src={{@song.modules.src_url}} {{play-when this.isPlaying}} />
582
+ <button type='button' {{on 'click' this.play}}>Play</button>
583
+ <button type='button' {{on 'click' this.pause}}>Pause</button>
38
584
  ```
39
585
 
40
- Usage
41
- ------------------------------------------------------------------------------
586
+ ```javascript
587
+ // Component manages state
588
+ @tracked isPlaying = false;
42
589
 
43
- For more info visit https://postcodesolutions.com
590
+ @action
591
+ play() {
592
+ this.isPlaying = true;
593
+ }
594
+ ```
44
595
 
596
+ ```javascript
597
+ // Modifier handles DOM interaction
598
+ import { modifier } from 'ember-modifier';
45
599
 
46
- Contributing
47
- ------------------------------------------------------------------------------
600
+ export default modifier((element, [isPlaying]) => {
601
+ if (isPlaying) {
602
+ element.play();
603
+ } else {
604
+ element.pause();
605
+ }
606
+ });
607
+ ```
48
608
 
49
- See the [Contributing](CONTRIBUTING.md) guide for details.
609
+ **Modifier Forwarding:**
610
+ Modifiers applied to components pass through via `...attributes`:
50
611
 
612
+ ```handlebars
613
+ <Tooltip {{custom-modifier}} />
614
+ <!-- Forwards to: -->
615
+ <div ...attributes>...</div>
616
+ ```
617
+
618
+ ### Services
619
+
620
+ Services are Ember objects that persist for the entire application duration, providing shared state or persistent connections across different parts of your app.
621
+
622
+ **Service Definition:**
623
+
624
+ ```javascript
625
+ // app/services/shopping-cart.js
626
+ import { TrackedArray } from 'tracked-built-ins';
627
+ import Service from '@ember/service';
628
+
629
+ export default class ShoppingCartService extends Service {
630
+ items = new TrackedArray([]);
631
+
632
+ add(item) {
633
+ this.items.push(item);
634
+ }
635
+
636
+ remove(item) {
637
+ this.items.splice(this.items.indexOf(item), 1);
638
+ }
639
+
640
+ empty() {
641
+ this.items.splice(0, this.items.length);
642
+ }
643
+ }
644
+ ```
645
+
646
+ **Service Access:**
647
+
648
+ ```javascript
649
+ import Component from '@glimmer/component';
650
+ import { service } from '@ember/service';
651
+
652
+ export default class CartContentsComponent extends Component {
653
+ // Loads service from: app/services/shopping-cart.js
654
+ @service shoppingCart;
655
+ }
656
+ ```
657
+
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
663
+
664
+ ## Code Generation Process
665
+
666
+ ### File Upload Javascript Example
667
+
668
+ ```javascript
669
+ import ENV from '<your-application-name>/config/environment';
670
+
671
+ @action
672
+ async uploadFile(file) {
673
+ try {
674
+ const response = await file.upload(ENV.TribeENV.API_URL + '/uploads.php');
675
+ response.json().then(async (data) => {
676
+ if (data.status == 'success') {
677
+ //data.file.name
678
+ //data.file.mime
679
+ //data.file.url
680
+ //if mime type is an image, following converted sizes are also available
681
+ //data.file.xl.url
682
+ //data.file.lg.url
683
+ //data.file.md.url
684
+ //data.file.sm.url
685
+ //data.file.xs.url
686
+ } else if (data.status == 'error') {
687
+ alert(data.error_message);
688
+ }
689
+ });
690
+ } catch (error) {
691
+ file.state = 'aborted';
692
+ }
693
+ }
694
+ ```
695
+
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
+ ```
728
+
729
+ ## Best Practices
730
+
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
758
+
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
+ ```
51
802
 
52
- License
53
- ------------------------------------------------------------------------------
803
+ # License
54
804
 
55
- This project is licensed under the [MIT License](LICENSE.md).
805
+ This project is licensed under the [GNU GPL v3 License](LICENSE.md).