@smartsoft001-mobilems/claude-plugins 2.86.0 → 2.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugins/mobilems/.claude-plugin/plugin.json +1 -1
- package/plugins/mobilems/skills/mobilems-articles/SKILL.md +84 -0
- package/plugins/mobilems/skills/mobilems-articles/references/lib-contents.md +164 -0
- package/plugins/mobilems/skills/mobilems-articles/references/module-and-routes.md +146 -0
- package/plugins/mobilems/skills/mobilems-articles/references/shared-components.md +56 -0
- package/plugins/mobilems/skills/mobilems-articles/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-articles/scripts/verify-module.sh +61 -0
- package/plugins/mobilems/skills/mobilems-events/SKILL.md +65 -0
- package/plugins/mobilems/skills/mobilems-events/references/lib-contents.md +212 -0
- package/plugins/mobilems/skills/mobilems-events/references/module-and-routes.md +113 -0
- package/plugins/mobilems/skills/mobilems-events/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-events/scripts/verify-module.sh +59 -0
- package/plugins/mobilems/skills/mobilems-object-collections/SKILL.md +52 -0
- package/plugins/mobilems/skills/mobilems-object-collections/references/lib-contents.md +76 -0
- package/plugins/mobilems/skills/mobilems-object-collections/references/module-and-routes.md +116 -0
- package/plugins/mobilems/skills/mobilems-object-collections/references/shared-components.md +61 -0
- package/plugins/mobilems/skills/mobilems-object-collections/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-object-collections/scripts/verify-module.sh +44 -0
- package/plugins/mobilems/skills/mobilems-objects/SKILL.md +52 -0
- package/plugins/mobilems/skills/mobilems-objects/references/lib-contents.md +68 -0
- package/plugins/mobilems/skills/mobilems-objects/references/module-and-routes.md +108 -0
- package/plugins/mobilems/skills/mobilems-objects/references/shared-components.md +69 -0
- package/plugins/mobilems/skills/mobilems-objects/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-objects/scripts/verify-module.sh +51 -0
- package/plugins/mobilems/skills/mobilems-partners/SKILL.md +65 -0
- package/plugins/mobilems/skills/mobilems-partners/references/lib-contents.md +160 -0
- package/plugins/mobilems/skills/mobilems-partners/references/module-and-routes.md +125 -0
- package/plugins/mobilems/skills/mobilems-partners/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-partners/scripts/verify-module.sh +59 -0
- package/plugins/mobilems/skills/mobilems-project-init/SKILL.md +134 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/app-shell.md +107 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/claude-config.md +44 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/config-providers.md +203 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/npm-deps.md +66 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/server-env.md +89 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/shared-lib.md +125 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/tailwind-styles.md +106 -0
- package/plugins/mobilems/skills/mobilems-project-init/scripts/classify-shared-components.py +114 -0
- package/plugins/mobilems/skills/mobilems-project-init/scripts/detect-base.sh +49 -0
- package/plugins/mobilems/skills/mobilems-project-init/scripts/verify-init.sh +73 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/SKILL.md +60 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/references/lib-contents.md +134 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/references/module-and-routes.md +107 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/verify-module.sh +56 -0
package/package.json
CHANGED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mobilems-articles
|
|
3
|
+
description: Add an article (content) feature module to a MobilEMS app initialized with mobilems-project-init. The module is parametrized — you give it a NAME and a TEMPLATE value, and it builds an Article-backed list+detail module whose API requests are filtered by that template. Use whenever the user wants to add / scaffold / wire an articles, news (aktualności), education, or any template-filtered content module, or asks to "dodaj moduł artykułów / aktualności". One run builds one named module; run it again for each additional content type. Creates libs/museum-<name> (domain + shell/angular), registers its route, builds its article card shared components, and adds the articles npm packages. Requires the MobilEMS foundation to exist first.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# MobilEMS article module
|
|
9
|
+
|
|
10
|
+
Build a **parametrized article module** on top of a MobilEMS foundation produced by
|
|
11
|
+
`mobilems-project-init`. An article module is an Article-backed list + detail feature whose API
|
|
12
|
+
requests carry a fixed `template` filter, so the same module shape powers any number of content
|
|
13
|
+
types — you choose the **name** and the **template value**. A project may have several article
|
|
14
|
+
modules under different names (e.g. one per content category), and the same skill builds each.
|
|
15
|
+
|
|
16
|
+
The module wraps `ArticlesSharedModule.forFeature` over the npm `Article` entity. The only thing that
|
|
17
|
+
distinguishes one article module from another is its name, its route, its `entity` key, the
|
|
18
|
+
`template` value it filters on, and whether it keeps feature-local ngrx state.
|
|
19
|
+
|
|
20
|
+
## Parameters
|
|
21
|
+
|
|
22
|
+
Collect these in Step 1 with `AskUserQuestion` (or accept them from the invocation if the user already
|
|
23
|
+
gave them). The first two are the essentials:
|
|
24
|
+
|
|
25
|
+
| Parameter | What it is | Example |
|
|
26
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
|
27
|
+
| **name** | the module name → folder `museum-<name>`, class `Museum<Name>Module`, lib alias `@<PREFIX>/museum-<name>` | `news` |
|
|
28
|
+
| **template** | the value sent in the API request filter `baseQuery: [{ key: 'template', value: '<template>', type: '=', hidden: true }]` | `AKTUALNOŚCI` |
|
|
29
|
+
| route | the URL path segment for the module's pages | `aktualnosci` |
|
|
30
|
+
| entity | the `forFeature` `entity` key (API entity + ngrx state key); often equals the name | `news` |
|
|
31
|
+
| feature state? | whether the module keeps its own ngrx state (adds a `reducerFactory`) | yes / no |
|
|
32
|
+
|
|
33
|
+
> The filter **key is always `template`** — only the **value** changes per module. Copy the file
|
|
34
|
+
> structure from any existing article module in the reference MobilEMS app the project was built from,
|
|
35
|
+
> adapting its name/route/entity/template; don't treat that app's particular modules as a fixed set to
|
|
36
|
+
> reproduce.
|
|
37
|
+
|
|
38
|
+
## Precondition
|
|
39
|
+
|
|
40
|
+
Run `scripts/detect-init.sh <workspace-root>`. It confirms the MobilEMS foundation is present
|
|
41
|
+
(`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If not, tell the user to
|
|
42
|
+
run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`; reference uses `@msr`)
|
|
43
|
+
and the reference repo path (same one init used).
|
|
44
|
+
|
|
45
|
+
## Steps
|
|
46
|
+
|
|
47
|
+
1. **Gather the parameters** above. Ask for the **name** and **template** at minimum; default `route`
|
|
48
|
+
and `entity` to the name unless the user specifies otherwise; ask whether the module needs feature
|
|
49
|
+
state.
|
|
50
|
+
2. **npm deps** — add `@smartsoft001-mobilems/articles-domain`, `@smartsoft001-mobilems/articles-shell-angular`,
|
|
51
|
+
`@smartsoft001-mobilems/articles-shell-dtos` (match the installed mobilems line). Every article
|
|
52
|
+
module shares these three — if a previous article module already added them, skip. Resolve versions
|
|
53
|
+
at run time; do not `npm install` unless asked. (`@thisissoon/angular-masonry` is already a foundation dep.)
|
|
54
|
+
Also add any **extra third-party packages the copied pages import** that aren't already in the
|
|
55
|
+
workspace — e.g. the reference list page uses `lodash-decorators` (`@debounce`); grep the pages you
|
|
56
|
+
copy for non-`@angular`/`@smartsoft001` imports and add the missing ones.
|
|
57
|
+
3. **Library** — create `libs/museum-<name>/{domain,shell/angular}` per `references/lib-contents.md`:
|
|
58
|
+
the thin domain entity (`Museum<Name> extends Article`), the shell (`Museum<Name>Module`, routes,
|
|
59
|
+
`pages/{list,item}`, services, and — if the module keeps feature state — `+state/<name>.reducer.ts`),
|
|
60
|
+
threading `<PREFIX>` and the chosen name/entity. Add the two `tsconfig.base.json` paths
|
|
61
|
+
(`@<PREFIX>/museum-<name>/domain` + `/shell/angular`).
|
|
62
|
+
4. **Module + route** — see `references/module-and-routes.md`: `Museum<Name>Module` wraps
|
|
63
|
+
`ArticlesSharedModule.forFeature` with `type: Article`, the chosen `entity`, and
|
|
64
|
+
`baseQuery: [{ key: 'template', value: '<template>', type: '=', hidden: true }]`, plus the `Masonry`
|
|
65
|
+
SSR factory. Add the lazy `<route>` route to `appRoutes` in `apps/web/src/app/app.routes.ts`, and a
|
|
66
|
+
matching child under the `wyszukaj/:text` search route if the module's results belong in site search.
|
|
67
|
+
Also build the app's **`/article-preview` page** if it isn't there yet (it previews an `Article`, so
|
|
68
|
+
it belongs to the articles feature, not the foundation) — idempotent across runs; see
|
|
69
|
+
`references/module-and-routes.md`.
|
|
70
|
+
5. **Article card shared components** — see `references/shared-components.md`: build the module's grid
|
|
71
|
+
and list cards (`<name>-grid-item`, `<name>-list-item`) in `libs/shared/angular` and export them; if
|
|
72
|
+
the module renders article-gallery content blocks, also build the article gallery widget and register
|
|
73
|
+
it in the widget content-type map (build once, idempotent across article modules).
|
|
74
|
+
6. **Verify** — run `scripts/verify-module.sh <workspace-root> <PREFIX> <name> [template]`: asserts the
|
|
75
|
+
library exists, the `forFeature` carries the chosen `entity` (and template, if given), the route is
|
|
76
|
+
registered, and the article card shared components are built and exported. Fix and re-run.
|
|
77
|
+
|
|
78
|
+
## Notes
|
|
79
|
+
|
|
80
|
+
- One run builds one named article module. Run the skill again — with a different name + template — for
|
|
81
|
+
each additional content type the project needs.
|
|
82
|
+
- Article modules reuse the foundation's generic `IArticle` model and the foundation's news carousel —
|
|
83
|
+
this skill does not rebuild them.
|
|
84
|
+
- Bundled `references/` + `scripts/` ship automatically. Do not hand-edit `plugin.json`.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# libs/museum-<name> — structure to create
|
|
2
|
+
|
|
3
|
+
Copy from an existing article module in the reference MobilEMS app and substitute the module's
|
|
4
|
+
`<name>` / `<entity>`, threading `<PREFIX>`. The library has two projects: a
|
|
5
|
+
thin `domain` (one entity extending the npm `Article`) and a `shell/angular` (the Angular module +
|
|
6
|
+
list/item pages + service). Article modules use the npm `Article` entity directly through
|
|
7
|
+
`ArticlesSharedModule.forFeature`; the local domain entity is a near-empty subclass.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
libs/museum-<name>/
|
|
11
|
+
├── domain/ # project: museum-<name>-domain
|
|
12
|
+
│ └── src/
|
|
13
|
+
│ ├── index.ts # export * from './lib/entities';
|
|
14
|
+
│ └── lib/
|
|
15
|
+
│ ├── entities/ # Museum<Name> extends Article (+ index)
|
|
16
|
+
│ ├── enums/ # .gitkeep
|
|
17
|
+
│ ├── specifications/ # .gitkeep
|
|
18
|
+
│ └── value-objects/ # .gitkeep
|
|
19
|
+
└── shell/angular/ # project: museum-<name>-shell-angular
|
|
20
|
+
└── src/
|
|
21
|
+
├── index.ts # public barrel (pages + module [+ service])
|
|
22
|
+
└── lib/
|
|
23
|
+
├── <name>.module.ts # Museum<Name>Module (references/module-and-routes.md)
|
|
24
|
+
├── <name>.routes.ts
|
|
25
|
+
├── index.ts # SERVICES const + barrel (see below)
|
|
26
|
+
├── +state/ # ONLY if the module keeps feature state: <name>.reducer.ts
|
|
27
|
+
├── pages/ # list (ListComponent), item (ItemComponent) + index (PAGES)
|
|
28
|
+
└── services/ # optional module service (e.g. a visit logger)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Domain entity (thin subclass of the npm `Article`)
|
|
32
|
+
|
|
33
|
+
`domain/src/lib/entities/museum-<name>.entity.ts`:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { Model } from '@smartsoft001/models';
|
|
37
|
+
import { Article } from '@smartsoft001-mobilems/articles-domain';
|
|
38
|
+
|
|
39
|
+
@Model({})
|
|
40
|
+
export class Museum<Name> extends Article {}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then `entities/index.ts` re-exports it and `domain/src/index.ts` does `export * from './lib/entities';`.
|
|
44
|
+
(If the module needs extra fields, add them to this subclass.)
|
|
45
|
+
|
|
46
|
+
## shell `lib/index.ts` — `SERVICES` + barrel
|
|
47
|
+
|
|
48
|
+
Define a `SERVICES` const here binding `ArticlesService` as the `CrudService`; a module that keeps
|
|
49
|
+
feature state spreads it into its providers:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { CrudService } from '@smartsoft001/crud-shell-angular';
|
|
53
|
+
import { ArticlesService } from '@smartsoft001-mobilems/articles-shell-angular';
|
|
54
|
+
|
|
55
|
+
export * from './pages/index';
|
|
56
|
+
export * from './<name>.module';
|
|
57
|
+
|
|
58
|
+
export const SERVICES = [
|
|
59
|
+
ArticlesService,
|
|
60
|
+
{ provide: CrudService, useClass: ArticlesService },
|
|
61
|
+
];
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## shell `src/index.ts` (public barrel)
|
|
65
|
+
|
|
66
|
+
`export * from './lib/pages';` + `export * from './lib/<name>.module';` (plus the module's own service
|
|
67
|
+
if it ships one — see below).
|
|
68
|
+
|
|
69
|
+
## `pages/index.ts`
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { ItemComponent } from './item/item.component';
|
|
73
|
+
import { ListComponent } from './list/list.component';
|
|
74
|
+
|
|
75
|
+
export * from './item/item.component';
|
|
76
|
+
export * from './list/list.component';
|
|
77
|
+
|
|
78
|
+
export const PAGES = [ListComponent, ItemComponent];
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Copy the `list/` and `item/` `.component.ts` + `.html` from a reference article module, threading
|
|
82
|
+
`<PREFIX>`.
|
|
83
|
+
|
|
84
|
+
## Optional: `+state/<name>.reducer.ts` (feature-local ngrx state)
|
|
85
|
+
|
|
86
|
+
Build this only if the module keeps its own ngrx state (Step 1 "feature state? = yes"). It wraps the
|
|
87
|
+
smart CRUD reducer keyed by the module's `<entity>`:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { Action } from '@ngrx/store';
|
|
91
|
+
import {
|
|
92
|
+
State as SmartState,
|
|
93
|
+
initialState as smartInitialState,
|
|
94
|
+
getReducer as smartGetReducer,
|
|
95
|
+
} from '@smartsoft001/crud-shell-angular';
|
|
96
|
+
import { Article } from '@smartsoft001-mobilems/articles-domain';
|
|
97
|
+
|
|
98
|
+
export type State = SmartState<Article>;
|
|
99
|
+
|
|
100
|
+
export const initialState = { ...smartInitialState };
|
|
101
|
+
|
|
102
|
+
const reducer = (state = initialState, action: any) => {
|
|
103
|
+
switch (action.type) {
|
|
104
|
+
default:
|
|
105
|
+
return smartGetReducer('<entity>')(state, action);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export function getReducer() {
|
|
110
|
+
return (state: any, action: Action) => reducer(state, action);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The module then passes `reducerFactory: getReducer` to `forFeature` (see
|
|
115
|
+
`references/module-and-routes.md`). A module without feature state omits `+state/` entirely.
|
|
116
|
+
|
|
117
|
+
## Optional: a module service
|
|
118
|
+
|
|
119
|
+
A module may ship a small injectable (for example, a visit logger). Copy it from the reference module
|
|
120
|
+
if it has one, threading `<PREFIX>`, and export it from `src/index.ts`:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { HttpClient } from '@angular/common/http';
|
|
124
|
+
import { inject, Injectable } from '@angular/core';
|
|
125
|
+
import { firstValueFrom } from 'rxjs';
|
|
126
|
+
|
|
127
|
+
import { environment } from '@<PREFIX>/angular';
|
|
128
|
+
|
|
129
|
+
@Injectable({ providedIn: 'root' })
|
|
130
|
+
export class Museum<Name>Service {
|
|
131
|
+
private http = inject(HttpClient);
|
|
132
|
+
|
|
133
|
+
logVisit(id: string | number): Promise<void> {
|
|
134
|
+
const url = environment.apiUrl + 'api/article/' + id + '/visit';
|
|
135
|
+
return firstValueFrom(this.http.post<void>(url, {}));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
> **Watch for cross-module service imports.** In the reference, some article modules' pages inject
|
|
141
|
+
> _another_ article module's service (e.g. a `news` page importing `MuseumArticlesService` from
|
|
142
|
+
> `@<ref-prefix>/museum-articles/shell/angular`). A generic article module must be self-contained — when
|
|
143
|
+
> a copied page injects a sibling module's service, give this module its **own** `Museum<Name>Service`
|
|
144
|
+
> (the template above) and rewire the page's import to it, rather than depending on a module that may
|
|
145
|
+
> not exist in the target.
|
|
146
|
+
|
|
147
|
+
## Wiring
|
|
148
|
+
|
|
149
|
+
- **tsconfig.base.json paths** (add both):
|
|
150
|
+
```json
|
|
151
|
+
"@<PREFIX>/museum-<name>/domain": ["libs/museum-<name>/domain/src/index.ts"],
|
|
152
|
+
"@<PREFIX>/museum-<name>/shell/angular": ["libs/museum-<name>/shell/angular/src/index.ts"]
|
|
153
|
+
```
|
|
154
|
+
- **project.json / build config** — copy each project's build config and normalize every path field
|
|
155
|
+
the reference baked to its origin location — `sourceRoot`, `dest`, `outputPath`, `coverageDirectory`,
|
|
156
|
+
`jestConfig`, `lintFilePatterns`, and the jest `displayName` — to `libs/museum-<name>/...`. Some reference modules carry
|
|
157
|
+
stale values (e.g. a `sourceRoot` missing the `shell/angular` segment, or a `coverageDirectory`
|
|
158
|
+
under `packages/...`) while others are already correct — verify each against a known-good sibling and
|
|
159
|
+
fix only the stale ones. Set each `package.json` `name` to match (`museum-<name>-domain` /
|
|
160
|
+
`museum-<name>-shell-angular`) — a copied `package.json` may carry a stale name. A `domain` with no
|
|
161
|
+
`project.json` (Nx-inferred) is fine.
|
|
162
|
+
- **commit scope** — `commitlint.config.js` reads `libs/` dir names, so `museum-<name>` becomes a valid
|
|
163
|
+
scope automatically; no edit needed.
|
|
164
|
+
- **jest** — keep the reference `jest.config.ts` / `tsconfig.spec.json` so the shell unit-tests run.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# `Museum<Name>Module` + route registration
|
|
2
|
+
|
|
3
|
+
An article module wraps `ArticlesSharedModule.forFeature` over the npm `Article` entity and filters
|
|
4
|
+
its API requests with a `template` value. Thread `<PREFIX>` (the `environment` import) and the chosen
|
|
5
|
+
`<name>` / `<entity>` / `<template>`. The `Masonry` factory returns the real library in the browser
|
|
6
|
+
and a no-op mock during SSR. Copy the file structure from an existing article module in the reference
|
|
7
|
+
MobilEMS app and substitute the parameters.
|
|
8
|
+
|
|
9
|
+
## `libs/museum-<name>/shell/angular/src/lib/<name>.module.ts`
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { NgModule } from '@angular/core';
|
|
13
|
+
import { FormsModule } from '@angular/forms';
|
|
14
|
+
import { RouterModule } from '@angular/router';
|
|
15
|
+
import { SharedModule as SmartSharedModule } from '@smartsoft001/angular';
|
|
16
|
+
import { Article } from '@smartsoft001-mobilems/articles-domain';
|
|
17
|
+
import { ArticlesSharedModule } from '@smartsoft001-mobilems/articles-shell-angular';
|
|
18
|
+
import { Masonry } from '@thisissoon/angular-masonry';
|
|
19
|
+
|
|
20
|
+
import { environment } from '@<PREFIX>/angular';
|
|
21
|
+
|
|
22
|
+
import { routes } from './<name>.routes';
|
|
23
|
+
|
|
24
|
+
@NgModule({
|
|
25
|
+
providers: [
|
|
26
|
+
{
|
|
27
|
+
provide: Masonry,
|
|
28
|
+
useFactory: () => {
|
|
29
|
+
if (typeof window !== 'undefined' && (window as any)['Masonry']) {
|
|
30
|
+
return (window as any)['Masonry'];
|
|
31
|
+
}
|
|
32
|
+
// Return a mock for SSR
|
|
33
|
+
return class {
|
|
34
|
+
constructor() {
|
|
35
|
+
// no-op
|
|
36
|
+
}
|
|
37
|
+
layout() { return; }
|
|
38
|
+
reloadItems() { return; }
|
|
39
|
+
destroy() { return; }
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
imports: [
|
|
45
|
+
FormsModule,
|
|
46
|
+
SmartSharedModule,
|
|
47
|
+
ArticlesSharedModule.forFeature({
|
|
48
|
+
routing: false,
|
|
49
|
+
config: {
|
|
50
|
+
type: Article,
|
|
51
|
+
apiUrl: environment.apiUrl,
|
|
52
|
+
entity: '<entity>',
|
|
53
|
+
baseQuery: [
|
|
54
|
+
{ value: '<template>', key: 'template', type: '=', hidden: true },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
RouterModule.forChild(routes),
|
|
59
|
+
],
|
|
60
|
+
})
|
|
61
|
+
export class Museum<Name>Module {}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The **`baseQuery`** is what scopes the module to its content: `key` is always `'template'`, `type` is
|
|
65
|
+
`'='`, `hidden` is `true`, and `value` is the module's `<template>` (the API then returns only records
|
|
66
|
+
matching that template). Change only the `value` between modules.
|
|
67
|
+
|
|
68
|
+
### Optional: feature-local ngrx state
|
|
69
|
+
|
|
70
|
+
If the module keeps its own ngrx state, build `+state/<name>.reducer.ts` (see
|
|
71
|
+
`references/lib-contents.md`) exporting a `getReducer`, then in the module:
|
|
72
|
+
|
|
73
|
+
- pass `reducerFactory: getReducer` inside the `forFeature` `config`,
|
|
74
|
+
- define a `SERVICES` const in `lib/index.ts` that binds `ArticlesService` as the `CrudService`, and
|
|
75
|
+
spread it into the `providers` (alongside the `Masonry` provider).
|
|
76
|
+
|
|
77
|
+
(In the reference, the stateful article module also omits `SmartSharedModule` from its `imports` and
|
|
78
|
+
imports `ConfigsModule` from `@<PREFIX>/angular` — mirror whichever the reference module you copy
|
|
79
|
+
uses.) A module with no feature state omits `+state/` and the `reducerFactory` entirely.
|
|
80
|
+
|
|
81
|
+
## `<name>.routes.ts`
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { Route } from '@angular/router';
|
|
85
|
+
|
|
86
|
+
import { ItemComponent, ListComponent } from './pages';
|
|
87
|
+
|
|
88
|
+
export const routes: Route[] = [
|
|
89
|
+
{ path: ':id/:slug', component: ItemComponent },
|
|
90
|
+
{ path: ':id', component: ItemComponent },
|
|
91
|
+
{ path: '', component: ListComponent },
|
|
92
|
+
];
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Register in `apps/web/src/app/app.routes.ts`
|
|
96
|
+
|
|
97
|
+
Add the module's `<route>` to the `appRoutes` array (idempotently — if a `<route>` route already
|
|
98
|
+
exists, skip). Add it as a top-level route, and — if the module's results belong in site search — as a
|
|
99
|
+
child of the `wyszukaj/:text` route so `/wyszukaj/:text/<route>` works.
|
|
100
|
+
|
|
101
|
+
Top-level — add to `appRoutes`:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
{
|
|
105
|
+
path: '<route>',
|
|
106
|
+
loadChildren: () =>
|
|
107
|
+
import('@<PREFIX>/museum-<name>/shell/angular').then(
|
|
108
|
+
(m) => m.Museum<Name>Module,
|
|
109
|
+
),
|
|
110
|
+
},
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Search child — add to the `children` of the `wyszukaj/:text` route, before the
|
|
114
|
+
`{ path: '', redirectTo: ... }` default:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
{
|
|
118
|
+
path: '<route>',
|
|
119
|
+
loadChildren: () =>
|
|
120
|
+
import('@<PREFIX>/museum-<name>/shell/angular').then(
|
|
121
|
+
(m) => m.Museum<Name>Module,
|
|
122
|
+
),
|
|
123
|
+
},
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## The `/article-preview` page (build once)
|
|
127
|
+
|
|
128
|
+
The app's content-preview page (`apps/web/src/app/article-preview/`) renders an `Article` for CMS
|
|
129
|
+
preview mode, so it imports `@smartsoft001-mobilems/articles-domain` and belongs to the articles
|
|
130
|
+
feature — the foundation deliberately leaves it out. Build it the first time an article module is
|
|
131
|
+
added, and skip it if it already exists (idempotent across article-module runs):
|
|
132
|
+
|
|
133
|
+
1. Copy `apps/web/src/app/article-preview/` (`.component.ts` + `.html`) from the reference app, thread
|
|
134
|
+
`<PREFIX>`.
|
|
135
|
+
2. Add its route to `appRoutes` in `apps/web/src/app/app.routes.ts` (a plain top-level route, before
|
|
136
|
+
the `**` catch-all):
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
{
|
|
140
|
+
path: 'article-preview',
|
|
141
|
+
loadComponent: () =>
|
|
142
|
+
import('./article-preview/article-preview.component').then(
|
|
143
|
+
(m) => m.ArticlePreviewComponent,
|
|
144
|
+
),
|
|
145
|
+
},
|
|
146
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Article module shared components
|
|
2
|
+
|
|
3
|
+
An article module renders its records as cards in the grid view and rows in the list view, so it
|
|
4
|
+
contributes a pair of shared components to `libs/shared/angular` named after the module. Build them by
|
|
5
|
+
copying the card components of an existing article module in the reference MobilEMS app and adapting
|
|
6
|
+
them to the module's `<name>` and `<PREFIX>`. Every step is idempotent — if the target already exists,
|
|
7
|
+
leave it.
|
|
8
|
+
|
|
9
|
+
All paths below are under `libs/shared/angular/src/lib`.
|
|
10
|
+
|
|
11
|
+
## Article card components — build + export
|
|
12
|
+
|
|
13
|
+
Build these two component directories and export them from `components/index.ts`:
|
|
14
|
+
|
|
15
|
+
| Component | Renders |
|
|
16
|
+
| ----------------------------- | -------------------------------- |
|
|
17
|
+
| `components/<name>-grid-item` | an article card in the grid view |
|
|
18
|
+
| `components/<name>-list-item` | an article row in the list view |
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// components/index.ts — these exports belong to the <name> module:
|
|
22
|
+
export * from './<name>-grid-item';
|
|
23
|
+
export * from './<name>-list-item';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
These render an `Article` (via the foundation's generic `IArticle` model), so they depend on
|
|
27
|
+
`@smartsoft001-mobilems/articles-domain` — which this skill installs. There is no module-specific
|
|
28
|
+
model type to add (contrast `mobilems-objects`, which adds `ISpecificObject` to `objects.model.ts`).
|
|
29
|
+
|
|
30
|
+
## Optional: article gallery widget
|
|
31
|
+
|
|
32
|
+
If the project renders **article-gallery content blocks** (a page widget that embeds a gallery of
|
|
33
|
+
articles), build the article gallery widget once and register it in the widget content-type map. This
|
|
34
|
+
is shared by all article modules — build it the first time an article module needs it, and skip if a
|
|
35
|
+
previous article module already added it.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// components/widget/index.ts:
|
|
39
|
+
export * from './gallery-article/widget-gallery-article.component';
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Register it in the widget content-type map in `components/widget/widget.component.ts`:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { WidgetGalleryArticleComponent } from './gallery-article/widget-gallery-article.component';
|
|
46
|
+
// …in the ArticleWidgetType → component map:
|
|
47
|
+
[ArticleWidgetType.GALLERY_ARTICLE]: WidgetGalleryArticleComponent,
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The widget container renders sub-widgets via `NgComponentOutlet`, so the gallery widget goes in the
|
|
51
|
+
type-map only — it does **not** go in the container's `imports` array.
|
|
52
|
+
|
|
53
|
+
## Verify
|
|
54
|
+
|
|
55
|
+
`scripts/verify-module.sh <workspace> <PREFIX> <name>` checks the module's `<name>-grid-item` and
|
|
56
|
+
`<name>-list-item` exist and are exported from the components barrel.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# detect-init.sh <workspace-root>
|
|
4
|
+
#
|
|
5
|
+
# Precondition gate for mobilems-objects (and the other module skills follow the same shape):
|
|
6
|
+
# confirm the MobilEMS foundation from mobilems-project-init is present.
|
|
7
|
+
#
|
|
8
|
+
# Exit 0 → foundation present, OK to add the module.
|
|
9
|
+
# Exit 3 → foundation missing (tell the user to run /mobilems:project-init first).
|
|
10
|
+
set -eu
|
|
11
|
+
|
|
12
|
+
ROOT="${1:?usage: detect-init.sh <workspace-root>}"
|
|
13
|
+
cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
|
|
14
|
+
|
|
15
|
+
miss() {
|
|
16
|
+
echo "FOUNDATION MISSING: $1" >&2
|
|
17
|
+
echo "Run /mobilems:project-init first to lay down the MobilEMS foundation." >&2
|
|
18
|
+
exit 3
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
[ -f apps/web/src/app/app.config.ts ] || miss "app.config.ts missing"
|
|
22
|
+
grep -q "SharedModule.forRoot" apps/web/src/app/app.config.ts || miss "SharedModule.forRoot not in app.config.ts (foundation not applied)"
|
|
23
|
+
grep -q "./lib/components" libs/shared/angular/src/index.ts 2>/dev/null || miss "shared-lib components barrel not populated"
|
|
24
|
+
[ -f apps/web/src/app/app.routes.ts ] || miss "app.routes.ts missing"
|
|
25
|
+
|
|
26
|
+
PREFIX="$(grep -oE '@[a-z0-9-]+/angular' tsconfig.base.json | head -1 | sed -E 's#@([a-z0-9-]+)/angular#\1#')"
|
|
27
|
+
echo "FOUNDATION OK. Detected prefix: ${PREFIX:-<unknown>}"
|
|
28
|
+
[ -d libs/museum-objects ] && echo "NOTE: libs/museum-objects already exists — build steps must be idempotent." >&2
|
|
29
|
+
exit 0
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# verify-module.sh <workspace-root> <PREFIX> <name> [template]
|
|
4
|
+
#
|
|
5
|
+
# Post-condition check for one article module named <name>: library created, ArticlesSharedModule
|
|
6
|
+
# .forFeature wired (with the template value if given), route registered, and the module's article
|
|
7
|
+
# card shared components built + exported.
|
|
8
|
+
set -eu
|
|
9
|
+
|
|
10
|
+
ROOT="${1:?usage: verify-module.sh <workspace-root> <PREFIX> <name> [template]}"
|
|
11
|
+
PREFIX="${2:?usage: verify-module.sh <workspace-root> <PREFIX> <name> [template]}"
|
|
12
|
+
NAME="${3:?usage: verify-module.sh <workspace-root> <PREFIX> <name> [template]}"
|
|
13
|
+
TEMPLATE="${4:-}"
|
|
14
|
+
cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
|
|
15
|
+
|
|
16
|
+
LIBF="museum-$NAME"
|
|
17
|
+
fail=0
|
|
18
|
+
pass() { echo " PASS: $1"; }
|
|
19
|
+
bad() { echo " FAIL: $1" >&2; fail=1; }
|
|
20
|
+
info() { echo " INFO: $1"; }
|
|
21
|
+
|
|
22
|
+
echo "== Verifying article module: name=$NAME lib=$LIBF =="
|
|
23
|
+
|
|
24
|
+
echo "== Library =="
|
|
25
|
+
MOD="$(ls libs/$LIBF/shell/angular/src/lib/*.module.ts 2>/dev/null | head -1)"
|
|
26
|
+
[ -n "$MOD" ] && pass "module file present ($MOD)" || bad "no *.module.ts in libs/$LIBF/shell/angular/src/lib"
|
|
27
|
+
[ -f "libs/$LIBF/domain/src/lib/entities/index.ts" ] && pass "domain entity present" || bad "domain entity missing"
|
|
28
|
+
grep -q "$LIBF/shell/angular" tsconfig.base.json && pass "tsconfig shell path added" || bad "tsconfig.base.json shell path missing"
|
|
29
|
+
grep -q "$LIBF/domain" tsconfig.base.json && pass "tsconfig domain path added" || bad "tsconfig.base.json domain path missing"
|
|
30
|
+
|
|
31
|
+
echo "== forFeature wiring =="
|
|
32
|
+
if [ -n "$MOD" ]; then
|
|
33
|
+
grep -q "ArticlesSharedModule.forFeature" "$MOD" && pass "ArticlesSharedModule.forFeature wired" || bad "ArticlesSharedModule.forFeature missing"
|
|
34
|
+
grep -q "key: 'template'" "$MOD" && pass "baseQuery filters on key 'template'" || bad "baseQuery key 'template' missing"
|
|
35
|
+
if [ -n "$TEMPLATE" ]; then
|
|
36
|
+
grep -q "$TEMPLATE" "$MOD" && pass "template value '$TEMPLATE' present" || bad "template value '$TEMPLATE' missing"
|
|
37
|
+
fi
|
|
38
|
+
grep -q "reducerFactory" "$MOD" && info "module keeps feature state (reducerFactory present)" || info "module has no feature state"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
echo "== Route registration =="
|
|
42
|
+
ROUTES=apps/web/src/app/app.routes.ts
|
|
43
|
+
n=$(grep -c "$LIBF/shell/angular" "$ROUTES" 2>/dev/null || true)
|
|
44
|
+
[ "${n:-0}" -ge 1 ] && pass "route registered ($n occurrence(s) — top-level$([ "${n:-0}" -ge 2 ] && echo ' + search child'))" || bad "route not in app.routes.ts"
|
|
45
|
+
|
|
46
|
+
echo "== Article card shared components =="
|
|
47
|
+
COMP=libs/shared/angular/src/lib/components
|
|
48
|
+
for d in "$NAME-grid-item" "$NAME-list-item"; do
|
|
49
|
+
[ -d "$COMP/$d" ] && pass "built $d" || bad "missing $COMP/$d"
|
|
50
|
+
done
|
|
51
|
+
grep -q "$NAME-grid-item" "$COMP/index.ts" && pass "$NAME-grid-item exported" || bad "$NAME-grid-item export missing"
|
|
52
|
+
grep -q "$NAME-list-item" "$COMP/index.ts" && pass "$NAME-list-item exported" || bad "$NAME-list-item export missing"
|
|
53
|
+
|
|
54
|
+
echo "== Deps =="
|
|
55
|
+
for p in articles-domain articles-shell-angular articles-shell-dtos; do
|
|
56
|
+
grep -q "@smartsoft001-mobilems/$p" package.json && pass "$p dep" || bad "$p dep missing"
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
echo
|
|
60
|
+
if [ "$fail" = 0 ]; then echo "VERIFY: all checks passed."; else echo "VERIFY: FAILED — fix the items above and re-run." >&2; fi
|
|
61
|
+
exit "$fail"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mobilems-events
|
|
3
|
+
description: Add the museum EVENTS feature module (wydarzenia) to a MobilEMS app initialized with mobilems-project-init. Use whenever the user wants to add / scaffold / wire the events module, the "wydarzenia" pages (event list + event detail + booking/reservation flow with confirm + cancel), event booking, event reservation, an event calendar / available-dates picker, or asks to "dodaj moduł wydarzeń / rezerwacji wydarzeń". Creates libs/museum-events (shell/angular only — a standalone booking module), registers the /wydarzenia route, and confirms the foundation packages it reuses are present. Requires the MobilEMS foundation to exist first.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# MobilEMS events module
|
|
9
|
+
|
|
10
|
+
Add the **events** (wydarzenia) feature module to a MobilEMS app whose foundation was built by
|
|
11
|
+
`mobilems-project-init`. Events is a self-contained booking / reservation system: a list of events,
|
|
12
|
+
an event detail page, and a reservation flow (booking form -> confirmation -> cancellation) driven by
|
|
13
|
+
its own `EventsReservationService` over the event / event-periods / event-reservations API. This
|
|
14
|
+
skill builds the `libs/museum-events` library and registers a single top-level `/wydarzenia` route.
|
|
15
|
+
|
|
16
|
+
Events has a leaner shape than the other feature modules: a pure `RouterModule.forChild` module with
|
|
17
|
+
**lazy `loadComponent` routes** and **no shared-module `forFeature`**, and it ships **only a
|
|
18
|
+
`shell/angular` project** (there is no `domain` library). All of its UI and data access live inside
|
|
19
|
+
its own `shell/angular`.
|
|
20
|
+
|
|
21
|
+
## Precondition
|
|
22
|
+
|
|
23
|
+
Run `scripts/detect-init.sh <workspace-root>`. It confirms the MobilEMS foundation is present
|
|
24
|
+
(`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If not, tell the user to
|
|
25
|
+
run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`; the reference
|
|
26
|
+
MobilEMS app uses `@msr`) and the reference repo path (same one init used).
|
|
27
|
+
|
|
28
|
+
## Steps
|
|
29
|
+
|
|
30
|
+
1. **npm deps (precondition check)** — events imports only foundation packages:
|
|
31
|
+
`@smartsoft001-mobilems/angular`, `@smartsoft001-mobilems/models`, `@smartsoft001/angular`,
|
|
32
|
+
`@thisissoon/angular-masonry`, and `@ngx-translate/core` — all already installed by
|
|
33
|
+
`mobilems-project-init`. So this step is a CHECK: confirm those lines are in `package.json`; there
|
|
34
|
+
is nothing new to add. As a safeguard, grep the copied lib for any third-party import not in that
|
|
35
|
+
foundation set and add only what is genuinely missing. Do not `npm install` unless asked.
|
|
36
|
+
2. **Library** — create `libs/museum-events/shell/angular` per `references/lib-contents.md`: copy the
|
|
37
|
+
shell (`MuseumEventsModule`, `events.routes.ts`, `pages/{list,item,booking,confirm,cancel}`,
|
|
38
|
+
`services/events-reservation.service.ts`, `models/events.models.ts`, `pipes/time-format.pipe.ts`,
|
|
39
|
+
and the `src/__mocks__/@smartsoft001-mobilems/{angular,models}.ts` jest mocks), thread `<PREFIX>`.
|
|
40
|
+
Add the single `tsconfig.base.json` path `@<PREFIX>/museum-events/shell/angular` and the
|
|
41
|
+
`museum-events` commit scope.
|
|
42
|
+
3. **Module + routes** — see `references/module-and-routes.md`: `MuseumEventsModule` is just
|
|
43
|
+
`@NgModule({ imports: [RouterModule.forChild(eventsRoutes)] })`; `eventsRoutes` uses lazy
|
|
44
|
+
`loadComponent` for every page and provides the `Masonry` SSR factory on the list route. Add the
|
|
45
|
+
lazy `wydarzenia` route to the `appRoutes` array in `apps/web/src/app/app.routes.ts` as a single
|
|
46
|
+
top-level route.
|
|
47
|
+
4. **Verify** — run `scripts/verify-module.sh <workspace-root> <PREFIX>`: asserts the
|
|
48
|
+
`libs/museum-events/shell/angular` lib exists with `MuseumEventsModule` (`RouterModule.forChild`)
|
|
49
|
+
and `events.routes.ts`, the `@<PREFIX>/museum-events/shell/angular` tsconfig path is added, the
|
|
50
|
+
five pages + `EventsReservationService` + `events.models.ts` + `TimeFormatPipe` are present, the
|
|
51
|
+
`wydarzenia` route is registered as a single top-level route, and the foundation deps it reuses are
|
|
52
|
+
in `package.json`. Fix and re-run.
|
|
53
|
+
|
|
54
|
+
## Notes
|
|
55
|
+
|
|
56
|
+
- Events is a standalone booking module: it owns its data access (`EventsReservationService` calling
|
|
57
|
+
the events API) and contributes **nothing** to `libs/shared/angular`. The shared lib stays
|
|
58
|
+
untouched.
|
|
59
|
+
- Events ships **only** a `shell/angular` project — there is no `domain` library and no shared-module
|
|
60
|
+
`forFeature`; its event/reservation types live in `shell/angular/src/lib/models/events.models.ts`.
|
|
61
|
+
- `/wydarzenia` is a top-level route only. Events is a booking destination, not a search result, so it
|
|
62
|
+
is NOT added under the `wyszukaj/:text` search children.
|
|
63
|
+
- This skill ONLY touches the events module + its route. It never adds other feature modules, never
|
|
64
|
+
re-runs the foundation, and never edits `libs/shared/angular`.
|
|
65
|
+
- Bundled `references/` + `scripts/` ship automatically. Do not hand-edit `plugin.json`.
|