mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
Compare commits
14 Commits
feature/52
...
feature/sc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a27a0c8b9c | ||
|
|
acb76c1384 | ||
|
|
5882655862 | ||
|
|
5b440da557 | ||
|
|
be91a0f1b4 | ||
|
|
65e3d5d4f1 | ||
|
|
a0e7614fa0 | ||
|
|
094c881e7f | ||
|
|
d3cd6f415b | ||
|
|
e733396c63 | ||
|
|
e88795f96b | ||
|
|
fd276b6553 | ||
|
|
22c3b057d7 | ||
|
|
b7feea46f7 |
@@ -1,4 +0,0 @@
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 iOS major versions
|
||||
@@ -7,7 +7,6 @@ indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = crlf
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
|
||||
86
.github/commit-instructions.md
vendored
86
.github/commit-instructions.md
vendored
@@ -1,86 +0,0 @@
|
||||
# Commit Message Instructions (Conventional Commits)
|
||||
|
||||
Commit messages should follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). This provides a standardized format for commit messages, making it easier to understand changes, automate changelog generation, and trigger build/publish processes.
|
||||
|
||||
## Format
|
||||
|
||||
The commit message structure is as follows:
|
||||
|
||||
```
|
||||
<type>[optional scope]: <description>
|
||||
|
||||
[optional body]
|
||||
|
||||
[optional footer(s)]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Components
|
||||
|
||||
1. **Type**: Indicates the kind of change introduced by the commit. Must be one of the allowed types (see below).
|
||||
2. **Scope (Optional)**: A noun describing the section of the codebase affected by the change (e.g., `auth`, `ui`, `build`). Enclosed in parentheses.
|
||||
3. **Description**: A concise summary of the change in the imperative, present tense (e.g., "add login feature", not "added login feature" or "adds login feature"). Starts with a lowercase letter and should not end with a period. Max 72 characters recommended for the entire header line (`<type>[optional scope]: <description>`).
|
||||
4. **Body (Optional)**: A more detailed explanation of the changes. Use the imperative, present tense. Explain the _what_ and _why_ vs. _how_. Separate from the description by a blank line. Wrap lines at 72 characters.
|
||||
5. **Footer(s) (Optional)**: Contains additional metadata. Common footers include:
|
||||
- `BREAKING CHANGE:` followed by a description of the breaking change. A `!` can also be appended to the type/scope (`feat!:`) to indicate a breaking change.
|
||||
- Issue references (e.g., `Refs: #123`, `Closes: #456`). Separate from the body by a blank line.
|
||||
|
||||
---
|
||||
|
||||
### Allowed Types
|
||||
|
||||
- **feat**: A new feature for the user.
|
||||
- **fix**: A bug fix for the user.
|
||||
- **build**: Changes that affect the build system or external dependencies (e.g., gulp, broccoli, npm).
|
||||
- **chore**: Other changes that don't modify src or test files (e.g., updating dependencies, build tasks).
|
||||
- **ci**: Changes to CI configuration files and scripts (e.g., Travis, Circle, BrowserStack, SauceLabs).
|
||||
- **docs**: Documentation only changes.
|
||||
- **perf**: A code change that improves performance.
|
||||
- **refactor**: A code change that neither fixes a bug nor adds a feature.
|
||||
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc).
|
||||
- **test**: Adding missing tests or correcting existing tests.
|
||||
|
||||
---
|
||||
|
||||
### Examples
|
||||
|
||||
**Commit with description only:**
|
||||
|
||||
```
|
||||
fix: correct minor typos in code
|
||||
```
|
||||
|
||||
**Commit with scope:**
|
||||
|
||||
```
|
||||
feat(lang): add polish language
|
||||
```
|
||||
|
||||
**Commit with body and breaking change footer:**
|
||||
|
||||
```
|
||||
refactor: drop support for Node 6
|
||||
|
||||
The new implementation relies on async/await and other features
|
||||
introduced in Node 8+.
|
||||
|
||||
BREAKING CHANGE: refactor to use JavaScript features not available in Node 6.
|
||||
```
|
||||
|
||||
**Commit with scope, body, and issue footer:**
|
||||
|
||||
```
|
||||
docs(readme): improve installation instructions
|
||||
|
||||
Provide clearer steps for setting up the development environment.
|
||||
Add links to prerequisite tools.
|
||||
|
||||
Closes: #12
|
||||
```
|
||||
|
||||
**Commit with `!` for breaking change:**
|
||||
|
||||
```
|
||||
feat(api)!: send an email to the customer when a product is shipped
|
||||
```
|
||||
415
.github/copilot-instructions.md
vendored
415
.github/copilot-instructions.md
vendored
@@ -1,415 +0,0 @@
|
||||
## ISA Frontend – AI Assistant Working Rules
|
||||
|
||||
Concise, project-specific guidance so an AI agent can be productive quickly. Focus on THESE patterns; avoid generic boilerplate.
|
||||
|
||||
### 1. Monorepo & Tooling
|
||||
|
||||
- Nx workspace (Angular 20 + Libraries under `libs/**`, main app `apps/isa-app`).
|
||||
- Scripts (see `package.json`):
|
||||
- Dev serve: `npm start` (=> `nx serve isa-app --ssl`).
|
||||
- Library tests (exclude app): `npm test` (Jest + emerging Vitest). CI uses `npm run ci`.
|
||||
- Build dev: `npm run build`; prod: `npm run build-prod`.
|
||||
- Storybook: `npm run storybook`.
|
||||
- Swagger codegen: `npm run generate:swagger` then `npm run fix:files:swagger`.
|
||||
- Default branch in Nx: `develop` (`nx.json: defaultBase`). Use affected commands when adding libs.
|
||||
- Node >=22, TS 5.8, ESLint flat config (`eslint.config.js`).
|
||||
|
||||
### 1.a Project Tree (Detailed Overview)
|
||||
|
||||
```
|
||||
.
|
||||
├─ apps/
|
||||
│ └─ isa-app/ # Main Angular app (Jest). Legacy non-standalone root component pattern.
|
||||
│ ├─ project.json # Build/serve/test targets
|
||||
│ ├─ src/
|
||||
│ │ ├─ main.ts / index.html # Angular bootstrap
|
||||
│ │ ├─ app/main.component.ts # Root component (standalone:false)
|
||||
│ │ ├─ environments/ # Environment files (prod replace)
|
||||
│ │ ├─ assets/ # Static assets
|
||||
│ │ └─ config/ # Runtime config JSON (read via Config service)
|
||||
│ └─ .storybook/ # App Storybook config
|
||||
│
|
||||
├─ libs/ # All reusable code (grouped by domain / concern)
|
||||
│ ├─ core/ # Cross-cutting infrastructure
|
||||
│ │ ├─ logging/ # Logging service + providers + sinks
|
||||
│ │ │ ├─ src/lib/logging.service.ts
|
||||
│ │ │ ├─ src/lib/logging.providers.ts
|
||||
│ │ │ └─ README.md # Full API & patterns
|
||||
│ │ ├─ config/ # `Config` service (Zod validated lookup)
|
||||
│ │ └─ storage/ # User-scoped storage + signal store feature (`withStorage`)
|
||||
│ │ ├─ src/lib/signal-store-feature.ts
|
||||
│ │ └─ src/lib/storage.ts
|
||||
│ │
|
||||
│ ├─ shared/ # Shared UI/services not domain specific
|
||||
│ │ └─ scanner/ # Scandit integration (tokens, service, components, platform gating)
|
||||
│ │ ├─ src/lib/scanner.service.ts
|
||||
│ │ └─ src/lib/render-if-scanner-is-ready.directive.ts
|
||||
│ │
|
||||
│ ├─ remission/ # Remission domain features (newer pattern; Vitest)
|
||||
│ │ ├─ feature/
|
||||
│ │ │ ├─ remission-return-receipt-details/
|
||||
│ │ │ │ ├─ vite.config.mts # Signals + Vitest example
|
||||
│ │ │ │ └─ src/lib/resources/ # Resource factories (signals async pattern)
|
||||
│ │ │ └─ remission-return-receipt-list/
|
||||
│ │ └─ shared/ # Dialogs / shared remission UI pieces
|
||||
│ │
|
||||
│ ├─ common/ # Cross-domain utilities (decorators, print, data-access)
|
||||
│ ├─ utils/ # Narrow utility libs (ean-validation, z-safe-parse, etc.)
|
||||
│ ├─ ui/ # Generic UI components (presentational)
|
||||
│ ├─ icons/ # Icon sets / wrappers
|
||||
│ ├─ catalogue/ # Domain area (legacy Jest)
|
||||
│ ├─ customer/ # Domain area (legacy Jest)
|
||||
│ └─ oms/ # Domain area (legacy Jest)
|
||||
│
|
||||
├─ generated/swagger/ # Generated API clients (regen via scripts; do not hand edit)
|
||||
├─ tools/ # Helper scripts (e.g. swagger fix script)
|
||||
├─ testresults/ # JUnit XML (jest-junit). CI artifact pickup.
|
||||
├─ coverage/ # Per-project coverage outputs
|
||||
├─ tailwind-plugins/ # Custom Tailwind plugin modules used by `tailwind.config.js`
|
||||
├─ vitest.workspace.ts # Glob enabling multi-lib Vitest detection
|
||||
├─ nx.json / package.json # Workspace + scripts + defaultBase=develop
|
||||
└─ eslint.config.js # Flat ESLint root config
|
||||
```
|
||||
|
||||
Guidelines: create new code in the closest domain folder; expose public API via each lib `src/index.ts`; follow existing naming (`feature-name.type.ts`). Keep generated swagger untouched—extend via wrapper libs if needed.
|
||||
|
||||
### 1.b Import Path Aliases
|
||||
|
||||
Use existing TS path aliases (see `tsconfig.base.json`) instead of long relative paths:
|
||||
|
||||
Core / Cross-cutting:
|
||||
|
||||
- `@isa/core/logging`, `@isa/core/config`, `@isa/core/storage`, `@isa/core/tabs`, `@isa/core/notifications`
|
||||
|
||||
Domain & Features:
|
||||
|
||||
- Catalogue: `@isa/catalogue/data-access`
|
||||
- Customer: `@isa/customer/data-access`
|
||||
- OMS features: `@isa/oms/feature/return-details`, `.../return-process`, `.../return-review`, `.../return-search`, `.../return-summary`
|
||||
- OMS shared: `@isa/oms/shared/product-info`, `@isa/oms/shared/task-list`
|
||||
- Remission: `@isa/remission/data-access`, feature libs (`@isa/remission/feature/remission-return-receipt-details`, `...-list`) and shared (`@isa/remission/shared/remission-start-dialog`, `.../search-item-to-remit-dialog`, `.../return-receipt-actions`, `.../product`)
|
||||
|
||||
Shared / UI:
|
||||
|
||||
- Shared libs: `@isa/shared/scanner`, `@isa/shared/filter`, `@isa/shared/product-image`, `@isa/shared/product-router-link`, `@isa/shared/product-format`
|
||||
- UI components: `@isa/ui/buttons`, `@isa/ui/dialog`, `@isa/ui/input-controls`, `@isa/ui/layout`, `@isa/ui/menu`, `@isa/ui/toolbar`, etc. (one alias per folder under `libs/ui/*`)
|
||||
- Icons: `@isa/icons`
|
||||
|
||||
Utilities:
|
||||
|
||||
- `@isa/utils/ean-validation`, `@isa/utils/z-safe-parse`, `@isa/utils/scroll-position`
|
||||
|
||||
Generated Swagger Clients:
|
||||
|
||||
- `@generated/swagger/isa-api`, `@generated/swagger/oms-api`, `@generated/swagger/inventory-api`, etc. (one per subfolder). Never edit generated sources—wrap in a domain lib if extension needed.
|
||||
|
||||
App-local (only inside `apps/isa-app` context):
|
||||
|
||||
- Namespaced folders: `@adapter/*`, `@domain/*`, `@hub/*`, `@modal/*`, `@page/*`, `@shared/*` (and nested: `@shared/components/*`, `@shared/services/*`, etc.), `@ui/*`, `@utils/*`, `@swagger/*`.
|
||||
|
||||
Patterns:
|
||||
|
||||
- Always add new reusable code as a library then expose via an `@isa/...` alias; do not add new generic code under app-local aliases if it may be reused later.
|
||||
- When introducing a new library ensure its `src/index.ts` re-exports only stable public surface; internal helpers stay un-exported.
|
||||
- For new generated API groups, extend via thin wrappers in a domain `data-access` lib rather than patching generated code.
|
||||
|
||||
### 2. Testing Strategy
|
||||
|
||||
- Legacy tests: Jest (`@nx/jest:jest`). New feature libs (e.g. remission feature) use Vitest + Vite plugin (`vite.config.mts`).
|
||||
- When adding a new library today prefer Vitest unless consistency with existing Jest-only area is required.
|
||||
- Do NOT mix frameworks inside one lib. Check presence of `vite.config.*` to know it is Vitest-enabled.
|
||||
- App (`isa-app`) still uses Jest.
|
||||
|
||||
### 3. Architecture & Cross-Cutting Services
|
||||
|
||||
- Core libraries underpin features: `@isa/core/logging`, `@isa/core/config`, `@isa/core/storage`.
|
||||
- Feature domains grouped (e.g. `libs/remission/**`, `libs/shared/**`, `libs/common/**`). Keep domain-specific code there; UI-only pieces in `ui/` or `shared/`.
|
||||
- Prefer standalone components but some legacy components set `standalone: false` (see `MainComponent`). Maintain existing pattern unless doing a focused migration.
|
||||
|
||||
### 4. Logging (Critical Pattern)
|
||||
|
||||
- Central logging via `@isa/core/logging` (files: `logging.service.ts`, `logging.providers.ts`).
|
||||
- Configure once in app config using provider builders: `provideLogging(withLogLevel(...), withSink(ConsoleLogSink), withContext({...}))`.
|
||||
- Use factory `logger(() => ({ dynamicContext }))` (see README) rather than injecting `LoggingService` directly unless extending framework code.
|
||||
- Context hierarchy: global -> component (`provideLoggerContext`) -> instance (factory param) -> message (callback arg). Always pass context as lazy function `() => ({ ... })` for perf.
|
||||
- Respect log level threshold; do not perform expensive serialization before calling (let sinks handle it or gate behind dev checks).
|
||||
|
||||
### 5. Configuration Access
|
||||
|
||||
- Use `Config` service (`@isa/core/config/src/lib/config.ts`). Fetch values with Zod schema: `config.get('licence.scandit', z.string())` (see `SCANDIT_LICENSE` token). Avoid deprecated untyped access.
|
||||
|
||||
### 6. Storage & State Persistence
|
||||
|
||||
- Storage abstraction: `injectStorage(SomeProvider)` wraps a `StorageProvider` (local/session/indexedDB/custom user storage) and prefixes keys with current authenticated user `sub` (OAuth `sub` fallback 'anonymous').
|
||||
- When adding persisted signal stores, use `withStorage(storageKey, ProviderType)` feature (`signal-store-feature.ts`) to auto debounce-save (1s) + restore on init. Only pass plain serializable state.
|
||||
|
||||
### 7. Signals & State
|
||||
|
||||
- Internal state often via Angular signals & NgRx Signals (`@ngrx/signals`). Avoid manual subscriptions—prefer computed/signals and `rxMethod` for side effects.
|
||||
- When persisting, ensure objects are JSON-safe; validation via Zod if deserializing external data.
|
||||
|
||||
#### 7.a NgRx Signals Deep Dive
|
||||
|
||||
Core building blocks we use:
|
||||
|
||||
- `signalStore(...)` + features: `withState`, `withComputed`, `withMethods`, `withHooks`, `withStorage` (custom feature in `core/storage`).
|
||||
- `rxMethod` (from `@ngrx/signals/rxjs-interop`) to bridge imperative async flows (HTTP calls, debounce, switchMap) into store-driven mutations.
|
||||
- `getState`, `patchState` for immutable, shallow merges; avoid manually mutating nested objects—spread + patch.
|
||||
|
||||
Patterns:
|
||||
|
||||
1. Store Shape: Keep initial state small & serializable (no class instances, functions, DOM nodes). Derive heavy or view-specific projections with `withComputed`.
|
||||
2. Side Effects: Wrap fetch/update flows inside `rxMethod` pipes; ensure cancellation semantics (`switchMap`) to drop stale requests.
|
||||
3. Persistence: Apply `withStorage(key, Provider)` last so hooks run after other features; persisted state must be plain JSON (no Dates—convert to ISO strings). Debounce already handled (1s) in `withStorage`—do NOT add another debounce upstream unless burst traffic is extreme.
|
||||
4. Error Handling: Keep an `error` field in state for presentation; log via `logger()` at Warn/Error levels but do not store full Error object (serialize minimal fields: `message`, maybe `code`).
|
||||
5. Loading Flags: Prefer a boolean `loading` OR a discriminated union `status: 'idle'|'loading'|'success'|'error'` for richer UI; avoid multiple booleans that can drift.
|
||||
6. Computed Selectors: Name as `XComputed` or just semantic (e.g. `filteredItems`) using `computed(() => ...)` inside `withComputed`; never cause side-effects in a computed.
|
||||
7. Resource Factory Pattern: For remote data needed in multiple components, create a factory function returning an object with `value`, `isLoading`, `error` signals plus a `reload()` method; see remission `resources/` directory.
|
||||
|
||||
Store Lifecycle Hooks:
|
||||
|
||||
- Use `withHooks({ onInit() { ... }, onDestroy() { ... } })` for restoration, websockets, or timers. Pair cleanups explicitly.
|
||||
|
||||
Persistence Feature (`withStorage`):
|
||||
|
||||
- Implementation: Debounced `storeState` rxMethod listens to any state change, saves hashed user‑scoped key (see `hash.utils.ts`). On init it calls `restoreState()`.
|
||||
- Extending: If you need to blacklist transient fields from persistence, add a method wrapping `getState` and remove keys before `storage.set` (extend feature locally rather than editing shared code unless broadly needed).
|
||||
|
||||
Typical Store Template:
|
||||
|
||||
```ts
|
||||
// feature-x.store.ts
|
||||
import {
|
||||
signalStore,
|
||||
withState,
|
||||
withComputed,
|
||||
withMethods,
|
||||
withHooks,
|
||||
} from '@ngrx/signals';
|
||||
import { rxMethod } from '@ngrx/signals/rxjs-interop';
|
||||
import { debounceTime, switchMap, tap, catchError, of } from 'rxjs';
|
||||
import { withStorage } from '@isa/core/storage';
|
||||
import { logger } from '@isa/core/logging';
|
||||
|
||||
interface FeatureXState {
|
||||
items: ReadonlyArray<Item>;
|
||||
query: string;
|
||||
loading: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
const initialState: FeatureXState = { items: [], query: '', loading: false };
|
||||
|
||||
export const FeatureXStore = signalStore(
|
||||
withState(initialState),
|
||||
withProps((store, logger = logger(() => ({ store: 'FeatureX' }))) => ({
|
||||
_logger: logger,
|
||||
})),
|
||||
withComputed(({ items, query }) => ({
|
||||
filtered: computed(() => items().filter((i) => i.name.includes(query()))),
|
||||
hasError: computed(() => !!query() && !items().length),
|
||||
})),
|
||||
withMethods((store) => ({
|
||||
setQuery: (q: string) => patchState(store, { query: q }),
|
||||
// rxMethod side effect to load items
|
||||
loadItems: rxMethod<string | void>(
|
||||
pipe(
|
||||
debounceTime(150),
|
||||
tap(() => patchState(store, { loading: true, error: undefined })),
|
||||
switchMap(() =>
|
||||
fetchItems(store.query()).pipe(
|
||||
tap((items) => patchState(store, { items, loading: false })),
|
||||
catchError((err) => {
|
||||
store._logger.error('Load failed', err as Error, () => ({
|
||||
query: store.query(),
|
||||
}));
|
||||
patchState(store, {
|
||||
loading: false,
|
||||
error: (err as Error).message,
|
||||
});
|
||||
return of([]);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
})),
|
||||
withHooks((store) => ({
|
||||
onInit() {
|
||||
store.loadItems();
|
||||
},
|
||||
})),
|
||||
withStorage('feature-x', LocalStorageProvider),
|
||||
);
|
||||
```
|
||||
|
||||
Testing Signal Stores (Vitest or Jest):
|
||||
|
||||
- Use `runInInjectionContext(TestBed.inject(Injector), () => FeatureXStore)` or instantiate via exported factory if provided.
|
||||
- For async rxMethod flows, flush microtasks (`await vi.runAllTimersAsync()` if timers used) or rely on returned observable completion when you subscribe inside the test harness.
|
||||
- Snapshot only primitive slices (avoid full object snapshots with volatile ordering).
|
||||
|
||||
Migration Tips:
|
||||
|
||||
- Converting legacy NgRx reducers: Start by lifting static initial state + selectors into `withState` + `withComputed`; replace effects with `rxMethod` maintaining cancellation semantics (`switchMap` mirrors effect flattening strategy).
|
||||
- Keep action names only if externally observed (analytics, logging). Otherwise remove ceremony—call store methods directly.
|
||||
|
||||
Anti-Patterns to Avoid:
|
||||
|
||||
- Writing to signals inside a computed or inside another signal setter (causes cascading updates).
|
||||
- Storing large unnormalized arrays and then repeatedly filtering/sorting in multiple components—centralize that in computed selectors.
|
||||
- Persisting secrets or PII directly; hash keys already user-scoped but content still plain—sanitize if needed.
|
||||
- Returning raw subscriptions from store methods; expose signals or idempotent methods only.
|
||||
|
||||
#### 7.b Prefer Signals over Observables (Practical Rules)
|
||||
|
||||
Default to signals for all in-memory UI & derived state; keep Observables only at I/O edges.
|
||||
|
||||
Use Observables for:
|
||||
|
||||
- HTTP / WebSocket / SignalR streams at the boundary.
|
||||
- Timer / interval / external event sources.
|
||||
- Interop with legacy NgRx store pieces not yet migrated.
|
||||
|
||||
Immediately convert inbound Observables to signals:
|
||||
|
||||
```ts
|
||||
// Legacy service returning Observable<Item[]>
|
||||
items$ = http.get<Item[]>(url);
|
||||
// New pattern
|
||||
const items = toSignal(http.get<Item[]>(url), { initialValue: [] });
|
||||
```
|
||||
|
||||
Expose signals from stores & services:
|
||||
|
||||
```ts
|
||||
// BAD (forces template async pipe + subscription mgmt)
|
||||
getItems(): Observable<Item[]> { return this.http.get(...); }
|
||||
|
||||
// GOOD
|
||||
items = toSignal(this.http.get<Item[]>(url), { initialValue: [] });
|
||||
```
|
||||
|
||||
Bridge when needed:
|
||||
|
||||
```ts
|
||||
// Signal -> Observable (rare):
|
||||
const queryChanges$ = fromSignal(query, { requireSync: true });
|
||||
|
||||
// Observable -> Signal (preferred):
|
||||
const data = toSignal(data$, { initialValue: undefined });
|
||||
```
|
||||
|
||||
Side-effects: never subscribe manually—wrap in `rxMethod` (cancels stale work via `switchMap`).
|
||||
|
||||
```ts
|
||||
loadData: rxMethod<void>(
|
||||
pipe(
|
||||
switchMap(() =>
|
||||
this.api.fetch().pipe(tap((r) => patchState(store, { data: r }))),
|
||||
),
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
Template usage: reference signals directly (`{{ item.name }}`) or in control flow; no `| async` needed.
|
||||
|
||||
Replacing combineLatest / map chains:
|
||||
|
||||
```ts
|
||||
// Before (Observable)
|
||||
vm$ = combineLatest([a$, b$]).pipe(map(([a, b]) => buildVm(a, b)));
|
||||
|
||||
// After (Signals)
|
||||
const vm = computed(() => buildVm(a(), b()));
|
||||
```
|
||||
|
||||
Debounce / throttle user input:
|
||||
Keep raw form value as a signal; create an rxMethod for debounced fetch instead of debouncing inside a computed.
|
||||
|
||||
```ts
|
||||
search = signal('');
|
||||
runSearch: rxMethod<string>(
|
||||
pipe(
|
||||
debounceTime(300),
|
||||
switchMap((term) =>
|
||||
this.api
|
||||
.search(term)
|
||||
.pipe(tap((results) => patchState(store, { results }))),
|
||||
),
|
||||
),
|
||||
);
|
||||
effect(() => {
|
||||
runSearch(this.search());
|
||||
});
|
||||
```
|
||||
|
||||
Avoid converting a signal back to an Observable just to use a single RxJS operator; prefer inline signal `computed` or small helper.
|
||||
|
||||
Migration heuristic:
|
||||
|
||||
1. Identify component `foo$` fields used only in template -> convert to signal via `toSignal`.
|
||||
2. Collapse chains of `combineLatest` + `map` into `computed`.
|
||||
3. Replace imperative `subscribe` side-effects with `rxMethod` + `patchState`.
|
||||
4. Add persistence last via `withStorage` if state must survive reload.
|
||||
|
||||
Performance tip: heavy derived computations (sorting large arrays) belong in a memoized `computed`; if expensive & infrequently needed, gate behind another signal flag.
|
||||
|
||||
### 8. Scanner Integration (Scandit)
|
||||
|
||||
- Barcode scanning encapsulated in `@isa/shared/scanner` (`scanner.service.ts`). Use provided injection tokens for license & defaults (override via DI if needed). Service auto-configures once; `ready` signal triggers `configure()` lazily.
|
||||
- Always catch and log errors with proper context; platform gating throws `PlatformNotSupportedError` which is downgraded to warn.
|
||||
|
||||
### 9. Styling
|
||||
|
||||
- Tailwind with custom semantic tokens (`tailwind.config.js`). Prefer design tokens like `text-isa-neutral-700`, spacing utilities with custom `px-*` scales rather than ad‑hoc raw values.
|
||||
- Global overlays rely on CDK classes; retain `@angular/cdk/overlay-prebuilt.css` in style arrays when creating new entrypoints or Storybook stories.
|
||||
|
||||
### 10. Library Conventions
|
||||
|
||||
- File naming: kebab-case; feature first then type (e.g. `return-receipt-list.component.ts`).
|
||||
- Provide public API via each lib `src/index.ts`. Export only stable symbols; keep internal utilities in subfolders not re-exported.
|
||||
- Add `project.json` with `test` & `lint` targets; for new Vitest libs include `vite.config.mts` and adjust `tsconfig.spec.json` references to vitest types.
|
||||
|
||||
### 11. Adding / Modifying Tests
|
||||
|
||||
- For Jest libs: standard `*.spec.ts` with `TestBed`. Spectator may appear in legacy code—do not introduce Spectator in new tests; use Angular Testing Utilities.
|
||||
- For Vitest libs: ensure `vite.config.mts` includes `setupFiles`. Use `describe/it` from `vitest` and Angular TestBed (see remission resource spec for pattern of using `runInInjectionContext`).
|
||||
- Prefer resource-style factories returning signals for async state (pattern in `createSupplierResource`).
|
||||
|
||||
### 12. Performance & Safety
|
||||
|
||||
- Logging: rely on lazy context function; avoid `JSON.stringify()` unless behind a dev guard.
|
||||
- Storage: hashing keys (see `hash.utils.ts`) ensures stable key space; do not bypass if you need consistent per-user scoping.
|
||||
- Scanner overlay: always clean up overlay + event listeners (follow existing `open` implementation for pattern).
|
||||
|
||||
### 13. CI / Coverage / Artifacts
|
||||
|
||||
- JUnit XML placed in `testresults/` (Jest configured with `jest-junit`). Keep filename stability for pipeline consumption; do not rename those outputs.
|
||||
- Coverage output under `coverage/libs/...`; respect Nx caching—avoid side effects outside project roots.
|
||||
|
||||
### 14. When Unsure
|
||||
|
||||
- Search existing domain folder for analogous implementation (e.g. new feature under remission: inspect sibling feature libs for structure).
|
||||
- Preserve existing DI token patterns instead of introducing new global singletons.
|
||||
|
||||
### 15. Quick Examples
|
||||
|
||||
```ts
|
||||
// New feature logger usage
|
||||
const log = logger(() => ({ feature: 'ReturnReceipt', action: 'init' }));
|
||||
log.info('Mount');
|
||||
|
||||
// Persisting a signal store slice
|
||||
export const FeatureStore = signalStore(
|
||||
withState(initState),
|
||||
withStorage('return:filters', LocalStorageProvider),
|
||||
);
|
||||
|
||||
// Fetch config value safely
|
||||
const apiBase = inject(Config).get('api.baseUrl', z.string().url());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Let me know if any area (e.g. auth flow, NgRx usage, Swagger generation details) needs deeper coverage and I can extend this file.
|
||||
189
.github/prompts/plan.prompt.md
vendored
189
.github/prompts/plan.prompt.md
vendored
@@ -1,189 +0,0 @@
|
||||
---
|
||||
mode: agent
|
||||
tools: ['edit', 'search', 'usages', 'vscodeAPI', 'problems', 'changes', 'fetch', 'githubRepo', 'Nx Mcp Server', 'context7']
|
||||
description: Plan Mode - Research and create a detailed implementation plan before making any changes.
|
||||
model: Gemini 2.5 Pro (copilot)
|
||||
---
|
||||
|
||||
# Plan Mode
|
||||
|
||||
You are now operating in **Plan Mode** - a research and planning phase that ensures thorough analysis before implementation. Plan mode is **ALWAYS ACTIVE** when using this prompt. You must follow these strict guidelines for every request:
|
||||
|
||||
## Phase 1: Research & Analysis (MANDATORY)
|
||||
|
||||
### ALLOWED Operations:
|
||||
|
||||
- ✅ Read files using Read, Glob, Grep tools
|
||||
- ✅ Search documentation and codebases
|
||||
- ✅ Analyze existing patterns and structures
|
||||
- ✅ Use WebFetch for documentation research
|
||||
- ✅ List and explore project structure
|
||||
- ✅ Use Nx/Angular/Context7 MCP tools for workspace analysis
|
||||
- ✅ Review dependencies and configurations
|
||||
|
||||
### FORBIDDEN Operations:
|
||||
|
||||
- ❌ **NEVER** create, edit, or modify any files
|
||||
- ❌ **NEVER** run commands that change system state
|
||||
- ❌ **NEVER** make commits or push changes
|
||||
- ❌ **NEVER** install packages or modify configurations
|
||||
- ❌ **NEVER** run build/test commands during planning
|
||||
|
||||
## Phase 2: Plan Presentation (REQUIRED FORMAT)
|
||||
|
||||
After thorough research, present your plan using this exact structure:
|
||||
|
||||
```markdown
|
||||
## 📋 Implementation Plan
|
||||
|
||||
### 🎯 Objective
|
||||
|
||||
[Clear statement of what will be accomplished]
|
||||
|
||||
### 🔍 Research Summary
|
||||
|
||||
- **Current State**: [What exists now]
|
||||
- **Requirements**: [What needs to be built/changed]
|
||||
- **Constraints**: [Limitations and considerations]
|
||||
|
||||
### 📁 Files to be Modified/Created
|
||||
|
||||
1. **File**: `path/to/file.ts`
|
||||
|
||||
- **Action**: Create/Modify/Delete
|
||||
- **Purpose**: [Why this file needs changes]
|
||||
- **Key Changes**: [Specific modifications planned]
|
||||
|
||||
2. **File**: `path/to/another-file.ts`
|
||||
- **Action**: Create/Modify/Delete
|
||||
- **Purpose**: [Why this file needs changes]
|
||||
- **Key Changes**: [Specific modifications planned]
|
||||
|
||||
### 🏗️ Implementation Steps
|
||||
|
||||
1. **Step 1**: [Detailed description]
|
||||
|
||||
- Files affected: `file1.ts`, `file2.ts`
|
||||
- Rationale: [Why this step is necessary]
|
||||
|
||||
2. **Step 2**: [Detailed description]
|
||||
|
||||
- Files affected: `file3.ts`
|
||||
- Rationale: [Why this step is necessary]
|
||||
|
||||
3. **Step N**: [Continue numbering...]
|
||||
|
||||
### ⚠️ Risks & Considerations
|
||||
|
||||
- **Risk 1**: [Potential issue and mitigation]
|
||||
- **Risk 2**: [Potential issue and mitigation]
|
||||
|
||||
### 🧪 Testing Strategy
|
||||
|
||||
- [How the changes will be tested]
|
||||
- [Specific test files or approaches]
|
||||
|
||||
### 📚 Architecture Decisions
|
||||
|
||||
- **Pattern Used**: [Which architectural pattern will be followed]
|
||||
- **Libraries/Dependencies**: [What will be used and why]
|
||||
- **Integration Points**: [How this fits with existing code]
|
||||
|
||||
### ✅ Success Criteria
|
||||
|
||||
- [ ] Criterion 1
|
||||
- [ ] Criterion 2
|
||||
- [ ] All tests pass
|
||||
- [ ] No lint errors
|
||||
```
|
||||
|
||||
## Phase 3: Await Approval
|
||||
|
||||
After presenting the plan:
|
||||
|
||||
1. **STOP** all implementation activities
|
||||
2. **WAIT** for explicit user approval
|
||||
3. **DO NOT** proceed with any file changes
|
||||
4. **RESPOND** to questions or plan modifications
|
||||
5. **EXIT PLAN MODE** only when user explicitly says "execute", "implement", "go ahead", "approved", or similar approval language
|
||||
|
||||
## Phase 4: Implementation (After Exiting Plan Mode)
|
||||
|
||||
Once the user explicitly approves and you exit plan mode:
|
||||
|
||||
1. **PLAN MODE IS NOW DISABLED** - you can proceed with normal implementation
|
||||
2. Use TodoWrite to create implementation todos
|
||||
3. Follow the plan step-by-step
|
||||
4. Update todos as you progress
|
||||
5. Run tests and lint checks as specified
|
||||
6. Provide progress updates
|
||||
|
||||
## Key Behavioral Rules
|
||||
|
||||
### Research Thoroughly
|
||||
|
||||
- Spend significant time understanding the codebase
|
||||
- Look for existing patterns to follow
|
||||
- Identify all dependencies and integration points
|
||||
- Consider edge cases and error scenarios
|
||||
|
||||
### Be Comprehensive
|
||||
|
||||
- Plans should be detailed enough for another developer to implement
|
||||
- Include all necessary file changes
|
||||
- Consider testing, documentation, and deployment
|
||||
- Address potential conflicts or breaking changes
|
||||
|
||||
### Show Your Work
|
||||
|
||||
- Explain reasoning behind architectural decisions
|
||||
- Reference existing code patterns when applicable
|
||||
- Cite documentation or best practices
|
||||
- Provide alternatives when multiple approaches exist
|
||||
|
||||
### Safety First
|
||||
|
||||
- Never make changes during planning phase
|
||||
- Always wait for explicit approval
|
||||
- Flag potentially risky changes
|
||||
- Suggest incremental implementation when complex
|
||||
|
||||
## Example Interactions
|
||||
|
||||
### Good Plan Mode Behavior:
|
||||
|
||||
```
|
||||
User: "Add a dark mode toggle to the settings page"
|
||||
Assistant: I'll research the current theming system and create a comprehensive plan for implementing dark mode.
|
||||
|
||||
[Extensive research using Read, Grep, Glob tools]
|
||||
|
||||
## 📋 Implementation Plan
|
||||
[Follows complete format above]
|
||||
|
||||
Ready to proceed? Please approve this plan before I begin implementation.
|
||||
```
|
||||
|
||||
### What NOT to do:
|
||||
|
||||
```
|
||||
User: "Add a dark mode toggle"
|
||||
Assistant: I'll add that right away!
|
||||
[Immediately starts editing files - WRONG!]
|
||||
```
|
||||
|
||||
# <<<<<<< HEAD
|
||||
|
||||
## Integration with Existing Copilot Instructions
|
||||
|
||||
This plan mode respects all existing project patterns:
|
||||
|
||||
- Follows Angular + Nx workspace conventions
|
||||
- Uses existing import path aliases
|
||||
- Respects testing strategy (Jest/Vitest)
|
||||
- Follows NgRx Signals patterns
|
||||
- Adheres to logging and configuration patterns
|
||||
- Maintains library conventions and file naming
|
||||
|
||||
> > > > > > > develop
|
||||
> > > > > > > Remember: **RESEARCH FIRST, PLAN THOROUGHLY, WAIT FOR APPROVAL, THEN IMPLEMENT**
|
||||
182
.github/review-instructions.md
vendored
182
.github/review-instructions.md
vendored
@@ -1,182 +0,0 @@
|
||||
# Code Review Instructions
|
||||
|
||||
## Summary
|
||||
|
||||
When conducting a code review, follow these steps to ensure a thorough and constructive process.
|
||||
**Ensure that all review guidelines are followed. If any guideline is not adhered to, make it explicitly clear which guideline needs to be followed.**
|
||||
|
||||
## Review Process
|
||||
|
||||
1. 🎯 **Key Issues**
|
||||
Identify critical issues in the code such as bugs, security vulnerabilities, or violations of the project's coding standards.
|
||||
_Include specific links to files and line numbers (e.g., file.js#L10) where applicable._
|
||||
|
||||
2. 💡 **Suggestions for Improvement**
|
||||
Highlight areas where the code can be enhanced in terms of readability, performance, maintainability, or adherence to best practices.
|
||||
_Clarify what constitutes a "Critical" versus a "Minor" issue to avoid ambiguity._
|
||||
|
||||
3. ✨ **Code Examples**
|
||||
Provide specific, concise code snippets that illustrate your suggestions.
|
||||
_Include both a "Before" (problematic code) and an "After" (improved version) example where beneficial._
|
||||
|
||||
4. 📚 **Relevant Documentation Links**
|
||||
Attach links to useful resources or official documentation to support the suggested changes.
|
||||
_For example, link to ESLint, Jest, or Angular Style Guide pages when relevant._
|
||||
|
||||
## Tone and Feedback
|
||||
|
||||
- Be constructive and supportive.
|
||||
Frame suggestions as opportunities for growth rather than criticism.
|
||||
- Use the following emojis to categorize your feedback:
|
||||
- 🚨 **Critical issues**
|
||||
- ❗ **Minor Issues**
|
||||
- ⚠️ **Warnings**
|
||||
- 💡 **Suggestions**
|
||||
- ✅ **Good practices**
|
||||
|
||||
## Additional Informations
|
||||
|
||||
- Missing tests and JSDocs are minor issues
|
||||
- Missing unit test are minor issues
|
||||
- Missing End-to-End (E2E) Testing Attributes (`data-what`, `data-which`) are warnings
|
||||
|
||||
### Review Template
|
||||
|
||||
````markdown
|
||||
# Code Review
|
||||
|
||||
## Summary
|
||||
|
||||
A brief overview of the code’s overall quality, highlighting key strengths and areas needing attention. This sets the stage for the detailed feedback below.
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Critical Issues
|
||||
|
||||
High-priority issues that must be addressed immediately due to their potential to severely impact functionality, performance, or security.
|
||||
|
||||
### 1. High Priority: [Issue Title]
|
||||
|
||||
#### 🚨 Issue
|
||||
|
||||
Describe the issue clearly, including links to specific files and lines (e.g., file.js#L10). Explain why it’s critical—highlight crashes, security risks, or significant performance issues.
|
||||
|
||||
#### 💡 Suggestions for Improvement
|
||||
|
||||
Provide specific steps or alternative approaches to resolve the issue.
|
||||
|
||||
#### ✨ Code Example
|
||||
|
||||
**Current**: [file](file.js#L10) Problematic code with path to the file and line of the code
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
**Improvement**: Improved version
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
#### 📚 Relevant Documentation
|
||||
|
||||
Include URLs for further research (e.g., [Jest Documentation](https://jestjs.io/docs/getting-started)).
|
||||
|
||||
---
|
||||
|
||||
## ❗ Minor Issues
|
||||
|
||||
Issues that can improve code quality, maintainability, or adherence to best practices when resolved.
|
||||
|
||||
### 1. Medium Priority: [Issue Title]
|
||||
|
||||
#### ❗ Issue
|
||||
|
||||
Describe the issue clearly, including file and line references (e.g., file.js#L10). Explain the impact on the project.
|
||||
|
||||
#### 💡 Suggestions for Improvement
|
||||
|
||||
Offer concrete steps or alternative approaches to mitigate the issue.
|
||||
|
||||
#### ✨ Code Example
|
||||
|
||||
**Current**: [file](file.js#L10) Problematic code with path to the file and line of the code
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
**Improvement**: Improved version
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
#### 📚 Relevant Documentation
|
||||
|
||||
Provide links to further resources.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Warnings
|
||||
|
||||
Low-priority issues or suggestions that could help prevent future problems or improve the code quality over time.
|
||||
|
||||
### 1. Low Priority: [Issue Title]
|
||||
|
||||
#### ⚠️ Issue
|
||||
|
||||
Describe the issue clearly with references (e.g., file.js#L10). Explain the potential impact if left unaddressed.
|
||||
|
||||
#### 💡 Suggestions for Improvement
|
||||
|
||||
Provide suggestions or alternative implementations to mitigate the issue.
|
||||
|
||||
#### ✨ Code Example
|
||||
|
||||
**Current**: [file](file.js#L10) Problematic code with path to the file and line of the code
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
**Improvement**: Improved version
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
#### 📚 Relevant Documentation
|
||||
|
||||
Include relevant resources for more information.
|
||||
|
||||
---
|
||||
|
||||
## 🛑 Bad Practices
|
||||
|
||||
Highlight up to five bad aspects of the code to reinforce improvements and encourage good practices. Use different funny emoji at the beginning of each bad practice.
|
||||
|
||||
- Emoji **Bad Practice 1**:
|
||||
Describe a specific weakness (e.g., clear code structure) with an example reference (e.g., file.js#L20). Explain why it’s bad.
|
||||
- Emoji **Bad Practice 2**:
|
||||
Outline another negative feature (e.g., effective error handling) with a snippet reference.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Good Practices
|
||||
|
||||
Highlight up to five positive aspects of the code to reinforce well-implemented patterns and encourage good practices. Use different funny emoji at the beginning of each good practice.
|
||||
|
||||
- Emoji **Good Practice 1**:
|
||||
Describe a specific strength (e.g., clear code structure) with an example reference (e.g., file.js#L20). Explain why it’s commendable.
|
||||
- Emoji **Good Practice 2**:
|
||||
Outline another positive feature (e.g., effective error handling) with a snippet reference.
|
||||
|
||||
---
|
||||
|
||||
## 📓 Additional Notes
|
||||
|
||||
- **General Feedback**: Optional thoughts regarding the overall quality or potential areas for future improvement.
|
||||
- **Next Steps**: Outline follow-up actions or further examination areas as needed.
|
||||
````
|
||||
73
.github/testing-instructions.md
vendored
73
.github/testing-instructions.md
vendored
@@ -1,73 +0,0 @@
|
||||
# Testing Instructions
|
||||
|
||||
## Framework and Tools
|
||||
|
||||
- **Vitest** is the recommended testing framework.
|
||||
[Vitest Documentation (latest)](https://context7.com/vitest-dev/vitest/llms.txt?topic=getting+started)
|
||||
- **Jest** and **Spectator** are **deprecated**.
|
||||
Do not use them for new tests. Existing tests should be migrated to Vitest where possible.
|
||||
|
||||
## Guidelines
|
||||
|
||||
1. **Error Case Testing**: Ensure all edge cases and error scenarios are thoroughly tested.
|
||||
2. **Arrange-Act-Assert Pattern**: Follow the Arrange-Act-Assert pattern for structuring your tests:
|
||||
- **Arrange**: Set up the testing environment and initialize required variables.
|
||||
- **Act**: Execute the functionality being tested.
|
||||
- **Assert**: Verify the expected outcomes.
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Write clear and descriptive test names.
|
||||
- Ensure tests are isolated and do not depend on each other.
|
||||
- Mock external dependencies to avoid side effects.
|
||||
- Aim for high code coverage without compromising test quality.
|
||||
|
||||
## Example Test Structure
|
||||
|
||||
```typescript
|
||||
// Example using Vitest (Jest and Spectator are deprecated)
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { render } from '@testing-library/angular';
|
||||
import { MyComponent } from './my-component.component';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
let component: MyComponent;
|
||||
|
||||
beforeEach(async () => {
|
||||
const { fixture } = await render(MyComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should display the correct title', async () => {
|
||||
// Arrange
|
||||
const expectedTitle = 'Hello World';
|
||||
|
||||
// Act
|
||||
component.title = expectedTitle;
|
||||
// If using Angular, trigger change detection:
|
||||
// fixture.detectChanges();
|
||||
|
||||
// Assert
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('h1')?.textContent).toBe(expectedTitle);
|
||||
});
|
||||
|
||||
it('should handle error cases gracefully', () => {
|
||||
// Arrange
|
||||
const invalidInput = null;
|
||||
|
||||
// Act
|
||||
component.input = invalidInput;
|
||||
|
||||
// Assert
|
||||
expect(() => component.processInput()).toThrowError('Invalid input');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Vitest Documentation (latest)](https://context7.com/vitest-dev/vitest/llms.txt?topic=getting+started)
|
||||
- [Vitest Official Guide](https://vitest.dev/guide/)
|
||||
- [Testing Library for Angular](https://testing-library.com/docs/angular-testing-library/intro/)
|
||||
- **Jest** and **Spectator** documentation are deprecated
|
||||
127
.gitignore
vendored
127
.gitignore
vendored
@@ -1,78 +1,49 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
.matomo
|
||||
junit.xml
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
|
||||
/
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events.json
|
||||
speed-measure-plugin.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.angular/cache
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/testresults
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
libs/swagger/src/lib/*
|
||||
*storybook.log
|
||||
|
||||
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
.angular
|
||||
.claude
|
||||
|
||||
|
||||
storybook-static
|
||||
|
||||
.cursor\rules\nx-rules.mdc
|
||||
.github\instructions\nx.instructions.md
|
||||
.cursor/rules/nx-rules.mdc
|
||||
.github/instructions/nx.instructions.md
|
||||
|
||||
vite.config.*.timestamp*
|
||||
vitest.config.*.timestamp*
|
||||
|
||||
.mcp.json
|
||||
.memory.json
|
||||
|
||||
nx.instructions.md
|
||||
CLAUDE.md
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events.json
|
||||
speed-measure-plugin.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
.prettierrc
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/testresults
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
libs/swagger/src/lib/*
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
npx lint-staged
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"*.ts": "npx eslint --fix --config eslint.config.js",
|
||||
"*.tsx": "npx eslint --fix --config eslint.config.js",
|
||||
"*.js": "npx eslint --fix --config eslint.config.js",
|
||||
"*.jsx": "npx eslint --fix --config eslint.config.js",
|
||||
"*.html": "npx eslint --fix --config eslint.config.js"
|
||||
}
|
||||
4
.npmrc
4
.npmrc
@@ -1 +1,3 @@
|
||||
@paragondata:registry=https://npm.pkg.github.com
|
||||
@isa:registry=https://pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/registry/
|
||||
@cmf:registry=https://pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/registry/
|
||||
always-auth=true
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Add files here to ignore them from prettier formatting
|
||||
|
||||
/dist
|
||||
/coverage
|
||||
/.nx/cache
|
||||
/.nx/workspace-data
|
||||
/node_modules
|
||||
.angular
|
||||
.vscode
|
||||
/helmvalues
|
||||
/apps/swagger
|
||||
/ng-swagger-gen
|
||||
/apps/isa-app/src/assets
|
||||
|
||||
*.json
|
||||
*.yml
|
||||
|
||||
37
.prettierrc
37
.prettierrc
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"bracketSpacing": true,
|
||||
"printWidth": 80,
|
||||
"endOfLine": "auto",
|
||||
"arrowParens": "always",
|
||||
"quoteProps": "consistent",
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.html",
|
||||
"options": {
|
||||
"parser": "html"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.component.html",
|
||||
"options": {
|
||||
"parser": "angular"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.scss",
|
||||
"options": {
|
||||
"singleQuote": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.json",
|
||||
"options": {
|
||||
"printWidth": 80
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
5
.prettierrc.json
Normal file
5
.prettierrc.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"printWidth": 140
|
||||
}
|
||||
|
||||
17
.vscode/extensions.json
vendored
17
.vscode/extensions.json
vendored
@@ -1,11 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"johnpapa.angular2",
|
||||
"esbenp.prettier-vscode",
|
||||
"angular.ng-template",
|
||||
"nrwl.angular-console",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"editorconfig.editorconfig"
|
||||
]
|
||||
}
|
||||
"recommendations": [
|
||||
"johnpapa.angular2",
|
||||
"esbenp.prettier-vscode",
|
||||
"angular.ng-template",
|
||||
"eg2.vscode-npm-script"
|
||||
]
|
||||
}
|
||||
35
.vscode/launch.json
vendored
35
.vscode/launch.json
vendored
@@ -1,15 +1,22 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "ISA-App Chrome",
|
||||
"url": "https://localhost:4200",
|
||||
"webRoot": "${workspaceFolder}",
|
||||
}
|
||||
]
|
||||
}
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.11.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome",
|
||||
"url": "https://192.168.2.112:4200",
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"sourceMaps": true,
|
||||
"sourceMapPathOverrides": {
|
||||
"/./*": "${webRoot}/*",
|
||||
"/src/*": "${webRoot}/*",
|
||||
"/*": "*",
|
||||
"/./~/*": "${webRoot}/node_modules/*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
115
.vscode/settings.json
vendored
115
.vscode/settings.json
vendored
@@ -1,100 +1,15 @@
|
||||
{
|
||||
"editor.accessibilitySupport": "off",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"exportall.config.exclude": [".test.", ".spec.", ".stories."],
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"eslint.validate": [
|
||||
"json"
|
||||
],
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.formatOnSave": false
|
||||
},
|
||||
"exportall.config.folderListener": [
|
||||
"/libs/oms/data-access/src/lib/models",
|
||||
"/libs/oms/data-access/src/lib/schemas",
|
||||
"/libs/catalogue/data-access/src/lib/models",
|
||||
"/libs/common/data-access/src/lib/models",
|
||||
"/libs/common/data-access/src/lib/error",
|
||||
"/libs/oms/data-access/src/lib/errors/return-process"
|
||||
],
|
||||
"github.copilot.chat.commitMessageGeneration.instructions": [
|
||||
{
|
||||
"file": ".github/commit-instructions.md"
|
||||
}
|
||||
],
|
||||
"github.copilot.chat.codeGeneration.instructions": [
|
||||
{
|
||||
"file": ".vscode/llms/angular.txt"
|
||||
},
|
||||
{
|
||||
"file": "docs/tech-stack.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/code-style.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/project-structure.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/state-management.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/testing.md"
|
||||
}
|
||||
],
|
||||
"github.copilot.chat.testGeneration.instructions": [
|
||||
{
|
||||
"file": ".github/testing-instructions.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/tech-stack.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/code-style.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/testing.md"
|
||||
}
|
||||
],
|
||||
"github.copilot.chat.reviewSelection.instructions": [
|
||||
{
|
||||
"file": ".github/copilot-instructions.md"
|
||||
},
|
||||
{
|
||||
"file": ".github/review-instructions.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/tech-stack.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/code-style.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/project-structure.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/state-management.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/testing.md"
|
||||
}
|
||||
],
|
||||
"nxConsole.generateAiAgentRules": true,
|
||||
"chat.mcp.discovery.enabled": {
|
||||
"claude-desktop": true,
|
||||
"windsurf": true,
|
||||
"cursor-global": true,
|
||||
"cursor-workspace": true
|
||||
},
|
||||
"chat.mcp.access": "all"
|
||||
}
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"editor.formatOnSave": true,
|
||||
"typescriptHero.imports.insertSemicolons": false,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"css.validate": false,
|
||||
"less.validate": false,
|
||||
"scss.validate": false
|
||||
}
|
||||
|
||||
148
CLAUDE.md
148
CLAUDE.md
@@ -1,148 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is an Angular monorepo managed by Nx. The main application is `isa-app`, which appears to be an inventory and returns management system for retail/e-commerce.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Monorepo Structure
|
||||
- **apps/isa-app**: Main Angular application
|
||||
- **libs/**: Reusable libraries organized by domain and type
|
||||
- **core/**: Core utilities (config, logging, storage, tabs)
|
||||
- **common/**: Shared utilities (data-access, decorators, print)
|
||||
- **ui/**: UI component libraries (buttons, dialogs, inputs, etc.)
|
||||
- **shared/**: Shared domain components (filter, scanner, product components)
|
||||
- **oms/**: Order Management System features and utilities
|
||||
- **remission/**: Remission/returns management features
|
||||
- **catalogue/**: Product catalogue functionality
|
||||
- **utils/**: General utilities (validation, scroll position, parsing)
|
||||
- **icons/**: Icon library
|
||||
- **generated/swagger/**: Auto-generated API client code from OpenAPI specs
|
||||
|
||||
### Key Architectural Patterns
|
||||
- **Standalone Components**: Project uses Angular standalone components
|
||||
- **Feature Libraries**: Domain features organized as separate libraries (e.g., `oms-feature-return-search`)
|
||||
- **Data Access Layer**: Separate data-access libraries for each domain (e.g., `oms-data-access`, `remission-data-access`)
|
||||
- **Shared UI Components**: Reusable UI components in `libs/ui/`
|
||||
- **Generated API Clients**: Swagger/OpenAPI clients auto-generated in `generated/swagger/`
|
||||
|
||||
## Common Development Commands
|
||||
|
||||
### Build Commands
|
||||
```bash
|
||||
# Build the main application (development)
|
||||
npx nx build isa-app --configuration=development
|
||||
|
||||
# Build for production
|
||||
npx nx build isa-app --configuration=production
|
||||
|
||||
# Serve the application with SSL
|
||||
npx nx serve isa-app --ssl
|
||||
```
|
||||
|
||||
### Testing Commands
|
||||
```bash
|
||||
# Run tests for a specific library (always use --skip-cache)
|
||||
npx nx run <project-name>:test --skip-cache
|
||||
# Example: npx nx run remission-data-access:test --skip-cache
|
||||
|
||||
# Run tests for all libraries except the main app
|
||||
npx nx run-many -t test --exclude isa-app --skip-cache
|
||||
|
||||
# Run a single test file
|
||||
npx nx run <project-name>:test --testFile=<path-to-test-file> --skip-cache
|
||||
|
||||
# Run tests with coverage
|
||||
npx nx run <project-name>:test --code-coverage --skip-cache
|
||||
|
||||
# Run tests in watch mode
|
||||
npx nx run <project-name>:test --watch
|
||||
```
|
||||
|
||||
### Linting Commands
|
||||
```bash
|
||||
# Lint a specific project
|
||||
npx nx lint <project-name>
|
||||
# Example: npx nx lint remission-data-access
|
||||
|
||||
# Run linting for all projects
|
||||
npx nx run-many -t lint
|
||||
```
|
||||
|
||||
### Other Useful Commands
|
||||
```bash
|
||||
# Generate Swagger API clients
|
||||
npm run generate:swagger
|
||||
|
||||
# Start Storybook
|
||||
npx nx run isa-app:storybook
|
||||
|
||||
# Format code with Prettier
|
||||
npm run prettier
|
||||
|
||||
# List all projects in the workspace
|
||||
npx nx list
|
||||
|
||||
# Show project dependencies graph
|
||||
npx nx graph
|
||||
|
||||
# Run affected tests (based on git changes)
|
||||
npx nx affected:test
|
||||
```
|
||||
|
||||
## Testing Framework
|
||||
|
||||
### Current Setup
|
||||
- **Jest**: Primary test runner for existing libraries
|
||||
- **Vitest**: Being adopted for new libraries (migration in progress)
|
||||
- **Testing Utilities**:
|
||||
- **Angular Testing Utilities** (TestBed, ComponentFixture): Use for new tests
|
||||
- **Spectator**: Legacy testing utility for existing tests
|
||||
- **ng-mocks**: For advanced mocking scenarios
|
||||
|
||||
### Test File Requirements
|
||||
- Test files must end with `.spec.ts`
|
||||
- Use AAA pattern (Arrange-Act-Assert)
|
||||
- Include E2E testing attributes (`data-what`, `data-which`) in HTML templates
|
||||
- Mock external dependencies and child components
|
||||
|
||||
## State Management
|
||||
- **NgRx**: Store, Effects, Entity, Component Store, Signals
|
||||
- **RxJS**: For reactive programming patterns
|
||||
|
||||
## Styling
|
||||
- **Tailwind CSS**: Primary styling framework with custom configuration
|
||||
- **SCSS**: For component-specific styles
|
||||
- **Custom Tailwind plugins**: For buttons, inputs, menus, typography
|
||||
|
||||
## API Integration
|
||||
- **Generated Swagger Clients**: Auto-generated TypeScript clients from OpenAPI specs
|
||||
- **Available APIs**: availability, cat-search, checkout, crm, eis, inventory, isa, oms, print, wws
|
||||
|
||||
## Build Configuration
|
||||
- **Angular 20.1.2**: Latest Angular version
|
||||
- **TypeScript 5.8.3**: For type safety
|
||||
- **Node.js >= 22.0.0**: Required Node version
|
||||
- **npm >= 10.0.0**: Required npm version
|
||||
|
||||
## Important Conventions
|
||||
- **Component Prefix**: Each library has its own prefix (e.g., `remi` for remission, `oms` for OMS)
|
||||
- **Standalone Components**: All new components should be standalone
|
||||
- **Path Aliases**: Use TypeScript path aliases defined in `tsconfig.base.json` (e.g., `@isa/core/config`)
|
||||
- **Project Names**: Can be found in each library's `project.json` file
|
||||
|
||||
## Development Workflow Tips
|
||||
- Always use `npx nx run` pattern for executing tasks
|
||||
- Include `--skip-cache` flag when running tests to ensure fresh results
|
||||
- Use Nx's affected commands to optimize CI/CD pipelines
|
||||
- Project graph visualization helps understand dependencies: `npx nx graph`
|
||||
|
||||
## Development Notes
|
||||
- Use start target to start the application. Only one project can be started: isa-app
|
||||
- Make sure to have a look at @docs/guidelines/testing.md before writing tests
|
||||
- Make sure to add e2e attributes to the html. Those are important for my colleagues writen e2e tests
|
||||
- Guide for the e2e testing attributes can be found in the testing.md
|
||||
- When reviewing code follow the instructions @.github/review-instructions.md
|
||||
@@ -1,5 +1,5 @@
|
||||
#stage 1
|
||||
FROM node:22 as base
|
||||
FROM node:14 as base
|
||||
ARG IS_PRODUCTION=false
|
||||
ARG SEMVERSION=1.0.0
|
||||
ARG BuildUniqueID
|
||||
@@ -8,7 +8,7 @@ WORKDIR /app
|
||||
COPY . .
|
||||
RUN umask 0022
|
||||
RUN npm version ${SEMVERSION}
|
||||
RUN npm ci --foreground-scripts
|
||||
RUN npm install --always-auth=false
|
||||
RUN if [ "${IS_PRODUCTION}" = "true" ] ; then npm run-script build-prod ; else npm run-script build ; fi
|
||||
|
||||
# stage final
|
||||
@@ -24,6 +24,6 @@ ARG BuildUniqueID
|
||||
LABEL build.uniqueid="${BuildUniqueID:-1}"
|
||||
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -q -O /tmp/chrome.deb && apt update && apt install -y /tmp/chrome.deb
|
||||
# ignore exitcode, sonst gibts keinen container
|
||||
RUN npm run ci || true
|
||||
RUN npm test || true
|
||||
ENTRYPOINT [ "/bin/sleep", "60000" ]
|
||||
|
||||
|
||||
4
TASKS.md
Normal file
4
TASKS.md
Normal file
@@ -0,0 +1,4 @@
|
||||
- Neue Icon Module (z.B. mit SVG sprites)
|
||||
- Breadcrumb Navigation (Neu)
|
||||
- Remissions Produkt Liste (Refactoring / Neu)
|
||||
- Angular Version (Upgrade)
|
||||
3850
angular.json
Normal file
3850
angular.json
Normal file
File diff suppressed because it is too large
Load Diff
25
apps/adapter/scan/README.md
Normal file
25
apps/adapter/scan/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Scan
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project scan` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project scan`.
|
||||
|
||||
> Note: Don't forget to add `--project scan` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build scan` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build scan`, go to the dist folder `cd dist/scan` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test scan` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
41
apps/adapter/scan/karma.conf.js
Normal file
41
apps/adapter/scan/karma.conf.js
Normal file
@@ -0,0 +1,41 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true, // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, '../../../coverage/adapter/scan'),
|
||||
subdir: '.',
|
||||
reporters: [{ type: 'html' }, { type: 'text-summary' }],
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
};
|
||||
7
apps/adapter/scan/ng-package.json
Normal file
7
apps/adapter/scan/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/adapter/scan",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
11
apps/adapter/scan/package.json
Normal file
11
apps/adapter/scan/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@adapter/scan",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^12.2.0",
|
||||
"@angular/core": "^12.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
}
|
||||
46
apps/adapter/scan/src/lib/dev.scan-adapter.ts
Normal file
46
apps/adapter/scan/src/lib/dev.scan-adapter.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PromptModalData, UiModalService, UiPromptModalComponent } from '@ui/modal';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ScanAdapter } from './scan-adapter';
|
||||
|
||||
@Injectable()
|
||||
export class DevScanAdapter implements ScanAdapter {
|
||||
constructor(private _modal: UiModalService) {}
|
||||
|
||||
getName(): string {
|
||||
return 'Dev Scanner';
|
||||
}
|
||||
|
||||
isPrimary(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
isReady(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
scan(): Observable<string> {
|
||||
return new Observable((observer) => {
|
||||
const modalRef = this._modal.open({
|
||||
content: UiPromptModalComponent,
|
||||
title: 'Scannen',
|
||||
data: {
|
||||
message: 'Diese Eingabemaske dient nur zu Entwicklungs und Testzwecken.',
|
||||
placeholder: 'Scan Code',
|
||||
confirmText: 'weiter',
|
||||
cancelText: 'abbrechen',
|
||||
} as PromptModalData,
|
||||
});
|
||||
|
||||
const sub = modalRef.afterClosed$.subscribe((result) => {
|
||||
observer.next(result.data);
|
||||
observer.complete();
|
||||
});
|
||||
|
||||
return () => {
|
||||
modalRef.close();
|
||||
sub.unsubscribe();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
32
apps/adapter/scan/src/lib/native.scan-adapter.ts
Normal file
32
apps/adapter/scan/src/lib/native.scan-adapter.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NativeContainerService } from 'native-container';
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter, map, take } from 'rxjs/operators';
|
||||
import { ScanAdapter } from './scan-adapter';
|
||||
|
||||
@Injectable()
|
||||
export class NativeScanAdapter implements ScanAdapter {
|
||||
constructor(private readonly nativeContainerService: NativeContainerService) {}
|
||||
|
||||
getName(): string {
|
||||
return 'Native Scanner';
|
||||
}
|
||||
|
||||
isPrimary(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
isReady(): boolean {
|
||||
// TODO: Fix Login Keycard Dauerschleife
|
||||
return this.nativeContainerService.isUiWebview().isNative;
|
||||
// return false;
|
||||
}
|
||||
|
||||
scan(): Observable<string> {
|
||||
return this.nativeContainerService.openScanner('scanBook').pipe(
|
||||
filter((result) => result.status === 'SUCCESS'),
|
||||
map((result) => result.data),
|
||||
take(1)
|
||||
);
|
||||
}
|
||||
}
|
||||
11
apps/adapter/scan/src/lib/scan-adapter.ts
Normal file
11
apps/adapter/scan/src/lib/scan-adapter.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export interface ScanAdapter {
|
||||
getName(): string;
|
||||
|
||||
isPrimary(): boolean;
|
||||
|
||||
isReady(): boolean;
|
||||
|
||||
scan(): Observable<string>;
|
||||
}
|
||||
26
apps/adapter/scan/src/lib/scan.module.ts
Normal file
26
apps/adapter/scan/src/lib/scan.module.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { DevScanAdapter } from './dev.scan-adapter';
|
||||
import { NativeScanAdapter } from './native.scan-adapter';
|
||||
import { ScanditModalModule } from './scandit-modal';
|
||||
import { ScanditScanAdapter } from './scandit.scan-adapter';
|
||||
import { SCAN_ADAPTER } from './tokens';
|
||||
import { ZxingModalModule } from './zxing-modal';
|
||||
import { ZxingScanAdapter } from './zxing.scan-adapter';
|
||||
|
||||
@NgModule({
|
||||
imports: [ScanditModalModule, ZxingModalModule],
|
||||
})
|
||||
export class ScanAdapterModule {
|
||||
static forRoot(dev?: boolean) {
|
||||
return {
|
||||
ngModule: ScanAdapterModule,
|
||||
providers: [
|
||||
{ provide: SCAN_ADAPTER, useClass: NativeScanAdapter, multi: true },
|
||||
// { provide: SCAN_ADAPTER, useClass: ScanditScanAdapter, multi: true },
|
||||
{ provide: SCAN_ADAPTER, useClass: ZxingScanAdapter, multi: true },
|
||||
],
|
||||
// Use for testing:
|
||||
// providers: [{ provide: SCAN_ADAPTER, useClass: dev ? DevScanAdapter : NativeScanAdapter, multi: true }],
|
||||
};
|
||||
}
|
||||
}
|
||||
30
apps/adapter/scan/src/lib/scan.service.ts
Normal file
30
apps/adapter/scan/src/lib/scan.service.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { ScanAdapter } from './scan-adapter';
|
||||
import { SCAN_ADAPTER } from './tokens';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ScanAdapterService {
|
||||
constructor(@Inject(SCAN_ADAPTER) private readonly scanAdapters: ScanAdapter[]) {}
|
||||
|
||||
scanners() {
|
||||
return this.scanAdapters.filter((adapter) => adapter.isReady());
|
||||
}
|
||||
|
||||
scanner() {
|
||||
return this.scanners().find((scanner) => scanner.isPrimary()) || this.scanners().find(() => true);
|
||||
}
|
||||
|
||||
isReady() {
|
||||
return this.scanAdapters.some((adapter) => adapter.isReady());
|
||||
}
|
||||
|
||||
scan() {
|
||||
const primaryScanner = this.scanner();
|
||||
if (primaryScanner) {
|
||||
return primaryScanner.scan();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
2
apps/adapter/scan/src/lib/scandit-modal/index.ts
Normal file
2
apps/adapter/scan/src/lib/scandit-modal/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './scandit-modal.component';
|
||||
export * from './scandit-modal.module';
|
||||
@@ -0,0 +1 @@
|
||||
<div class="scanner-container" #scanContainer></div>
|
||||
@@ -0,0 +1,4 @@
|
||||
.scanner-container {
|
||||
@apply mt-8;
|
||||
max-height: calc(100vh - 10rem);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { Component, ChangeDetectionStrategy, ViewChild, ElementRef, AfterViewInit, NgZone, Inject, OnDestroy } from '@angular/core';
|
||||
import { UiMessageModalComponent, UiModalRef, UiModalService } from '@ui/modal';
|
||||
import { Barcode, BarcodePicker, ScanSettings } from 'scandit-sdk';
|
||||
|
||||
@Component({
|
||||
selector: 'scandit-modal',
|
||||
templateUrl: 'scandit-modal.component.html',
|
||||
styleUrls: ['scandit-modal.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ScanditModalComponent implements AfterViewInit, OnDestroy {
|
||||
private _barcodePicker: BarcodePicker;
|
||||
|
||||
@ViewChild('scanContainer', { read: ElementRef, static: true }) scanContainer: ElementRef;
|
||||
|
||||
constructor(private _modalRef: UiModalRef, private readonly _zone: NgZone, private readonly _modal: UiModalService) {}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this._zone.runOutsideAngular(() => {
|
||||
BarcodePicker.create(this.scanContainer.nativeElement, {
|
||||
playSoundOnScan: true,
|
||||
vibrateOnScan: true,
|
||||
})
|
||||
.then(async (picker) => {
|
||||
this._barcodePicker = picker;
|
||||
|
||||
var scanSettings = new ScanSettings({
|
||||
blurryRecognition: false,
|
||||
|
||||
enabledSymbologies: [
|
||||
Barcode.Symbology.EAN8,
|
||||
Barcode.Symbology.EAN13,
|
||||
Barcode.Symbology.UPCA,
|
||||
Barcode.Symbology.UPCE,
|
||||
Barcode.Symbology.CODE128,
|
||||
Barcode.Symbology.CODE39,
|
||||
Barcode.Symbology.CODE93,
|
||||
Barcode.Symbology.INTERLEAVED_2_OF_5,
|
||||
Barcode.Symbology.QR,
|
||||
],
|
||||
codeDuplicateFilter: 1000,
|
||||
});
|
||||
picker.applyScanSettings(scanSettings);
|
||||
|
||||
picker.on('scan', (barcode) => {
|
||||
this._zone.run(() => {
|
||||
if (barcode.barcodes.length) {
|
||||
this._modalRef.close(barcode.barcodes[0].data);
|
||||
} else if (barcode.texts.length) {
|
||||
this._modalRef.close(barcode.texts[0].value);
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
this._zone.run(() => {
|
||||
this.cancel();
|
||||
|
||||
this._modal
|
||||
.open({
|
||||
content: UiMessageModalComponent,
|
||||
data: {
|
||||
message: `
|
||||
Scanner kann nicht aktiviert werden.
|
||||
Bitte stellen Sie sicher, dass der zugriff auf die Kamera erlaubt ist.
|
||||
`,
|
||||
},
|
||||
})
|
||||
.afterClosed$.subscribe(() => {
|
||||
this.cancel();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this._modalRef.close();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._zone.runOutsideAngular(() => {
|
||||
this._barcodePicker?.destroy(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { ScanditModalComponent } from './scandit-modal.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
exports: [ScanditModalComponent],
|
||||
declarations: [ScanditModalComponent],
|
||||
})
|
||||
export class ScanditModalModule {}
|
||||
49
apps/adapter/scan/src/lib/scandit.scan-adapter.ts
Normal file
49
apps/adapter/scan/src/lib/scandit.scan-adapter.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ScanAdapter } from './scan-adapter';
|
||||
|
||||
import { configure } from 'scandit-sdk';
|
||||
import { ScanditModalComponent } from './scandit-modal';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { Config } from '@core/config';
|
||||
|
||||
@Injectable()
|
||||
export class ScanditScanAdapter implements ScanAdapter {
|
||||
private _isReady = false;
|
||||
|
||||
constructor(private readonly _modal: UiModalService, private readonly _config: Config) {
|
||||
this.configure();
|
||||
}
|
||||
|
||||
private async configure() {
|
||||
await configure(this._config.get('licence.scandit'), {
|
||||
engineLocation: '/assets/scandit/',
|
||||
});
|
||||
|
||||
this._isReady = true;
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return 'Scandit';
|
||||
}
|
||||
|
||||
isPrimary(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
isReady(): boolean {
|
||||
return this._isReady;
|
||||
}
|
||||
|
||||
scan(): Observable<string> {
|
||||
return this._modal
|
||||
.open({
|
||||
content: ScanditModalComponent,
|
||||
})
|
||||
.afterClosed$.pipe(
|
||||
map((result) => result.data),
|
||||
filter((result) => !!result)
|
||||
);
|
||||
}
|
||||
}
|
||||
2
apps/adapter/scan/src/lib/zxing-modal/index.ts
Normal file
2
apps/adapter/scan/src/lib/zxing-modal/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './zxing-modal.component';
|
||||
export * from './zxing-modal.module';
|
||||
@@ -0,0 +1,17 @@
|
||||
<!-- <select>
|
||||
<options [value]="dev.deviceId" *ngFor="let dev in devices$ | async">
|
||||
{{ dev.label }}
|
||||
</options>
|
||||
</select> -->
|
||||
|
||||
<div class="device-container">
|
||||
<button
|
||||
[class.selected]="(selectedDevice$ | async) === dev.deviceId"
|
||||
*ngFor="let dev of devices$ | async"
|
||||
(click)="setSelectedDevice(dev.deviceId)"
|
||||
>
|
||||
{{ dev.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<video class="scanner-container" #scanContainer></video>
|
||||
@@ -0,0 +1,17 @@
|
||||
.scanner-container {
|
||||
// @apply mt-8;
|
||||
max-height: calc(100vh - 10rem);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply bg-white text-brand px-3 py-2;
|
||||
|
||||
&.selected {
|
||||
@apply bg-brand text-white;
|
||||
}
|
||||
}
|
||||
|
||||
.device-container {
|
||||
@apply flex flex-row items-center justify-center my-4;
|
||||
}
|
||||
104
apps/adapter/scan/src/lib/zxing-modal/zxing-modal.component.ts
Normal file
104
apps/adapter/scan/src/lib/zxing-modal/zxing-modal.component.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { Component, ChangeDetectionStrategy, ViewChild, ElementRef, AfterViewInit, NgZone, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||
import { UiMessageModalComponent, UiModalRef, UiModalService } from '@ui/modal';
|
||||
import { BarcodeFormat, DecodeHintType, BrowserMultiFormatReader } from '@zxing/library';
|
||||
import { BehaviorSubject, from, ReplaySubject, Subject } from 'rxjs';
|
||||
import { filter, takeUntil, tap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'zxing-modal',
|
||||
templateUrl: 'zxing-modal.component.html',
|
||||
styleUrls: ['zxing-modal.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ZxingModalComponent implements OnInit, OnDestroy {
|
||||
private _reader: BrowserMultiFormatReader;
|
||||
|
||||
@ViewChild('scanContainer', { read: ElementRef, static: true }) scanContainer: ElementRef;
|
||||
|
||||
devices$ = new ReplaySubject<MediaDeviceInfo[]>();
|
||||
|
||||
selectedDevice$ = new BehaviorSubject<string>(null);
|
||||
|
||||
get selectedDevice() {
|
||||
return this.selectedDevice$.value;
|
||||
}
|
||||
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
constructor(private _modalRef: UiModalRef, private readonly _zone: NgZone, private readonly _modal: UiModalService) {}
|
||||
|
||||
async ngOnInit() {
|
||||
try {
|
||||
const mediaStream: MediaStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });
|
||||
mediaStream.getVideoTracks().forEach((track) => track.stop());
|
||||
|
||||
const hints = new Map<DecodeHintType, any>();
|
||||
|
||||
const formats = [
|
||||
BarcodeFormat.EAN_8,
|
||||
BarcodeFormat.EAN_13,
|
||||
BarcodeFormat.UPC_A,
|
||||
BarcodeFormat.UPC_E,
|
||||
BarcodeFormat.CODE_128,
|
||||
BarcodeFormat.CODE_39,
|
||||
BarcodeFormat.CODE_93,
|
||||
BarcodeFormat.ITF,
|
||||
BarcodeFormat.QR_CODE,
|
||||
];
|
||||
|
||||
hints.set(DecodeHintType.POSSIBLE_FORMATS, formats);
|
||||
|
||||
this._reader = new BrowserMultiFormatReader(hints, 1000);
|
||||
|
||||
from(this._reader.listVideoInputDevices()).subscribe((devices) => {
|
||||
this.devices$.next(devices);
|
||||
this.selectedDevice$.next(devices[0].deviceId);
|
||||
});
|
||||
|
||||
this.selectedDevice$
|
||||
.pipe(
|
||||
takeUntil(this._onDestroy$),
|
||||
filter((v) => !!v),
|
||||
tap(() => {
|
||||
this._reader?.reset();
|
||||
})
|
||||
)
|
||||
.subscribe((device) => {
|
||||
this._reader.decodeFromVideoDevice(device, this.scanContainer.nativeElement, (result) => {
|
||||
if (result) {
|
||||
this._modalRef.close(result.getText());
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
this.cancel();
|
||||
this._modal
|
||||
.open({
|
||||
content: UiMessageModalComponent,
|
||||
data: {
|
||||
message: `
|
||||
Scanner kann nicht aktiviert werden.
|
||||
Bitte stellen Sie sicher, dass der zugriff auf die Kamera erlaubt ist.
|
||||
`,
|
||||
},
|
||||
})
|
||||
.afterClosed$.subscribe(() => {
|
||||
this.cancel();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this._modalRef.close();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._onDestroy$.next();
|
||||
|
||||
this._reader?.reset();
|
||||
}
|
||||
|
||||
setSelectedDevice(id: string) {
|
||||
this.selectedDevice$.next(id);
|
||||
}
|
||||
}
|
||||
11
apps/adapter/scan/src/lib/zxing-modal/zxing-modal.module.ts
Normal file
11
apps/adapter/scan/src/lib/zxing-modal/zxing-modal.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { ZxingModalComponent } from './zxing-modal.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
exports: [ZxingModalComponent],
|
||||
declarations: [ZxingModalComponent],
|
||||
})
|
||||
export class ZxingModalModule {}
|
||||
38
apps/adapter/scan/src/lib/zxing.scan-adapter.ts
Normal file
38
apps/adapter/scan/src/lib/zxing.scan-adapter.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ScanAdapter } from './scan-adapter';
|
||||
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { Config } from '@core/config';
|
||||
import { ZxingModalComponent } from './zxing-modal/zxing-modal.component';
|
||||
|
||||
@Injectable()
|
||||
export class ZxingScanAdapter implements ScanAdapter {
|
||||
private _isReady = true;
|
||||
|
||||
constructor(private readonly _modal: UiModalService, private readonly _config: Config) {}
|
||||
|
||||
getName(): string {
|
||||
return 'Zxing';
|
||||
}
|
||||
|
||||
isPrimary(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
isReady(): boolean {
|
||||
return this._isReady;
|
||||
}
|
||||
|
||||
scan(): Observable<string> {
|
||||
return this._modal
|
||||
.open({
|
||||
content: ZxingModalComponent,
|
||||
})
|
||||
.afterClosed$.pipe(
|
||||
map((result) => result.data),
|
||||
filter((result) => !!result)
|
||||
);
|
||||
}
|
||||
}
|
||||
6
apps/adapter/scan/src/public-api.ts
Normal file
6
apps/adapter/scan/src/public-api.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* Public API Surface of scan
|
||||
*/
|
||||
|
||||
export * from './lib/scan.service';
|
||||
export * from './lib/scan.module';
|
||||
25
apps/adapter/scan/src/test.ts
Normal file
25
apps/adapter/scan/src/test.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js';
|
||||
import 'zone.js/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: {
|
||||
context(
|
||||
path: string,
|
||||
deep?: boolean,
|
||||
filter?: RegExp
|
||||
): {
|
||||
keys(): string[];
|
||||
<T>(id: string): T;
|
||||
};
|
||||
};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } });
|
||||
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
||||
20
apps/adapter/scan/tsconfig.lib.json
Normal file
20
apps/adapter/scan/tsconfig.lib.json
Normal file
@@ -0,0 +1,20 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2018"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
10
apps/adapter/scan/tsconfig.lib.prod.json
Normal file
10
apps/adapter/scan/tsconfig.lib.prod.json
Normal file
@@ -0,0 +1,10 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"declarationMap": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial"
|
||||
}
|
||||
}
|
||||
17
apps/adapter/scan/tsconfig.spec.json
Normal file
17
apps/adapter/scan/tsconfig.spec.json
Normal file
@@ -0,0 +1,17 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
25
apps/cdn/product-image/README.md
Normal file
25
apps/cdn/product-image/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# ProductImage
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.2.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project product-image` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project product-image`.
|
||||
|
||||
> Note: Don't forget to add `--project product-image` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build product-image` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build product-image`, go to the dist folder `cd dist/product-image` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test product-image` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
32
apps/cdn/product-image/karma.conf.js
Normal file
32
apps/cdn/product-image/karma.conf.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
],
|
||||
client: {
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../../coverage/cdn/product-image'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true,
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
};
|
||||
7
apps/cdn/product-image/ng-package.json
Normal file
7
apps/cdn/product-image/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/cdn/product-image",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
11
apps/cdn/product-image/package.json
Normal file
11
apps/cdn/product-image/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@cdn/product-image",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^10.1.2",
|
||||
"@angular/core": "^10.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@ import { NgModule } from '@angular/core';
|
||||
import { ProductImagePipe } from './product-image.pipe';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [ProductImagePipe],
|
||||
declarations: [ProductImagePipe],
|
||||
imports: [],
|
||||
exports: [ProductImagePipe],
|
||||
})
|
||||
export class ProductImageModule {}
|
||||
@@ -3,8 +3,6 @@ import { ProductImageService } from './product-image.service';
|
||||
|
||||
@Pipe({
|
||||
name: 'productImage',
|
||||
standalone: true,
|
||||
pure: true,
|
||||
})
|
||||
export class ProductImagePipe implements PipeTransform {
|
||||
constructor(private imageService: ProductImageService) {}
|
||||
16
apps/cdn/product-image/src/lib/product-image.service.spec.ts
Normal file
16
apps/cdn/product-image/src/lib/product-image.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProductImageService } from './product-image.service';
|
||||
|
||||
describe('ProductImageService', () => {
|
||||
let service: ProductImageService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ProductImageService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
8
apps/cdn/product-image/src/public-api.ts
Normal file
8
apps/cdn/product-image/src/public-api.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Public API Surface of product-image
|
||||
*/
|
||||
|
||||
export * from './lib/product-image.service';
|
||||
export * from './lib/product-image.module';
|
||||
export * from './lib/product-image.pipe';
|
||||
export * from './lib/tokens';
|
||||
24
apps/cdn/product-image/src/test.ts
Normal file
24
apps/cdn/product-image/src/test.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js';
|
||||
import 'zone.js/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: {
|
||||
context(
|
||||
path: string,
|
||||
deep?: boolean,
|
||||
filter?: RegExp
|
||||
): {
|
||||
keys(): string[];
|
||||
<T>(id: string): T;
|
||||
};
|
||||
};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
||||
25
apps/cdn/product-image/tsconfig.lib.json
Normal file
25
apps/cdn/product-image/tsconfig.lib.json
Normal file
@@ -0,0 +1,25 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2018"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"skipTemplateCodegen": true,
|
||||
"strictMetadataEmit": true,
|
||||
"enableResourceInlining": true
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
10
apps/cdn/product-image/tsconfig.lib.prod.json
Normal file
10
apps/cdn/product-image/tsconfig.lib.prod.json
Normal file
@@ -0,0 +1,10 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"declarationMap": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableIvy": false
|
||||
}
|
||||
}
|
||||
17
apps/cdn/product-image/tsconfig.spec.json
Normal file
17
apps/cdn/product-image/tsconfig.spec.json
Normal file
@@ -0,0 +1,17 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
17
apps/cdn/product-image/tslint.json
Normal file
17
apps/cdn/product-image/tslint.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"cdn",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"cdn",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
||||
25
apps/core/application/README.md
Normal file
25
apps/core/application/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Application
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.2.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project application` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project application`.
|
||||
|
||||
> Note: Don't forget to add `--project application` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build application` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build application`, go to the dist folder `cd dist/application` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test application` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
43
apps/core/application/karma.conf.js
Normal file
43
apps/core/application/karma.conf.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
const customLaunchers = require('../../../karma/custom-launchers');
|
||||
const junitReporter = require('../../../karma/junit-reporter')('core-application');
|
||||
const coverageReporter = require('../../../karma/coverage-reporter')('core-application');
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('karma-junit-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true, // removes the duplicated traces
|
||||
},
|
||||
coverageReporter,
|
||||
junitReporter,
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
customLaunchers,
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
};
|
||||
7
apps/core/application/ng-package.json
Normal file
7
apps/core/application/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/application",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
11
apps/core/application/package.json
Normal file
11
apps/core/application/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@core/application",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^10.1.2",
|
||||
"@angular/core": "^10.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
}
|
||||
232
apps/core/application/src/lib/application.service.spec.ts
Normal file
232
apps/core/application/src/lib/application.service.spec.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { ApplicationProcess } from './defs';
|
||||
|
||||
import { ApplicationService } from './application.service';
|
||||
import * as actions from './store/application.actions';
|
||||
|
||||
describe('ApplicationService', () => {
|
||||
let spectator: SpectatorService<ApplicationService>;
|
||||
let store: SpyObject<Store>;
|
||||
const createService = createServiceFactory({
|
||||
service: ApplicationService,
|
||||
mocks: [Store],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
spectator = createService({});
|
||||
store = spectator.inject(Store);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(spectator.service).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('activatedProcessId$', () => {
|
||||
it('should return an observable', () => {
|
||||
expect(spectator.service.activatedProcessId$).toBeInstanceOf(Observable);
|
||||
});
|
||||
});
|
||||
|
||||
describe('activatedProcessId', () => {
|
||||
it('should return the process id as a number', () => {
|
||||
spyOnProperty(spectator.service['activatedProcessIdSubject'] as any, 'value').and.returnValue(2);
|
||||
expect(spectator.service.activatedProcessId).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getProcesses$()', () => {
|
||||
it('should call select on store and return all selected processes', async () => {
|
||||
const processes: ApplicationProcess[] = [
|
||||
{ id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } },
|
||||
{ id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' },
|
||||
];
|
||||
store.select.and.returnValue(of(processes));
|
||||
const result = await spectator.service.getProcesses$().pipe(first()).toPromise();
|
||||
expect(result).toEqual(processes);
|
||||
expect(store.select).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call select on store and return all section customer processes', async () => {
|
||||
const processes: ApplicationProcess[] = [
|
||||
{ id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } },
|
||||
{ id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' },
|
||||
];
|
||||
store.select.and.returnValue(of(processes));
|
||||
const result = await spectator.service.getProcesses$('customer').pipe(first()).toPromise();
|
||||
expect(result).toEqual([processes[0]]);
|
||||
expect(store.select).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call select on store and return all section branch processes', async () => {
|
||||
const processes: ApplicationProcess[] = [
|
||||
{ id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } },
|
||||
{ id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' },
|
||||
];
|
||||
store.select.and.returnValue(of(processes));
|
||||
const result = await spectator.service.getProcesses$('branch').pipe(first()).toPromise();
|
||||
expect(result).toEqual([processes[1]]);
|
||||
expect(store.select).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getProcessById$()', () => {
|
||||
it('should return the process by id', async () => {
|
||||
const processes: ApplicationProcess[] = [
|
||||
{ id: 1, name: 'Vorgang 1', section: 'customer' },
|
||||
{ id: 2, name: 'Vorgang 2', section: 'customer' },
|
||||
];
|
||||
spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes));
|
||||
|
||||
const process = await spectator.service.getProcessById$(1).toPromise();
|
||||
expect(process.id).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSection$()', () => {
|
||||
it('should return the selected section branch', async () => {
|
||||
const section = 'branch';
|
||||
store.select.and.returnValue(of(section));
|
||||
const result = await spectator.service.getSection$().pipe(first()).toPromise();
|
||||
expect(result).toEqual(section);
|
||||
expect(store.select).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getActivatedProcessId$', () => {
|
||||
it('should return the current selected activated process id', async () => {
|
||||
const activatedProcessId = 2;
|
||||
store.select.and.returnValue(of({ id: activatedProcessId }));
|
||||
const result = await spectator.service.getActivatedProcessId$().pipe(first()).toPromise();
|
||||
expect(result).toEqual(activatedProcessId);
|
||||
expect(store.select).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('activateProcess()', () => {
|
||||
it('should dispatch action setActivatedProcess with argument activatedProcessId and action type', () => {
|
||||
const activatedProcessId = 2;
|
||||
spectator.service.activateProcess(activatedProcessId);
|
||||
expect(store.dispatch).toHaveBeenCalledWith({ activatedProcessId, type: actions.setActivatedProcess.type });
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeProcess()', () => {
|
||||
it('should dispatch action removeProcess with argument processId and action type', () => {
|
||||
const processId = 2;
|
||||
spectator.service.removeProcess(processId);
|
||||
expect(store.dispatch).toHaveBeenCalledWith({ processId, type: actions.removeProcess.type });
|
||||
});
|
||||
});
|
||||
|
||||
describe('createProcess()', () => {
|
||||
it('should dispatch action addProcess with process', async () => {
|
||||
const process: ApplicationProcess = {
|
||||
id: 1,
|
||||
name: 'Vorgang 1',
|
||||
section: 'customer',
|
||||
type: 'cart',
|
||||
};
|
||||
|
||||
const timestamp = 100;
|
||||
spyOn(spectator.service as any, '_createTimestamp').and.returnValue(timestamp);
|
||||
spyOn(spectator.service, 'getProcessById$').and.returnValue(of(undefined));
|
||||
await spectator.service.createProcess(process);
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith({
|
||||
type: actions.addProcess.type,
|
||||
process: {
|
||||
...process,
|
||||
activated: 0,
|
||||
created: timestamp,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if the process id is already existing', async () => {
|
||||
const process: ApplicationProcess = {
|
||||
id: 1,
|
||||
name: 'Vorgang 1',
|
||||
section: 'customer',
|
||||
type: 'cart',
|
||||
};
|
||||
spyOn(spectator.service, 'getProcessById$').and.returnValue(of(process));
|
||||
await expectAsync(spectator.service.createProcess(process)).toBeRejectedWithError('Process Id existiert bereits');
|
||||
});
|
||||
|
||||
it('should throw an error if the process id is not a number', async () => {
|
||||
const process: ApplicationProcess = {
|
||||
id: undefined,
|
||||
name: 'Vorgang 1',
|
||||
section: 'customer',
|
||||
type: 'cart',
|
||||
};
|
||||
spyOn(spectator.service, 'getProcessById$').and.returnValue(of({ id: 5, name: 'Vorgang 2', section: 'customer' }));
|
||||
await expectAsync(spectator.service.createProcess(process)).toBeRejectedWithError('Process Id nicht gesetzt');
|
||||
});
|
||||
});
|
||||
|
||||
describe('patchProcess', () => {
|
||||
it('should dispatch action patchProcess with changes', async () => {
|
||||
const process: ApplicationProcess = {
|
||||
id: 1,
|
||||
name: 'Vorgang 1',
|
||||
section: 'customer',
|
||||
type: 'cart',
|
||||
};
|
||||
|
||||
await spectator.service.patchProcess(process.id, process);
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith({
|
||||
type: actions.patchProcess.type,
|
||||
process: {
|
||||
...process,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setSection()', () => {
|
||||
it('should dispatch action setSection with argument section and action type', () => {
|
||||
const section = 'customer';
|
||||
spectator.service.setSection(section);
|
||||
expect(store.dispatch).toHaveBeenCalledWith({ section, type: actions.setSection.type });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLastActivatedProcessWithSectionAndType()', () => {
|
||||
it('should return the last activated process by section and type', async () => {
|
||||
const processes: ApplicationProcess[] = [
|
||||
{ id: 1, name: 'Vorgang 1', section: 'customer', type: 'cart', activated: 100 },
|
||||
{ id: 2, name: 'Vorgang 2', section: 'customer', type: 'cart', activated: 200 },
|
||||
{ id: 3, name: 'Vorgang 3', section: 'customer', type: 'goodsOut', activated: 300 },
|
||||
];
|
||||
spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes));
|
||||
|
||||
expect(await spectator.service.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()).toBe(
|
||||
processes[1]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLastActivatedProcessWithSection()', () => {
|
||||
it('should return the last activated process by section', async () => {
|
||||
const processes: ApplicationProcess[] = [
|
||||
{ id: 1, name: 'Vorgang 1', section: 'customer', activated: 100 },
|
||||
{ id: 2, name: 'Vorgang 2', section: 'customer', activated: 200 },
|
||||
{ id: 3, name: 'Vorgang 3', section: 'customer', activated: 300 },
|
||||
];
|
||||
spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes));
|
||||
|
||||
expect(await spectator.service.getLastActivatedProcessWithSection$('customer').pipe(first()).toPromise()).toBe(processes[2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_createTimestamp', () => {
|
||||
it('should return the current timestamp in ms', () => {
|
||||
expect(spectator.service['_createTimestamp']()).toBeCloseTo(Date.now());
|
||||
});
|
||||
});
|
||||
});
|
||||
129
apps/core/application/src/lib/application.service.ts
Normal file
129
apps/core/application/src/lib/application.service.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { isBoolean, isNumber } from '@utils/common';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { first, map, switchMap } from 'rxjs/operators';
|
||||
import { ApplicationProcess } from './defs';
|
||||
import {
|
||||
removeProcess,
|
||||
selectSection,
|
||||
selectProcesses,
|
||||
setSection,
|
||||
addProcess,
|
||||
setActivatedProcess,
|
||||
selectActivatedProcess,
|
||||
patchProcess,
|
||||
patchProcessData,
|
||||
} from './store';
|
||||
|
||||
@Injectable()
|
||||
export class ApplicationService {
|
||||
/** @deprecated */
|
||||
private activatedProcessIdSubject = new BehaviorSubject<number>(undefined);
|
||||
|
||||
/** @deprecated */
|
||||
get activatedProcessId() {
|
||||
return this.activatedProcessIdSubject.value;
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
get activatedProcessId$() {
|
||||
return this.activatedProcessIdSubject.asObservable();
|
||||
}
|
||||
|
||||
constructor(private store: Store) {}
|
||||
|
||||
getProcesses$(section?: 'customer' | 'branch') {
|
||||
const processes$ = this.store.select(selectProcesses);
|
||||
return processes$.pipe(map((processes) => processes.filter((process) => (section ? process.section === section : true))));
|
||||
}
|
||||
|
||||
getProcessById$(processId: number): Observable<ApplicationProcess> {
|
||||
return this.getProcesses$().pipe(map((processes) => processes.find((process) => process.id === processId)));
|
||||
}
|
||||
|
||||
getSection$() {
|
||||
return this.store.select(selectSection);
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
getActivatedProcessId$() {
|
||||
return this.store.select(selectActivatedProcess).pipe(map((process) => process?.id));
|
||||
}
|
||||
|
||||
activateProcess(activatedProcessId: number) {
|
||||
this.store.dispatch(setActivatedProcess({ activatedProcessId }));
|
||||
this.activatedProcessIdSubject.next(activatedProcessId);
|
||||
}
|
||||
|
||||
removeProcess(processId: number) {
|
||||
this.store.dispatch(removeProcess({ processId }));
|
||||
}
|
||||
|
||||
patchProcess(processId: number, changes: Partial<ApplicationProcess>) {
|
||||
this.store.dispatch(patchProcess({ processId, changes }));
|
||||
}
|
||||
|
||||
patchProcessData(processId: number, data: Record<string, any>) {
|
||||
this.store.dispatch(patchProcessData({ processId, data }));
|
||||
}
|
||||
|
||||
async createProcess(process: ApplicationProcess) {
|
||||
const existingProcess = await this.getProcessById$(process?.id).pipe(first()).toPromise();
|
||||
if (existingProcess?.id === process?.id) {
|
||||
throw new Error('Process Id existiert bereits');
|
||||
}
|
||||
|
||||
if (!isNumber(process.id)) {
|
||||
throw new Error('Process Id nicht gesetzt');
|
||||
}
|
||||
|
||||
if (!isBoolean(process.closeable)) {
|
||||
process.closeable = true;
|
||||
}
|
||||
|
||||
if (!isBoolean(process.confirmClosing)) {
|
||||
process.confirmClosing = true;
|
||||
}
|
||||
|
||||
process.created = this._createTimestamp();
|
||||
process.activated = 0;
|
||||
this.store.dispatch(addProcess({ process }));
|
||||
}
|
||||
|
||||
setSection(section: 'customer' | 'branch') {
|
||||
this.store.dispatch(setSection({ section }));
|
||||
}
|
||||
|
||||
getLastActivatedProcessWithSectionAndType$(section: 'customer' | 'branch', type: string): Observable<ApplicationProcess> {
|
||||
return this.getProcesses$(section).pipe(
|
||||
map((processes) =>
|
||||
processes
|
||||
?.filter((process) => process.type === type)
|
||||
?.reduce((latest, current) => {
|
||||
if (!latest) {
|
||||
return current;
|
||||
}
|
||||
return latest?.activated > current?.activated ? latest : current;
|
||||
}, undefined)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getLastActivatedProcessWithSection$(section: 'customer' | 'branch'): Observable<ApplicationProcess> {
|
||||
return this.getProcesses$(section).pipe(
|
||||
map((processes) =>
|
||||
processes?.reduce((latest, current) => {
|
||||
if (!latest) {
|
||||
return current;
|
||||
}
|
||||
return latest?.activated > current?.activated ? latest : current;
|
||||
}, undefined)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _createTimestamp() {
|
||||
return Date.now();
|
||||
}
|
||||
}
|
||||
6
apps/core/application/src/lib/index.ts
Normal file
6
apps/core/application/src/lib/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// start:ng42.barrel
|
||||
export * from './application.module';
|
||||
export * from './application.service';
|
||||
export * from './defs';
|
||||
export * from './store';
|
||||
// end:ng42.barrel
|
||||
16
apps/core/application/src/lib/store/application.actions.ts
Normal file
16
apps/core/application/src/lib/store/application.actions.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { createAction, props } from '@ngrx/store';
|
||||
import { ApplicationProcess } from '..';
|
||||
|
||||
const prefix = '[CORE-APPLICATION]';
|
||||
|
||||
export const setSection = createAction(`${prefix} Set Section`, props<{ section: 'customer' | 'branch' }>());
|
||||
|
||||
export const addProcess = createAction(`${prefix} Add Process`, props<{ process: ApplicationProcess }>());
|
||||
|
||||
export const removeProcess = createAction(`${prefix} Remove Process`, props<{ processId: number }>());
|
||||
|
||||
export const setActivatedProcess = createAction(`${prefix} Set Activated Process`, props<{ activatedProcessId: number }>());
|
||||
|
||||
export const patchProcess = createAction(`${prefix} Patch Process`, props<{ processId: number; changes: Partial<ApplicationProcess> }>());
|
||||
|
||||
export const patchProcessData = createAction(`${prefix} Patch Process Data`, props<{ processId: number; data: Record<string, any> }>());
|
||||
@@ -60,13 +60,13 @@ describe('applicationReducer', () => {
|
||||
type: 'cart',
|
||||
};
|
||||
|
||||
const action = actions.patchProcess({ processId: process.id, changes: { ...process, name: 'Test' } });
|
||||
const action = actions.patchProcess({ process: { ...process, name: 'Test' } });
|
||||
const state = applicationReducer(
|
||||
{
|
||||
...initialState,
|
||||
processes: [process],
|
||||
},
|
||||
action,
|
||||
action
|
||||
);
|
||||
expect(state.processes[0].name).toEqual('Test');
|
||||
});
|
||||
@@ -81,13 +81,13 @@ describe('applicationReducer', () => {
|
||||
type: 'cart',
|
||||
};
|
||||
|
||||
const action = actions.patchProcess({ processId: process.id, changes: { ...process, id: 2 } });
|
||||
const action = actions.patchProcess({ process: { ...process, id: 2 } });
|
||||
const state = applicationReducer(
|
||||
{
|
||||
...initialState,
|
||||
processes: [process],
|
||||
},
|
||||
action,
|
||||
action
|
||||
);
|
||||
expect(state.processes).toEqual([process]);
|
||||
});
|
||||
@@ -1,18 +1,9 @@
|
||||
import { Action, createReducer, on } from '@ngrx/store';
|
||||
import {
|
||||
setSection,
|
||||
addProcess,
|
||||
removeProcess,
|
||||
setActivatedProcess,
|
||||
patchProcess,
|
||||
patchProcessData,
|
||||
setTitle,
|
||||
} from './application.actions';
|
||||
import { setSection, addProcess, removeProcess, setActivatedProcess, patchProcess, patchProcessData } from './application.actions';
|
||||
import { ApplicationState, INITIAL_APPLICATION_STATE } from './application.state';
|
||||
|
||||
const _applicationReducer = createReducer(
|
||||
INITIAL_APPLICATION_STATE,
|
||||
on(setTitle, (state, { title }) => ({ ...state, title })),
|
||||
on(setSection, (state, { section }) => ({ ...state, section })),
|
||||
on(addProcess, (state, { process }) => ({ ...state, processes: [...state.processes, { data: {}, ...process }] })),
|
||||
on(removeProcess, (state, { processId }) => {
|
||||
@@ -32,7 +23,7 @@ const _applicationReducer = createReducer(
|
||||
on(patchProcess, (state, { processId, changes }) => {
|
||||
const processes = state.processes.map((process) => {
|
||||
if (process.id === processId) {
|
||||
return { ...process, ...changes, id: processId };
|
||||
return { ...process, ...changes };
|
||||
}
|
||||
return process;
|
||||
});
|
||||
@@ -48,7 +39,7 @@ const _applicationReducer = createReducer(
|
||||
});
|
||||
|
||||
return { ...state, processes };
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
export function applicationReducer(state: ApplicationState, action: Action) {
|
||||
@@ -0,0 +1,32 @@
|
||||
import { ApplicationState } from './application.state';
|
||||
import { ApplicationProcess } from '../defs';
|
||||
import * as selectors from './application.selectors';
|
||||
|
||||
describe('applicationSelectors', () => {
|
||||
it('should select the processes', () => {
|
||||
const processes: ApplicationProcess[] = [{ id: 1, name: 'Vorgang 1', section: 'customer' }];
|
||||
const state: Partial<ApplicationState> = {
|
||||
processes,
|
||||
};
|
||||
expect(selectors.selectProcesses.projector(state)).toEqual(processes);
|
||||
});
|
||||
|
||||
it('should select the section', () => {
|
||||
const state: Partial<ApplicationState> = {
|
||||
section: 'customer',
|
||||
};
|
||||
expect(selectors.selectSection.projector(state)).toEqual('customer');
|
||||
});
|
||||
|
||||
it('should select the activatedProcess', () => {
|
||||
const processes: ApplicationProcess[] = [
|
||||
{ id: 1, name: 'Vorgang 1', section: 'customer', activated: 100 },
|
||||
{ id: 2, name: 'Vorgang 2', section: 'customer', activated: 300 },
|
||||
{ id: 3, name: 'Vorgang 3', section: 'customer', activated: 200 },
|
||||
];
|
||||
const state: Partial<ApplicationState> = {
|
||||
processes,
|
||||
};
|
||||
expect(selectors.selectActivatedProcess.projector(state)).toEqual(processes[1]);
|
||||
});
|
||||
});
|
||||
@@ -2,8 +2,6 @@ import { createFeatureSelector, createSelector } from '@ngrx/store';
|
||||
import { ApplicationState } from './application.state';
|
||||
export const selectApplicationState = createFeatureSelector<ApplicationState>('core-application');
|
||||
|
||||
export const selectTitle = createSelector(selectApplicationState, (s) => s.title);
|
||||
|
||||
export const selectSection = createSelector(selectApplicationState, (s) => s.section);
|
||||
|
||||
export const selectProcesses = createSelector(selectApplicationState, (s) => s.processes);
|
||||
@@ -14,5 +12,5 @@ export const selectActivatedProcess = createSelector(selectApplicationState, (s)
|
||||
return current;
|
||||
}
|
||||
return process.activated > current.activated ? process : current;
|
||||
}, undefined),
|
||||
}, undefined)
|
||||
);
|
||||
@@ -1,13 +1,11 @@
|
||||
import { ApplicationProcess } from '../defs';
|
||||
|
||||
export interface ApplicationState {
|
||||
title: string;
|
||||
processes: ApplicationProcess[];
|
||||
section: 'customer' | 'branch';
|
||||
}
|
||||
|
||||
export const INITIAL_APPLICATION_STATE: ApplicationState = {
|
||||
title: '',
|
||||
processes: [],
|
||||
section: 'customer',
|
||||
};
|
||||
5
apps/core/application/src/public-api.ts
Normal file
5
apps/core/application/src/public-api.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
* Public API Surface of application
|
||||
*/
|
||||
|
||||
export * from './lib';
|
||||
24
apps/core/application/src/test.ts
Normal file
24
apps/core/application/src/test.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js';
|
||||
import 'zone.js/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: {
|
||||
context(
|
||||
path: string,
|
||||
deep?: boolean,
|
||||
filter?: RegExp
|
||||
): {
|
||||
keys(): string[];
|
||||
<T>(id: string): T;
|
||||
};
|
||||
};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
||||
25
apps/core/application/tsconfig.lib.json
Normal file
25
apps/core/application/tsconfig.lib.json
Normal file
@@ -0,0 +1,25 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2018"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"skipTemplateCodegen": true,
|
||||
"strictMetadataEmit": true,
|
||||
"enableResourceInlining": true
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
10
apps/core/application/tsconfig.lib.prod.json
Normal file
10
apps/core/application/tsconfig.lib.prod.json
Normal file
@@ -0,0 +1,10 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"declarationMap": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableIvy": false
|
||||
}
|
||||
}
|
||||
17
apps/core/application/tsconfig.spec.json
Normal file
17
apps/core/application/tsconfig.spec.json
Normal file
@@ -0,0 +1,17 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
17
apps/core/application/tslint.json
Normal file
17
apps/core/application/tslint.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"lib",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"lib",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
||||
25
apps/core/auth/README.md
Normal file
25
apps/core/auth/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Auth
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project auth` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project auth`.
|
||||
|
||||
> Note: Don't forget to add `--project auth` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build auth` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build auth`, go to the dist folder `cd dist/auth` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test auth` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
43
apps/core/auth/karma.conf.js
Normal file
43
apps/core/auth/karma.conf.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
const customLaunchers = require('../../../karma/custom-launchers');
|
||||
const junitReporter = require('../../../karma/junit-reporter')('core-auth');
|
||||
const coverageReporter = require('../../../karma/coverage-reporter')('core-auth');
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('karma-junit-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true, // removes the duplicated traces
|
||||
},
|
||||
coverageReporter,
|
||||
junitReporter,
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
customLaunchers,
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
};
|
||||
7
apps/core/auth/ng-package.json
Normal file
7
apps/core/auth/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/auth",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
11
apps/core/auth/package.json
Normal file
11
apps/core/auth/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@core/auth",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^12.2.0",
|
||||
"@angular/core": "^12.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
||||
import { AuthService } from './auth.service';
|
||||
import { OAuthModule } from 'angular-oauth2-oidc';
|
||||
import { IfRoleDirective } from './if-role.directive';
|
||||
@NgModule({
|
||||
declarations: [IfRoleDirective],
|
||||
exports: [IfRoleDirective],
|
||||
})
|
||||
@NgModule({})
|
||||
export class AuthModule {
|
||||
static forRoot(): ModuleWithProviders<AuthModule> {
|
||||
return {
|
||||
84
apps/core/auth/src/lib/auth.service.ts
Normal file
84
apps/core/auth/src/lib/auth.service.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Config } from '@core/config';
|
||||
import { isNullOrUndefined } from '@utils/common';
|
||||
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
|
||||
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthService {
|
||||
private readonly _initialized = new BehaviorSubject<boolean>(false);
|
||||
get initialized$() {
|
||||
return this._initialized.asObservable();
|
||||
}
|
||||
|
||||
constructor(private _config: Config, private readonly _oAuthService: OAuthService) {}
|
||||
|
||||
async init() {
|
||||
if (this._initialized.getValue()) {
|
||||
throw new Error('AuthService is already initialized');
|
||||
}
|
||||
|
||||
const authConfig: AuthConfig = this._config.get('@core/auth');
|
||||
|
||||
authConfig.redirectUri = window.location.origin;
|
||||
authConfig.silentRefreshRedirectUri = window.location.origin + '/silent-refresh.html';
|
||||
authConfig.useSilentRefresh = true;
|
||||
|
||||
this._oAuthService.configure(authConfig);
|
||||
this._oAuthService.tokenValidationHandler = new JwksValidationHandler();
|
||||
|
||||
this._oAuthService.setupAutomaticSilentRefresh();
|
||||
await this._oAuthService.loadDiscoveryDocumentAndTryLogin();
|
||||
|
||||
this._initialized.next(true);
|
||||
}
|
||||
|
||||
isAuthenticated() {
|
||||
return this._oAuthService.hasValidIdToken();
|
||||
}
|
||||
|
||||
getToken() {
|
||||
return this._oAuthService.getAccessToken();
|
||||
}
|
||||
|
||||
getClaims() {
|
||||
const token = this._oAuthService.getAccessToken();
|
||||
return this.parseJwt(token);
|
||||
}
|
||||
|
||||
getClaimByKey(key: string) {
|
||||
const claims = this.getClaims();
|
||||
if (isNullOrUndefined(claims)) {
|
||||
return null;
|
||||
}
|
||||
return claims[key];
|
||||
}
|
||||
|
||||
parseJwt(token: string) {
|
||||
if (isNullOrUndefined(token)) {
|
||||
return null;
|
||||
}
|
||||
const base64Url = token.split('.')[1];
|
||||
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
||||
|
||||
const encoded = window.atob(base64);
|
||||
return JSON.parse(encoded);
|
||||
}
|
||||
|
||||
login() {
|
||||
this._oAuthService.initLoginFlow();
|
||||
}
|
||||
|
||||
setKeyCardToken(token: string) {
|
||||
this._oAuthService.customQueryParams = {
|
||||
temp_token: token,
|
||||
};
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this._oAuthService.revokeTokenAndLogout();
|
||||
}
|
||||
}
|
||||
4
apps/core/auth/src/lib/index.ts
Normal file
4
apps/core/auth/src/lib/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './auth.module';
|
||||
export * from './auth.service';
|
||||
// end:ng42.barrel
|
||||
5
apps/core/auth/src/public-api.ts
Normal file
5
apps/core/auth/src/public-api.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
* Public API Surface of auth
|
||||
*/
|
||||
|
||||
export * from './lib';
|
||||
25
apps/core/auth/src/test.ts
Normal file
25
apps/core/auth/src/test.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js';
|
||||
import 'zone.js/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: {
|
||||
context(
|
||||
path: string,
|
||||
deep?: boolean,
|
||||
filter?: RegExp
|
||||
): {
|
||||
keys(): string[];
|
||||
<T>(id: string): T;
|
||||
};
|
||||
};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } });
|
||||
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
||||
20
apps/core/auth/tsconfig.lib.json
Normal file
20
apps/core/auth/tsconfig.lib.json
Normal file
@@ -0,0 +1,20 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2018"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
10
apps/core/auth/tsconfig.lib.prod.json
Normal file
10
apps/core/auth/tsconfig.lib.prod.json
Normal file
@@ -0,0 +1,10 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"declarationMap": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial"
|
||||
}
|
||||
}
|
||||
17
apps/core/auth/tsconfig.spec.json
Normal file
17
apps/core/auth/tsconfig.spec.json
Normal file
@@ -0,0 +1,17 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
25
apps/core/breadcrumb/README.md
Normal file
25
apps/core/breadcrumb/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Breadcrumb
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.2.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project breadcrumb` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project breadcrumb`.
|
||||
|
||||
> Note: Don't forget to add `--project breadcrumb` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build breadcrumb` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build breadcrumb`, go to the dist folder `cd dist/breadcrumb` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test breadcrumb` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user