2026-05-06 22:01:20 +03:30
|
|
|
# AGENT.md
|
|
|
|
|
|
|
|
|
|
## Purpose
|
|
|
|
|
|
|
|
|
|
- This file defines repository-specific instructions for coding agents.
|
|
|
|
|
- Scope is the full repo unless a deeper `AGENT.md` overrides it.
|
|
|
|
|
|
|
|
|
|
## Stack Context
|
|
|
|
|
|
|
|
|
|
- Frontend: Angular 20 standalone app.
|
|
|
|
|
- Package manager: `pnpm`.
|
|
|
|
|
- Deployment: Docker / Docker Compose with tenant-specific services.
|
|
|
|
|
- Current service mapping expectation:
|
|
|
|
|
- `app_default` on host port `8090`
|
|
|
|
|
- `app_tis` on host port `8091`
|
|
|
|
|
|
|
|
|
|
## Tenant Build Rules
|
|
|
|
|
|
|
|
|
|
- `default` tenant currently builds via `ng build` and outputs to `dist/production`.
|
|
|
|
|
- `tis` tenant builds via `ng build --configuration tis` and outputs to `dist/tis`.
|
2026-05-10 14:54:53 +03:30
|
|
|
- `prebuild:tis` is tenant-scoped and should keep using scripts under `scripts/tis/*`.
|
2026-05-06 22:01:20 +03:30
|
|
|
- Keep Docker `DIST_DIR` aligned with actual Angular output path.
|
|
|
|
|
- Do not assume `default` Angular configuration is usable unless verified (it may reference missing replacements).
|
2026-05-10 14:54:53 +03:30
|
|
|
- Do not use Angular `fileReplacements` for static assets (`.png`, `.jpg`, etc.); use tenant public assets or prebuild copy scripts.
|
|
|
|
|
|
|
|
|
|
## Tenant PWA Rules
|
|
|
|
|
|
|
|
|
|
- Keep tenant manifest files under tenant public assets (e.g. `public-tis/favicon/site.webmanifest`).
|
|
|
|
|
- Ensure `manifest` `id`, `start_url`, and `scope` match actual deployment path:
|
|
|
|
|
- root deploy: `/`
|
|
|
|
|
- subpath deploy example: `/tis/`
|
|
|
|
|
- Keep `<link rel="manifest">` path and branding config `manifestPath` aligned.
|
2026-05-06 22:01:20 +03:30
|
|
|
|
|
|
|
|
## Input Component Rules
|
|
|
|
|
|
|
|
|
|
- File: `src/app/shared/components/input/input.component.ts`
|
|
|
|
|
- For `type === 'number'` or `type === 'price'`:
|
|
|
|
|
- Normalize Persian/Arabic digits to English digits.
|
|
|
|
|
- Allow only digits and `.`.
|
|
|
|
|
- Support `fixed` precision formatting when provided.
|
|
|
|
|
- Keep behavior for identifier fields (`mobile`, `phone`, `postalCode`, `nationalId`) string-safe.
|
|
|
|
|
|
|
|
|
|
## Change Policy
|
|
|
|
|
|
|
|
|
|
- Keep changes minimal and scoped to user request.
|
|
|
|
|
- Prefer root-cause fixes over temporary workarounds.
|
|
|
|
|
- Avoid unrelated refactors.
|
|
|
|
|
- Reuse existing patterns and naming conventions.
|
|
|
|
|
|
|
|
|
|
## Do / Don't
|
|
|
|
|
|
|
|
|
|
- Do follow existing field wrapper style in `src/app/shared/components/fields/*.component.ts`.
|
|
|
|
|
- Do reuse `app-input` and set only required props (`type`, `label`, `name`, constraints).
|
|
|
|
|
- Do register every new field in:
|
|
|
|
|
- `src/app/shared/components/fields/index.ts`
|
|
|
|
|
- `src/app/shared/constants/fields/index.ts`
|
|
|
|
|
- Do keep control keys consistent across form group, field component `name`, and `fieldControl` key.
|
|
|
|
|
- Don't add one-off field patterns when an existing field component can be reused.
|
|
|
|
|
- Don't use invalid Angular file replacements for directories or empty paths.
|
|
|
|
|
- Don't change tenant output directories without updating Docker `DIST_DIR`.
|
|
|
|
|
|
|
|
|
|
## How To Create Form Fields
|
|
|
|
|
|
|
|
|
|
- Create a wrapper component in `src/app/shared/components/fields`.
|
|
|
|
|
- Use the same pattern as existing files like:
|
|
|
|
|
- `src/app/shared/components/fields/name.component.ts`
|
|
|
|
|
- `src/app/shared/components/fields/unit_price.component.ts`
|
|
|
|
|
- Minimal wrapper shape:
|
|
|
|
|
- `selector`: `field-<field-name>`
|
|
|
|
|
- template: `<app-input ... />`
|
|
|
|
|
- inputs: `control` (required), optional `name`, optional `label`
|
|
|
|
|
|
|
|
|
|
Example pattern:
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'field-example',
|
|
|
|
|
template: `<app-input [label]="label" [control]="control" [name]="name" type="simple" />`,
|
|
|
|
|
imports: [ReactiveFormsModule, InputComponent],
|
|
|
|
|
})
|
|
|
|
|
export class ExampleComponent {
|
|
|
|
|
@Input({ required: true }) control = new FormControl<string>('');
|
|
|
|
|
@Input() name = 'example';
|
|
|
|
|
@Input() label = 'Example';
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Register New Fields
|
|
|
|
|
|
|
|
|
|
- Export the component from:
|
|
|
|
|
- `src/app/shared/components/fields/index.ts`
|
|
|
|
|
- Add its form control factory in:
|
|
|
|
|
- `src/app/shared/constants/fields/index.ts`
|
|
|
|
|
- `fieldControl` entry shape:
|
|
|
|
|
- key must match form control name
|
|
|
|
|
- return tuple: `[defaultValue, validators]`
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
example: (value = '', isRequired = true): ControlConfig => [
|
|
|
|
|
value,
|
|
|
|
|
isRequired ? [Validators.required] : [],
|
|
|
|
|
],
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Using Fields In Forms
|
|
|
|
|
|
|
|
|
|
- In form group builders, use `fieldControl.<key>(initialValue, isRequired)` for consistency.
|
|
|
|
|
- In templates, render matching wrapper component and pass the matching control:
|
|
|
|
|
- `<field-example [control]="form.controls.example" />`
|
|
|
|
|
- For numeric/price behavior, use `app-input` `type="number"` or `type="price"` and optional `[fixed]`.
|
|
|
|
|
|
2026-05-12 19:58:53 +03:30
|
|
|
## List Component Configs
|
|
|
|
|
|
|
|
|
|
- Centralized list metadata is stored in `src/app/shared/constants/list-configs/` — **never** in domain modules.
|
|
|
|
|
- Structure by data type, not domain (e.g. `good-list.const.ts`, `sku-list.const.ts`, `category-list.const.ts`).
|
|
|
|
|
- Each config implements `IListConfig` (defined in `list-config.model.ts`):
|
|
|
|
|
- `pageTitle`: display title for the list page
|
|
|
|
|
- `addNewCtaLabel`: call-to-action button label
|
|
|
|
|
- `emptyPlaceholderTitle` and `emptyPlaceholderDescription`: empty state messaging
|
|
|
|
|
- `columns`: array of `IColumn[]` definitions
|
|
|
|
|
- Usage in list components:
|
|
|
|
|
```ts
|
|
|
|
|
@Input() header: IColumn[] = goodListConfig.columns;
|
|
|
|
|
listConfig = goodListConfig;
|
|
|
|
|
```
|
|
|
|
|
- Any domain needing a list config imports from `@/shared/constants/list-configs` — no cross-domain dependencies.
|
|
|
|
|
- **Do not** define configs inside domains or duplicate them across modules.
|
|
|
|
|
|
|
|
|
|
## Breadcrumb Usage in Stores & Views
|
|
|
|
|
|
|
|
|
|
- Entity stores expose `breadcrumbItems` as a computed signal.
|
|
|
|
|
- Call `store.breadcrumbItems()` in view components and extend with current page:
|
|
|
|
|
```ts
|
|
|
|
|
setBreadcrumb() {
|
|
|
|
|
this.breadcrumbService.setItems([
|
|
|
|
|
...this.store.breadcrumbItems(),
|
|
|
|
|
{ title: 'Current Page' },
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
- Root page breadcrumbs are set in the store's `getData()` method once entity is loaded.
|
|
|
|
|
|
2026-05-06 22:01:20 +03:30
|
|
|
## Validation Checklist
|
|
|
|
|
|
|
|
|
|
- For TypeScript-only changes, run:
|
|
|
|
|
- `pnpm -s exec tsc -p tsconfig.app.json --noEmit`
|
|
|
|
|
- For Docker/build changes, verify with:
|
|
|
|
|
- `docker compose build app_default`
|
|
|
|
|
- `docker compose build app_tis`
|
|
|
|
|
- Start with targeted validation, then broader checks only if needed.
|
|
|
|
|
|
|
|
|
|
## Communication Expectations
|
|
|
|
|
|
|
|
|
|
- Report exactly which files changed and why.
|
|
|
|
|
- Call out any assumptions or discovered config mismatches.
|
|
|
|
|
- If validation is blocked (permissions, missing dependencies), state it clearly and provide next command.
|