@currentjs/gen 0.5.1 → 0.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.
@@ -1,765 +1,21 @@
1
- # CurrentJS Framework Rules
1
+ 0. This is fullstack application, generated by CurrentJS code generator. Instructions are in REFERENCE.md.
2
2
 
3
- ## Architecture Overview
4
- This is a CurrentJS framework application using clean architecture principles with the following layers:
5
- - **Controllers**: Handle HTTP requests/responses and route handling
6
- - **UseCases**: Orchestrate handler calls in sequence
7
- - **Services**: Contain business logic and orchestrate operations
8
- - **Stores**: Provide data access layer and database operations
9
- - **Domain Entities**: Core business models
10
- - **Views**: HTML templates for server-side rendering
3
+ 1. Yaml-first: edit yaml, `current generate`, `current commit` — in that order. `current generate` overwrites TypeScript files. Any hand-edits to generated files must be preserved with current commit before the next generation, or they are lost. This is the single most critical process rule.
11
4
 
12
- All layers (except Domain and Views) are auto-wired via **dependency injection** the generator scans `@Injectable` and `@Controller` decorators, resolves constructor dependencies, and generates wiring code in `src/app.ts`.
5
+ 2. Never edit the DI wiring block in src/app.ts. The block between // currentjs:controllers:start and // currentjs:controllers:end is fully auto-regenerated. Changes there will silently vanish. Don't touch app.yaml either.
13
6
 
14
- ## Commands
7
+ 3. Custom logic belongs exclusively in Models and Service methods. Controllers, UseCases, and DTOs are fully generated. When CRUD isn't enough, add a method to the *Service.ts class, then reference it in the yaml as a handler — never implement business logic in controllers.
15
8
 
16
- ```bash
17
- current create module Modulename # Creates "Modulename" with default structure and yaml file
18
- ```
19
- ```bash
20
- current generate Modulename # Generates all TypeScript files based on the module's yaml, and runs "npm run build"
21
- current generate # Does the same as above, but for all modules
22
- ```
23
- ```bash
24
- current commit [files...] # Commits all changes in the code, so they won't be overwritten after regeneration
25
- current diff [module] # Show differences between generated and current code
26
- ```
9
+ 4. Custom handlers must be declared in the yaml before (re)generating. After writing a service method, add it to useCases.Model.action.handlers in the yaml. If you skip this step, the use case won't call your method and the wiring won't be regenerated correctly.
27
10
 
28
- ## The flow
29
- 1. Create an empty app (`current create app`) – this step is already done.
30
- 2. Create a new module: `current create module Name`
31
- 3. In the module's yaml file, define module's:
32
- - domain (aggregates and value objects)
33
- - useCases (with input/output and handlers)
34
- - api endpoints (with auth)
35
- - web pages (with auth and onSuccess/onError)
36
- 4. Generate TypeScript files: `current generate Name`
37
- 5. If required, make changes in the:
38
- - model (i.e. some specific business rules or validations)
39
- - views
40
- 6. Commit those changes: `current commit`
11
+ 5. Never include auto-managed fields in the domain definition. id, owner_id, created_at, updated_at, deleted_at are added automatically. Defining them manually causes schema conflicts and duplicate columns.
41
12
 
42
- --- If needed more than CRUD: ---
13
+ 6. Model relationships use the model name as the field type, not a raw foreign key. owner: { type: Owner, required: true } — not ownerId: { type: id }. The generator derives the FK column, DTO field, and store JOIN automatically from the model-name type.
43
14
 
44
- 7. Define action in the service by creating a method
45
- 8. Describe this action in the module's yaml under useCases. Additionally, you may define api/web endpoints.
46
- 9. `current generate Modulename`
47
- 10. commit changes: `current commit`
15
+ 7. Business rule validation belongs in domain entities, not in services. Services orchestrate; entities own domain invariants. A rule like "a published post cannot be archived" is an entity concern. Type/presence validations are handled by DTOs.
48
16
 
49
- ## Configuration Files
17
+ 8. Auth and permissions are yaml configuration, not code. auth: [owner, admin] on an endpoint is enough. Do not write auth guards or role checks manually in controllers — it's redundant and won't survive regeneration.
50
18
 
51
- ### Application Configuration (app.yaml)
19
+ 9. The template engine is custom: x-for/x-if attributes and {{ }} syntax. It is not Alpine, Vue, or Jinja. Loops use <tbody x-for="items" x-row="item">, conditionals use x-if="value", interpolation uses {{ item.name }}. Don't import or use other templating conventions.
52
20
 
53
- **Do not modify this file**
54
-
55
- ### Module Configuration (modulename.yaml)
56
-
57
- **The most work must be done in these files**
58
-
59
- **Complete Module Example:**
60
- ```yaml
61
- domain:
62
- aggregates:
63
- Post:
64
- root: true # Marks as aggregate root
65
- fields:
66
- title: { type: string, required: true }
67
- content: { type: string, required: true }
68
- authorId: { type: id, required: true }
69
- publishedAt: { type: datetime }
70
- status: { type: string, required: true }
71
-
72
- useCases:
73
- Post:
74
- list:
75
- input:
76
- pagination: { type: offset, defaults: { limit: 20, maxLimit: 100 } }
77
- output: { from: Post, pagination: true }
78
- handlers: [default:list] # Built-in list handler
79
- get:
80
- input: { identifier: id }
81
- output: { from: Post }
82
- handlers: [default:get] # Built-in get handler
83
- create:
84
- input: { from: Post }
85
- output: { from: Post }
86
- handlers: [default:create] # Built-in create
87
- update:
88
- input: { identifier: id, from: Post, partial: true }
89
- output: { from: Post }
90
- handlers: [default:update]
91
- delete:
92
- input: { identifier: id }
93
- output: void
94
- handlers: [ # Chain multiple handlers
95
- checkCanDelete, # Custom → PostService.checkCanDelete(result, input)
96
- default:delete # Built-in delete
97
- ]
98
- publish: # Custom action
99
- input: { identifier: id }
100
- output: { from: Post }
101
- handlers: [
102
- default:get, # Fetch entity
103
- validateForPublish, # Custom → PostService.validateForPublish(result, input)
104
- updatePublishStatus # Custom → PostService.updatePublishStatus(result, input)
105
- ]
106
-
107
- api: # REST API configuration
108
- Post: # Keyed by model name
109
- prefix: /api/posts # Base URL for API endpoints
110
- endpoints:
111
- - method: GET # HTTP method
112
- path: / # Relative path (becomes /api/posts/)
113
- useCase: Post:list # References useCases.Post.list
114
- auth: all # Public access
115
- - method: GET
116
- path: /:id # Path parameter
117
- useCase: Post:get
118
- auth: all
119
- - method: POST
120
- path: /
121
- useCase: Post:create
122
- auth: authenticated # Must be logged in
123
- - method: PUT
124
- path: /:id
125
- useCase: Post:update
126
- auth: [owner, admin] # Owner OR admin (OR logic)
127
- - method: DELETE
128
- path: /:id
129
- useCase: Post:delete
130
- auth: [owner, admin]
131
- - method: POST # Custom endpoint
132
- path: /:id/publish
133
- useCase: Post:publish
134
- auth: [owner, editor, admin]
135
-
136
- web: # Web interface configuration
137
- Post: # Keyed by model name
138
- prefix: /posts # Base URL for web pages
139
- layout: main_view # Layout template name
140
- pages:
141
- - path: / # List page
142
- useCase: Post:list
143
- view: postList # Template name
144
- auth: all
145
- - path: /:id # Detail page
146
- useCase: Post:get
147
- view: postDetail
148
- auth: all
149
- - path: /create # Create form (GET = show form)
150
- method: GET
151
- view: postCreate
152
- auth: authenticated
153
- - path: /create # Create form (POST = submit)
154
- method: POST
155
- useCase: Post:create
156
- auth: authenticated
157
- onSuccess: # What happens after successful submission
158
- redirect: /posts/:id
159
- toast: "Post created successfully"
160
- onError:
161
- stay: true
162
- toast: error
163
- - path: /:id/edit # Edit form (GET = show form)
164
- method: GET
165
- useCase: Post:get # Load existing data
166
- view: postUpdate
167
- auth: [owner, admin]
168
- - path: /:id/edit # Edit form (POST = submit)
169
- method: POST
170
- useCase: Post:update
171
- auth: [owner, admin]
172
- onSuccess:
173
- back: true
174
- toast: "Post updated successfully"
175
- ```
176
-
177
- **Make sure no `id`/`owner_id`/`created_at`/`updated_at`/`deleted_at` fields are present in the domain definition, since they are added automatically**
178
-
179
- **Use case option: withChild (aggregate root with child entities)**
180
-
181
- Per use case you can set `withChild: true` (default `false`) to show child entities on the root's pages. Only applies when the entity has child entities (e.g. Invoice with InvoiceItem). Ignored if there are no child entities.
182
- - **list** + `withChild: true`: adds a link column on the list page to the child entity list (no extra loading).
183
- - **get** + `withChild: true`: on the detail page, renders a table of child entities below the main card, with View/Edit/Add links.
184
-
185
- Example:
186
- ```yaml
187
- useCases:
188
- Invoice:
189
- get:
190
- withChild: true
191
- input: { identifier: id }
192
- output: { from: Invoice }
193
- handlers: [default:get]
194
- ```
195
-
196
- **Field Types:**
197
- - `string` - Text data (VARCHAR in database)
198
- - `number` - Numeric data (INT/DECIMAL in database)
199
- - `integer` - Integer data (INT in database)
200
- - `decimal` - Decimal data (DECIMAL in database)
201
- - `boolean` - True/false values (BOOLEAN in database)
202
- - `datetime` - Date and time values (DATETIME in database)
203
- - `date` - Date values (DATE in database)
204
- - `id` - Foreign key reference (INT in database)
205
- - `json` - JSON data
206
- - `enum` - Enumerated values (use with `values: [...]`)
207
- - `ModelName` - Relationship to another model (e.g., `Owner`, `User`, `Post`)
208
-
209
- **Multi-Model Configuration:**
210
-
211
- When working with multiple models in a single module, each model is a separate key in `api` and `web`:
212
-
213
- ```yaml
214
- domain:
215
- aggregates:
216
- Cat:
217
- root: true
218
- fields:
219
- name: { type: string, required: true }
220
- Person:
221
- root: true
222
- fields:
223
- name: { type: string, required: true }
224
- email: { type: string, required: true }
225
-
226
- api:
227
- Cat:
228
- prefix: /api/cat
229
- endpoints:
230
- - method: GET
231
- path: /
232
- useCase: Cat:list
233
- auth: all
234
- Person:
235
- prefix: /api/person
236
- endpoints:
237
- - method: GET
238
- path: /
239
- useCase: Person:list
240
- auth: all
241
-
242
- web:
243
- Cat:
244
- prefix: /cat
245
- layout: main_view
246
- pages:
247
- - path: /
248
- useCase: Cat:list
249
- view: catList
250
- auth: all
251
- Person:
252
- prefix: /person
253
- layout: main_view
254
- pages:
255
- - path: /
256
- useCase: Person:list
257
- view: personList
258
- auth: all
259
- ```
260
-
261
- **Model Relationships:**
262
-
263
- Define relationships by using another model's name as the field type in `domain.aggregates`:
264
-
265
- ```yaml
266
- domain:
267
- aggregates:
268
- Owner:
269
- root: true
270
- fields:
271
- name: { type: string, required: true }
272
-
273
- Cat:
274
- root: true
275
- fields:
276
- name: { type: string, required: true }
277
- owner: { type: Owner, required: true } # Creates relationship with Owner model
278
- ```
279
-
280
- **Generated Behavior:**
281
- - **Domain Model**: Rich object with `owner: Owner` (full object, not just ID)
282
- - **DTOs**: Use `ownerId: number` for API requests
283
- - **Database**: Stores `ownerId` foreign key column (references `Owner.id`)
284
- - **Store**: Automatically loads the related Owner object when fetching a Cat
285
- - **HTML Forms**: Auto-generates select dropdown with "Create New" button
286
- - **TypeScript**: Full type safety with proper imports
287
-
288
- **Naming Convention:**
289
- Foreign keys are auto-generated following the pattern `fieldName + 'Id'`:
290
- - `owner` → `ownerId`
291
- - `author` → `authorId`
292
- - `parentComment` → `parentCommentId`
293
-
294
- The foreign key always references the `id` field of the related model.
295
-
296
- **🔄 Handler vs Use Case Architecture:**
297
- - **Handler**: Creates a separate service method (one handler = one service method)
298
- - **Use Case**: Defined under `useCases.ModelName.actionName`, calls handler methods step-by-step
299
- - **UseCase reference**: Used in `api`/`web` endpoints as `ModelName:actionName` (e.g., `Post:list`)
300
-
301
- **Built-in Handlers (used in `useCases.*.*.handlers`):**
302
- - `default:list` - Creates service method with pagination parameters
303
- - `default:get` - Creates service method named `get` with ID parameter
304
- - `default:create` - Creates service method with DTO parameter
305
- - `default:update` - Creates service method with ID and DTO parameters
306
- - `default:delete` - Creates service method with ID parameter
307
-
308
- Note: Handlers within `useCases` do NOT need a model prefix because the model is already the key.
309
-
310
- **Custom Handlers:**
311
- - `customMethodName` - Creates service method that accepts `(result, input)` parameters
312
- - `result`: Result from previous handler (or `null` if it's the first handler)
313
- - `input`: The parsed input DTO
314
- - Each handler generates a separate method in the service
315
- - User can customize the implementation after generation
316
-
317
- **🔗 Multiple Handlers per Use Case:**
318
- When a use case has multiple handlers, each handler generates a separate service method, and the use case orchestrator calls them sequentially. The use case returns the result from the last handler.
319
-
320
- **Parameter Passing Rules:**
321
- - **Default handlers** (`default:*`): Receive standard parameters (id, pagination, DTO, etc.)
322
- - **Custom handlers**: Receive `(result, input)` where:
323
- - `result`: Result from previous handler, or `null` if it's the first handler
324
- - `input`: Parsed input DTO
325
-
326
- **Handler Format Examples (inside `useCases`):**
327
- - `default:list` - Creates service method `list(page, limit)`
328
- - `default:get` - Creates service method `get(id)`
329
- - `validateContent` - Creates service method `validateContent(result, input)`
330
- - `notifySubscribers` - Creates service method `notifySubscribers(result, input)`
331
-
332
- **Form Success/Error Handling (in `web` pages):**
333
-
334
- Instead of a separate `strategy` field, use `onSuccess` and `onError` on web page endpoints:
335
- ```yaml
336
- onSuccess:
337
- redirect: /posts/:id # Redirect to URL
338
- toast: "Saved!" # Show toast notification
339
- back: true # Navigate back in browser history
340
- stay: true # Stay on current page
341
- onError:
342
- stay: true # Stay on current page
343
- toast: error # Show error toast
344
- ```
345
-
346
- The template generator converts `onSuccess` options into `data-strategy` attributes on HTML forms.
347
-
348
- **Auth Configuration (per endpoint in `api` and `web`):**
349
- - `all` - Anyone (including anonymous users)
350
- - `authenticated` - Any logged-in user
351
- - `owner` - User who created the entity (checks `ownerId` field)
352
- - `admin`, `editor`, `user` - Custom roles from JWT token
353
- - `[owner, admin]` - Array syntax: user must match ANY (OR logic). Privileged roles bypass ownership check.
354
-
355
- **Generated Files from Configuration:**
356
- - Domain entity class (one per model)
357
- - Use case orchestrator (one per model)
358
- - Service class with handler methods (one per model)
359
- - Input/Output DTOs (one per use case)
360
- - API controller with REST endpoints (one per model)
361
- - Web controller with page rendering (one per model)
362
- - Store class with database operations (one per model)
363
- - HTML templates for all views
364
-
365
- **Multi-Model Support:**
366
- - Each model gets its own Service, UseCase, Controller, and Store classes
367
- - In `api`/`web`, each model is a separate key (e.g., `api.Cat`, `api.Person`)
368
- - UseCase references use `ModelName:actionName` format (e.g., `Post:list`, `Person:create`)
369
- - Handlers within `useCases` do not need model prefix (model is already the key)
370
- - Controllers and services are generated per model, not per module
371
-
372
- ## Dependency Injection & Wiring
373
-
374
- The application uses a decorator-driven DI system. All wiring is auto-generated in `src/app.ts` between `// currentjs:controllers:start` and `// currentjs:controllers:end` markers. **Never edit this block manually** — it is regenerated on each `current generate`.
375
-
376
- ### `@Injectable` Decorator
377
-
378
- Lives in `src/system.ts`. Marks a class for automatic DI discovery:
379
-
380
- ```typescript
381
- import { Injectable } from '../../../../system';
382
-
383
- @Injectable()
384
- export class MyService {
385
- constructor(private myStore: MyStore) {}
386
- }
387
- ```
388
-
389
- Generated Stores, Services, and UseCases already have `@Injectable()`. Controllers use `@Controller()` from `@currentjs/router` instead.
390
-
391
- ### Adding Custom Classes to DI
392
-
393
- If you create a new class that should be auto-wired:
394
- 1. Add `@Injectable()` decorator (import from `src/system.ts`)
395
- 2. Declare dependencies as constructor parameters with their types
396
- 3. Run `current generate` — the class will be auto-imported and instantiated in `app.ts`
397
- 4. Run `current commit` to preserve your changes
398
-
399
- ### How Wiring Works
400
-
401
- The generator:
402
- 1. Scans all module `.ts` files for `@Injectable` and `@Controller` decorators
403
- 2. Parses constructor parameters to build a dependency graph
404
- 3. Topologically sorts classes (stores → services → use cases → controllers)
405
- 4. Generates imports, instantiations, and the `controllers` array in `app.ts`
406
-
407
- Database provider instances are injected into stores based on `app.yaml` configuration (global, with optional per-module overrides). Both npm packages and local file paths are supported as providers.
408
-
409
- ## Module Structure
410
- ```
411
- src/modules/ModuleName/
412
- ├── domain/
413
- │ ├── entities/
414
- │ │ └── Entity.ts # Domain model (aggregate root)
415
- │ └── valueObjects/
416
- │ └── ValueObject.ts # Value objects (if any)
417
- ├── application/
418
- │ ├── useCases/
419
- │ │ └── EntityUseCase.ts # Use case orchestrator
420
- │ ├── services/
421
- │ │ └── EntityService.ts # Business logic handlers
422
- │ └── dto/
423
- │ └── EntityAction.ts # Input/Output DTOs per action
424
- ├── infrastructure/
425
- │ ├── controllers/
426
- │ │ ├── EntityApiController.ts # REST API endpoints
427
- │ │ └── EntityWebController.ts # Web page controllers
428
- │ └── stores/
429
- │ └── EntityStore.ts # Data access implementation
430
- ├── views/
431
- │ ├── entityList.html # List view template
432
- │ ├── entityDetail.html # Detail view template
433
- │ ├── entityCreate.html # Create form template
434
- │ └── entityEdit.html # Edit form template
435
- └── modulename.yaml # Module configuration
436
- ```
437
-
438
- ## Best Practices
439
-
440
- - Use Domain Driven Design and Clean Architecture (kind of).
441
- - Prefer declarative configuration over imperative programming: when possible, change yamls instead of writing the code. Write the code only when it's really neccessary.
442
- - CRUD operation are autogenerated (first in module's yaml, then in the generated code).
443
- - If some custom action is needed, then it has to be defined in the **Service** then just put this action to the module's yaml. You may also define new methods in the *Store* and its interface (if needed).
444
- - Business rules must be defined only in models. That also applies to business rules validations (in contrast to *just* validations: e.g. if field exists and is of needed type – then validators are in use)
445
-
446
- ## Core Package APIs (For Generated Code)
447
-
448
- ### @currentjs/router - Controller Decorators & Context
449
-
450
- **Decorators (in controllers):**
451
- ```typescript
452
- @Controller('/api/posts') // Base path for controller
453
- @Get('/') // GET endpoint
454
- @Get('/:id') // GET with path parameter
455
- @Post('/') // POST endpoint
456
- @Put('/:id') // PUT endpoint
457
- @Delete('/:id') // DELETE endpoint
458
- @Render('template', 'layout') // For web controllers
459
- ```
460
-
461
- **Context Object (in route handlers):**
462
- ```typescript
463
- interface IContext {
464
- request: {
465
- url: string;
466
- path: string;
467
- method: string;
468
- parameters: Record<string, string | number>; // Path params + query params
469
- body: any; // Parsed JSON or raw string
470
- headers: Record<string, string | string[]>;
471
- user?: AuthenticatedUser; // Parsed JWT user if authenticated
472
- };
473
- response: Record<string, any>;
474
- }
475
-
476
- // Usage examples
477
- const id = parseInt(ctx.request.parameters.id as string);
478
- const page = parseInt(ctx.request.parameters.page as string) || 1;
479
- const payload = ctx.request.body;
480
- const user = ctx.request.user; // If authenticated
481
- ```
482
-
483
- **Authentication Support:**
484
- - JWT tokens parsed automatically from `Authorization: Bearer <token>` header
485
- - User object available at `ctx.request.user` with `id`, `email`, `role` fields
486
- - No manual setup required in generated code
487
-
488
- ### @currentjs/templating - Template Syntax
489
-
490
- **Variables & Data Access:**
491
- ```html
492
- {{ variableName }}
493
- {{ object.property }}
494
- {{ $root.arrayData }} <!-- Access root data -->
495
- {{ $index }} <!-- Loop index -->
496
- ```
497
-
498
- **Control Structures:**
499
- ```html
500
- <!-- Loops -->
501
- <tbody x-for="$root" x-row="item">
502
- <tr>
503
- <td>{{ item.name }}</td>
504
- <td>{{ $index }}</td>
505
- </tr>
506
- </tbody>
507
-
508
- <!-- Conditionals -->
509
- <div x-if="user.isAdmin">Admin content</div>
510
- <span x-if="errors.name">{{ errors.name }}</span>
511
-
512
- <!-- Template includes -->
513
- <userCard name="{{ user.name }}" role="admin" />
514
- ```
515
-
516
- **Layout Integration:**
517
- - Templates use `<!-- @template name="templateName" -->` header
518
- - Main layout gets `{{ content }}` variable
519
- - Forms use `{{ formData.field || '' }}` for default values
520
-
521
- ### @currentjs/provider-mysql - Database Operations
522
-
523
- **Query Execution (in stores):**
524
- ```typescript
525
- // Named parameters (preferred)
526
- const result = await this.db.query(
527
- 'SELECT * FROM users WHERE status = :status AND age > :minAge',
528
- { status: 'active', minAge: 18 }
529
- );
530
-
531
- // Result handling
532
- if (result.success && result.data.length > 0) {
533
- return result.data.map(row => this.rowToModel(row));
534
- }
535
- ```
536
-
537
- **Common Query Patterns:**
538
- ```typescript
539
- // Insert with auto-generated fields
540
- const row = { ...data, created_at: new Date(), updated_at: new Date() };
541
- const result = await this.db.query(
542
- `INSERT INTO table_name (\${fields.join(', ')}) VALUES (\${placeholders})`,
543
- row
544
- );
545
- const newId = result.insertId;
546
-
547
- // Update with validation
548
- const query = `UPDATE table_name SET \${updateFields.join(', ')}, updated_at = :updated_at WHERE id = :id`;
549
- await this.db.query(query, { ...updateData, updated_at: new Date(), id });
550
-
551
- // Soft delete
552
- await this.db.query(
553
- 'UPDATE table_name SET deleted_at = :deleted_at WHERE id = :id',
554
- { deleted_at: new Date(), id }
555
- );
556
- ```
557
-
558
- **Error Handling:**
559
- ```typescript
560
- try {
561
- const result = await this.db.query(query, params);
562
- } catch (error) {
563
- if (error instanceof MySQLConnectionError) {
564
- throw new Error(`Database connection error: \${error.message}`);
565
- } else if (error instanceof MySQLQueryError) {
566
- throw new Error(`Query error: \${error.message}`);
567
- }
568
- throw error;
569
- }
570
- ```
571
-
572
- ## Frontend System (web/app.js)
573
-
574
- ### Translation System
575
-
576
- **Basic Usage:**
577
- ```javascript
578
- // Translate strings
579
- App.lang.t('Hello World') // Returns translated version or original
580
- App.lang.t('Save changes')
581
-
582
- // Set language
583
- App.lang.set('pl') // Switch to Polish
584
- App.lang.set('en') // Switch to English
585
- App.lang.get() // Get current language code
586
- ```
587
-
588
- **Translation File (web/translations.json):**
589
- ```json
590
- {
591
- "pl": {
592
- "Hello World": "Witaj Świecie",
593
- "Save changes": "Zapisz zmiany",
594
- "Delete": "Usuń"
595
- },
596
- "ru": {
597
- "Hello World": "Привет мир",
598
- "Save changes": "Сохранить изменения"
599
- }
600
- }
601
- ```
602
-
603
- ### UI Feedback & Notifications
604
-
605
- **Toast Notifications:**
606
- ```javascript
607
- App.ui.showToast('Success message', 'success') // Green toast
608
- App.ui.showToast('Error occurred', 'error') // Red toast
609
- App.ui.showToast('Information', 'info') // Blue toast
610
- App.ui.showToast('Warning', 'warning') // Yellow toast
611
- ```
612
-
613
- **Inline Messages:**
614
- ```javascript
615
- App.ui.showMessage('messageId', 'Success!', 'success')
616
- App.ui.showMessage('errorContainer', 'Validation failed', 'error')
617
- ```
618
-
619
- **Modal Dialogs:**
620
- ```javascript
621
- App.ui.showModal('confirmModal', 'Item saved successfully', 'success')
622
- App.ui.showModal('errorModal', 'Operation failed', 'error')
623
- ```
624
-
625
- ### Navigation & Page Actions
626
-
627
- **Navigation Functions:**
628
- ```javascript
629
- // SPA-style navigation
630
- App.nav.go('/posts/123') // Loads via AJAX, updates #main
631
-
632
- // Or use native browser APIs directly
633
- window.history.back() // Go back in history
634
- window.location.href = '/posts' // Full page redirect
635
- window.location.reload() // Reload page
636
- ```
637
-
638
- ### Form Handling & Strategy System
639
-
640
- **Form Strategy Configuration:**
641
- ```html
642
- <!-- Form with strategy attributes -->
643
- <form data-strategy='["toast", "back"]'
644
- data-entity-name="Post"
645
- data-field-types='{"age": "number", "active": "boolean"}'>
646
- <input name="title" type="text" required>
647
- <input name="age" type="number">
648
- <input name="active" type="checkbox">
649
- <button type="submit">Save</button>
650
- </form>
651
- ```
652
-
653
- **Available Strategies:**
654
- - `toast` - Show success toast notification
655
- - `back` - Navigate back using browser history
656
- - `message` - Show inline message in specific element
657
- - `modal` - Show modal dialog
658
- - `redirect` - Redirect to specific URL
659
- - `refresh` - Reload the page
660
- - `remove` - Remove form element
661
-
662
- **Manual Form Submission:**
663
- ```javascript
664
- const form = document.querySelector('#myForm');
665
- App.nav.submit(form, ['toast', 'back'], {
666
- entityName: 'Post',
667
- basePath: '/posts',
668
- messageId: 'form-message'
669
- });
670
- ```
671
-
672
- ### Loading States & Utilities
673
-
674
- **Loading Indicators:**
675
- ```javascript
676
- App.ui.showLoading('#form') // Show spinner on form
677
- App.ui.hideLoading('#form') // Hide spinner
678
- App.ui.showLoading('#main') // Show spinner on main content
679
- ```
680
-
681
- **Utility Functions:**
682
- ```javascript
683
- App.utils.debounce(searchFunction, 300) // Debounce for search inputs
684
- App.utils.$('#selector') // Safe element selection
685
- ```
686
-
687
- ### Event Handling & SPA Integration
688
-
689
- **Automatic Link Handling:**
690
- - Internal links automatically use AJAX navigation
691
- - External links work normally
692
- - Links with `data-external` skip AJAX handling
693
-
694
- **Automatic Form Handling:**
695
- - Forms with `data-strategy` use AJAX submission
696
- - Regular forms work normally
697
- - Automatic JSON conversion from FormData
698
-
699
- **Custom Event Listeners:**
700
- ```javascript
701
- // Re-initialize after dynamic content loading
702
- App.utils.initializeEventListeners();
703
-
704
- // Handle specific link navigation
705
- document.querySelector('#myLink').addEventListener('click', (e) => {
706
- e.preventDefault();
707
- App.nav.go('/custom/path');
708
- });
709
- ```
710
-
711
- ### Global App Object
712
-
713
- **Accessing Functions:**
714
- ```javascript
715
- // All functions available under window.App
716
- // Organized by category for better discoverability
717
-
718
- // UI Functions
719
- App.ui.showToast('Message', 'success');
720
- App.ui.showMessage('elementId', 'Success!', 'success');
721
- App.ui.showModal('modalId', 'Done!', 'success');
722
- App.ui.showLoading('#form');
723
- App.ui.hideLoading('#form');
724
-
725
- // Navigation Functions
726
- App.nav.go('/posts/123'); // SPA-style navigation with AJAX
727
- App.nav.submit(formElement, ['toast', 'back'], options); // Submit form via AJAX
728
-
729
- // Translation Functions
730
- App.lang.t('Translate this'); // Translate string
731
- App.lang.set('pl'); // Set language
732
- App.lang.get(); // Get current language
733
-
734
- // Utility Functions
735
- App.utils.$('#selector'); // Safe element selection
736
- App.utils.debounce(fn, 300); // Debounce function
737
- App.utils.initializeEventListeners(); // Re-initialize after dynamic content
738
-
739
- // Authentication Functions (JWT)
740
- App.auth.setAuthToken(token); // Store JWT token
741
- App.auth.clearAuthToken(); // Remove JWT token
742
- App.auth.buildAuthHeaders(additionalHeaders); // Build headers with auth token
743
- ```
744
-
745
- ### Template Data Binding
746
- ```html
747
- <!-- List with pagination -->
748
- <tbody x-for="modules" x-row="module">
749
- <tr>
750
- <td>{{ module.name }}</td>
751
- <td><a href="/module/{{ module.id }}">View</a></td>
752
- </tr>
753
- </tbody>
754
-
755
- <!-- Form with validation errors -->
756
- <div x-if="errors.name" class="text-danger">{{ errors.name }}</div>
757
- <input type="text" name="name" value="{{ formData.name || '' }}" class="form-control">
758
-
759
- <!-- Form with strategy attributes -->
760
- <form data-strategy='["toast", "back"]'
761
- data-entity-name="Module"
762
- data-field-types='{"count": "number", "active": "boolean"}'>
763
- <!-- form fields -->
764
- </form>
765
- ```
21
+ 10. Run current diff before modifying any generated file. It shows exactly what's been committed (safe to keep) vs. what's purely generated (will be overwritten). Use it as orientation before every non-yaml change.