@smartsoft001-mobilems/claude-plugins 2.86.0 → 2.88.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 +108 -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 +77 -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 +80 -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 +151 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/app-shell.md +115 -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/qa-hardening.md +124 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/server-env.md +97 -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 +112 -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 +81 -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
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mobilems-partners
|
|
3
|
+
description: Add the museum PARTNERS feature module (partnerzy) to a MobilEMS app initialized with mobilems-project-init. Use whenever the user wants to add / scaffold / wire the partners (partnerzy) module, the museum partners list + detail pages (partner cards, map, connected objects), or asks to "dodaj moduł partnerów / partnerzy". Creates libs/museum-partners (domain + shell/angular), registers the /partnerzy route (top-level + search child), and adds the Articles + objects-domain npm packages it reuses. 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 partners module
|
|
9
|
+
|
|
10
|
+
Add the **partners** (partnerzy) feature module on top of a MobilEMS foundation produced by
|
|
11
|
+
`mobilems-project-init`. This module's entire UI lives inside its own `libs/museum-partners`
|
|
12
|
+
library, so this skill builds that library and registers the `partnerzy` route.
|
|
13
|
+
|
|
14
|
+
Partners is a list/detail feature where each entry is a partnering institution: a `ListComponent`
|
|
15
|
+
with grid/list partner cards (sortable, paged, masonry layout) plus an `ItemComponent` that renders
|
|
16
|
+
the partner detail (logo, description, map location, and the museum objects connected to that
|
|
17
|
+
partner). It is backed by `ArticlesSharedModule.forFeature` over the Articles `Article` type — it
|
|
18
|
+
REUSES the Articles shared module (like the thematic-paths module) but with its OWN domain entity
|
|
19
|
+
(`MuseumPartner`), its OWN reducer (`getReducer`), and **no `baseQuery`**.
|
|
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`; reference uses
|
|
26
|
+
`@msr`) and the reference repo path (same one init used).
|
|
27
|
+
|
|
28
|
+
## Steps
|
|
29
|
+
|
|
30
|
+
1. **npm deps** — add `@smartsoft001-mobilems/articles-domain`,
|
|
31
|
+
`@smartsoft001-mobilems/articles-shell-angular`, `@smartsoft001-mobilems/articles-shell-dtos`
|
|
32
|
+
(this module reuses the Articles shared module — `ArticlesSharedModule` + `ArticlesService`) and
|
|
33
|
+
`@smartsoft001-mobilems/objects-domain` (the `MuseumPartner` entity types its `connections` with
|
|
34
|
+
`IMsObjectConnection` from objects-domain). Also add `lodash-decorators` (the list page uses
|
|
35
|
+
`@debounce` for its date filter). Grep the copied pages/components for any other third-party
|
|
36
|
+
imports and add those too. Match the installed mobilems line; resolve versions at run time; do
|
|
37
|
+
not `npm install` unless asked. Adding is idempotent — if a sibling module already installed
|
|
38
|
+
these, nothing changes. (`@smartsoft001-mobilems/{angular,models}` and
|
|
39
|
+
`@thisissoon/angular-masonry` are already foundation deps.)
|
|
40
|
+
2. **Library** — create `libs/museum-partners/{domain,shell/angular}` per
|
|
41
|
+
`references/lib-contents.md`: copy the domain entity (`MuseumPartner`) and the shell
|
|
42
|
+
(`MuseumPartnersModule`, routes, `+state/partners.reducer.ts`, `components/{grid-item,list-item}`,
|
|
43
|
+
`pages/{list,item}`, `services/partners`), thread `<PREFIX>`. Add the two `tsconfig.base.json`
|
|
44
|
+
paths and the `museum-partners` commit scope.
|
|
45
|
+
3. **Module + routes** — see `references/module-and-routes.md`: `MuseumPartnersModule` wraps
|
|
46
|
+
`ArticlesSharedModule.forFeature({ routing: false, config: { reducerFactory: getReducer, type: Article, apiUrl, entity: 'partners' } })`
|
|
47
|
+
(note: **no baseQuery**) with the `Masonry` SSR factory and provides `SERVICES`. Add the lazy
|
|
48
|
+
`partnerzy` route to the `appRoutes` array in `apps/web/src/app/app.routes.ts` as a top-level
|
|
49
|
+
route, and a matching child under the `wyszukaj/:text` search route (partners appears in site
|
|
50
|
+
search).
|
|
51
|
+
4. **Verify** — run `scripts/verify-module.sh <workspace-root> <PREFIX>`: asserts the lib + entity
|
|
52
|
+
exist, both tsconfig paths are added, `ArticlesSharedModule.forFeature` is wired with
|
|
53
|
+
`entity: 'partners'` + `reducerFactory: getReducer`, the `+state/partners.reducer.ts` exports
|
|
54
|
+
`getReducer`, the `components/{grid-item,list-item}` cards are present in the partners lib, the
|
|
55
|
+
`partnerzy` route is registered (top-level + search child), and the Articles + objects-domain deps
|
|
56
|
+
are in `package.json`. Fix and re-run.
|
|
57
|
+
|
|
58
|
+
## Notes
|
|
59
|
+
|
|
60
|
+
- Partners contributes nothing to `libs/shared/angular`: its grid/list cards live in its own lib, and
|
|
61
|
+
the `partners-carousel` foundation component is already built by `mobilems-project-init`. So this
|
|
62
|
+
skill builds only the partners module + its route — there is no shared-components reference.
|
|
63
|
+
- This skill ONLY touches the partners module + its route. It never adds other feature modules,
|
|
64
|
+
never re-runs the foundation, and never edits `libs/shared/angular`.
|
|
65
|
+
- Bundled `references/` + `scripts/` ship automatically. Do not hand-edit `plugin.json`.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# libs/museum-partners — structure to create
|
|
2
|
+
|
|
3
|
+
Copy from the reference MobilEMS app's `libs/museum-partners` and thread `<PREFIX>`. The lib has two
|
|
4
|
+
projects: a `domain` (the entity) and a `shell/angular` (the Angular module + NgRx `+state` + UI).
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
libs/museum-partners/
|
|
8
|
+
├── domain/ # project: museum-partners-domain
|
|
9
|
+
│ ├── package.json # "name": "museum-partners-domain"
|
|
10
|
+
│ ├── jest.config.js # displayName + coverageDirectory under libs/museum-partners/domain
|
|
11
|
+
│ └── src/
|
|
12
|
+
│ ├── index.ts # export * from './lib/entities';
|
|
13
|
+
│ ├── features.ts # export const DOMAIN_SERVICES = [];
|
|
14
|
+
│ └── lib/
|
|
15
|
+
│ └── entities/ # museum-partner.entity.ts (MuseumPartner) + index
|
|
16
|
+
└── shell/angular/ # project: museum-partners-shell-angular
|
|
17
|
+
└── src/
|
|
18
|
+
├── index.ts # export * from './lib/pages'; export * from './lib/partners.module';
|
|
19
|
+
└── lib/
|
|
20
|
+
├── index.ts # export * from './pages'; './components'; './partners.module';
|
|
21
|
+
├── partners.module.ts # MuseumPartnersModule (references/module-and-routes.md)
|
|
22
|
+
├── partners.routes.ts
|
|
23
|
+
├── +state/ # partners.reducer.ts (getReducer)
|
|
24
|
+
├── components/ # grid-item (GridItemComponent), list-item (ListItemComponent) + index
|
|
25
|
+
├── pages/ # list (ListComponent), item (ItemComponent) + index
|
|
26
|
+
└── services/ # partners/partners.service.ts + index (SERVICES)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> The partner cards (`grid-item`, `list-item`) live in **this** lib, not in `libs/shared/angular`.
|
|
30
|
+
> Copy only what the reference has — there is no `+state/*.facade.ts`/`*.selectors.ts` and no
|
|
31
|
+
> `directives/` dir in this module; the only `+state` file is `partners.reducer.ts`.
|
|
32
|
+
|
|
33
|
+
## Domain entity (verbatim — thread nothing; pure types)
|
|
34
|
+
|
|
35
|
+
This entity `implements IEntity<string>` — it does NOT extend the Articles `Article` base class. It
|
|
36
|
+
pulls `MsFile` from `@smartsoft001-mobilems/models` and **`IMsObjectConnection` from
|
|
37
|
+
`@smartsoft001-mobilems/objects-domain`** (the cross-module type that drives the SKILL.md
|
|
38
|
+
objects-domain dep). Copy `domain/src/lib/entities/museum-partner.entity.ts` verbatim:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { IEntity } from '@smartsoft001/domain-core';
|
|
42
|
+
import { Model, Field } from '@smartsoft001/models';
|
|
43
|
+
import { MsFile } from '@smartsoft001-mobilems/models';
|
|
44
|
+
import { IMsObjectConnection } from '@smartsoft001-mobilems/objects-domain';
|
|
45
|
+
|
|
46
|
+
@Model({})
|
|
47
|
+
export class MuseumPartner implements IEntity<string> {
|
|
48
|
+
id!: string;
|
|
49
|
+
|
|
50
|
+
@Field()
|
|
51
|
+
name!: string;
|
|
52
|
+
|
|
53
|
+
@Field()
|
|
54
|
+
additionalInfo!: string;
|
|
55
|
+
|
|
56
|
+
@Field()
|
|
57
|
+
geoLocation!: any;
|
|
58
|
+
|
|
59
|
+
@Field()
|
|
60
|
+
latitude!: number;
|
|
61
|
+
|
|
62
|
+
@Field()
|
|
63
|
+
longitude!: number;
|
|
64
|
+
|
|
65
|
+
@Field()
|
|
66
|
+
objectCount!: number;
|
|
67
|
+
|
|
68
|
+
@Field()
|
|
69
|
+
extraData!: {
|
|
70
|
+
name: string;
|
|
71
|
+
phone: string;
|
|
72
|
+
emailAddress?: string;
|
|
73
|
+
description: string;
|
|
74
|
+
shortDescription: string;
|
|
75
|
+
address: string;
|
|
76
|
+
openingHours: number;
|
|
77
|
+
ticketPrices: number;
|
|
78
|
+
logo: MsFile;
|
|
79
|
+
image: MsFile;
|
|
80
|
+
website: string;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
@Field()
|
|
84
|
+
connections?: Array<IMsObjectConnection>;
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Shell `+state` (NgRx — copy verbatim, thread `<PREFIX>`)
|
|
89
|
+
|
|
90
|
+
- `partners.reducer.ts` exports `getReducer()` — wraps `@smartsoft001/crud-shell-angular`'s
|
|
91
|
+
`getReducer('partners')` and returns a `(state, action) => …` reducer. This is the function passed
|
|
92
|
+
as `reducerFactory` in the module config. The reducer's `State` type is `SmartState<Article>`
|
|
93
|
+
(from `@smartsoft001-mobilems/articles-domain`), since the module is configured over `Article`.
|
|
94
|
+
|
|
95
|
+
## Services — `services/index.ts` exports a `SERVICES` array (copy verbatim)
|
|
96
|
+
|
|
97
|
+
`PartnersService extends CrudBaseService<MuseumPartner>` (from `@smartsoft001-mobilems/angular`) and
|
|
98
|
+
points its list/detail URLs at the partners (`dictionary/Department`) endpoints, enriching list items
|
|
99
|
+
with geodata. It is bound to BOTH `CrudService` and `ArticlesService` (the reused Articles shared
|
|
100
|
+
module resolves `ArticlesService`):
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { CrudService } from '@smartsoft001/crud-shell-angular';
|
|
104
|
+
import { ArticlesService } from '@smartsoft001-mobilems/articles-shell-angular';
|
|
105
|
+
|
|
106
|
+
import { PartnersService } from './partners/partners.service';
|
|
107
|
+
|
|
108
|
+
export * from './partners/partners.service';
|
|
109
|
+
|
|
110
|
+
export const SERVICES = [
|
|
111
|
+
PartnersService,
|
|
112
|
+
{
|
|
113
|
+
provide: CrudService,
|
|
114
|
+
useClass: PartnersService,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
provide: ArticlesService,
|
|
118
|
+
useClass: PartnersService,
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## UI components & pages
|
|
124
|
+
|
|
125
|
+
- `components/grid-item` (`GridItemComponent`, selector `app-partners-grid-item`) and
|
|
126
|
+
`components/list-item` (`ListItemComponent`, selector `app-partners-list-item`) are the partner
|
|
127
|
+
cards used by the list page; `components/index.ts` re-exports both. Copy verbatim, thread
|
|
128
|
+
`<PREFIX>`.
|
|
129
|
+
- `pages/list` (`ListComponent extends ArticlesListComponent`) renders the sortable/paged masonry
|
|
130
|
+
grid; it uses `@debounce` from `lodash-decorators` (the SKILL.md dep) for its date filter.
|
|
131
|
+
- `pages/item` (`ItemComponent extends ArticlesItemComponent`) renders the partner detail with map
|
|
132
|
+
and connected objects (it composes foundation components from `@<PREFIX>/angular` such as
|
|
133
|
+
`MapComponent` and `ObjectsGridItemComponent`).
|
|
134
|
+
|
|
135
|
+
## Wiring
|
|
136
|
+
|
|
137
|
+
- **tsconfig.base.json paths** (add both):
|
|
138
|
+
```json
|
|
139
|
+
"@<PREFIX>/museum-partners/domain": ["libs/museum-partners/domain/src/index.ts"],
|
|
140
|
+
"@<PREFIX>/museum-partners/shell/angular": ["libs/museum-partners/shell/angular/src/index.ts"]
|
|
141
|
+
```
|
|
142
|
+
- **project.json / build config** — copy each project's config (names `museum-partners-domain`,
|
|
143
|
+
`museum-partners-shell-angular`) and normalize every path field the reference baked to its origin
|
|
144
|
+
location — `sourceRoot`, `dest`, `outputPath`, `coverageDirectory`, `jestConfig`,
|
|
145
|
+
`lintFilePatterns`, and the jest `displayName` — to `libs/museum-partners/...`. Some reference
|
|
146
|
+
fields are stale and need fixing: the shell `project.json` `sourceRoot` reads
|
|
147
|
+
`libs/museum-partners/src` (correct it to `libs/museum-partners/shell/angular/src`), and the shell
|
|
148
|
+
`jest.config.ts` `coverageDirectory` points at a sibling's `coverage/packages/...` path (correct it
|
|
149
|
+
to `coverage/libs/museum-partners/shell/angular`); the domain `package.json` `name` reads
|
|
150
|
+
`museum-objects-domain` (correct it to `museum-partners-domain`). Others are already correct (the
|
|
151
|
+
domain `jest.config.js` `displayName`/`coverageDirectory` are right) — verify each against a
|
|
152
|
+
known-good sibling and fix only the stale ones. A `domain` with no `project.json` (Nx-inferred) is
|
|
153
|
+
fine — just set its `package.json` `name`.
|
|
154
|
+
- **commit scope** — `commitlint.config.js` reads `libs/` dir names, so `museum-partners` becomes a
|
|
155
|
+
valid scope automatically; no edit needed.
|
|
156
|
+
|
|
157
|
+
This module reuses the Articles shared module, so it needs the three `@smartsoft001-mobilems/articles-*`
|
|
158
|
+
npm packages plus `@smartsoft001-mobilems/objects-domain` (for `IMsObjectConnection`) and
|
|
159
|
+
`lodash-decorators` (see SKILL.md step 1). `@angular/cdk`, `@thisissoon/angular-masonry`,
|
|
160
|
+
`@ngx-translate/core`, and `@smartsoft001-mobilems/{angular,models}` are already foundation deps.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# MuseumPartnersModule + route registration
|
|
2
|
+
|
|
3
|
+
## `libs/museum-partners/shell/angular/src/lib/partners.module.ts`
|
|
4
|
+
|
|
5
|
+
Thread `<PREFIX>` (the `environment` import). This module REUSES `ArticlesSharedModule` (the same
|
|
6
|
+
shared module the news/articles/thematic-paths modules use) but configures it over the Articles
|
|
7
|
+
`Article` type with its OWN `getReducer` and **no `baseQuery`**. The `Masonry` factory returns the
|
|
8
|
+
real library in the browser and a no-op mock during SSR (identical to objects). It provides the
|
|
9
|
+
`SERVICES` array (which binds both `CrudService` and `ArticlesService` to `PartnersService`).
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { NgModule } from '@angular/core';
|
|
13
|
+
import { FormsModule } from '@angular/forms';
|
|
14
|
+
import { RouterModule } from '@angular/router';
|
|
15
|
+
import { Article } from '@smartsoft001-mobilems/articles-domain';
|
|
16
|
+
import { ArticlesSharedModule } from '@smartsoft001-mobilems/articles-shell-angular';
|
|
17
|
+
import { Masonry } from '@thisissoon/angular-masonry';
|
|
18
|
+
|
|
19
|
+
import { environment } from '@<PREFIX>/angular';
|
|
20
|
+
|
|
21
|
+
import { getReducer } from './+state/partners.reducer';
|
|
22
|
+
import { routes } from './partners.routes';
|
|
23
|
+
import { SERVICES } from './services';
|
|
24
|
+
|
|
25
|
+
@NgModule({
|
|
26
|
+
providers: [
|
|
27
|
+
...SERVICES,
|
|
28
|
+
{
|
|
29
|
+
provide: Masonry,
|
|
30
|
+
useFactory: () => {
|
|
31
|
+
if (typeof window !== 'undefined' && (window as any)['Masonry']) {
|
|
32
|
+
return (window as any)['Masonry'];
|
|
33
|
+
}
|
|
34
|
+
// Return a mock for SSR
|
|
35
|
+
return class {
|
|
36
|
+
constructor() {
|
|
37
|
+
// no-op
|
|
38
|
+
}
|
|
39
|
+
layout() {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
reloadItems() {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
destroy() {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
imports: [
|
|
53
|
+
FormsModule,
|
|
54
|
+
ArticlesSharedModule.forFeature({
|
|
55
|
+
routing: false,
|
|
56
|
+
config: {
|
|
57
|
+
reducerFactory: getReducer,
|
|
58
|
+
type: Article,
|
|
59
|
+
apiUrl: environment.apiUrl,
|
|
60
|
+
entity: 'partners',
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
RouterModule.forChild(routes),
|
|
64
|
+
],
|
|
65
|
+
})
|
|
66
|
+
export class MuseumPartnersModule {}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> Contrast with `mobilems-objects` (`ObjectsSharedModule.forFeature` over `MsObject`, no
|
|
70
|
+
> `reducerFactory`): here the module reuses `ArticlesSharedModule`, supplies `type: Article` with
|
|
71
|
+
> `reducerFactory: getReducer`, and there is deliberately **no `baseQuery`** in the config.
|
|
72
|
+
|
|
73
|
+
## `partners.routes.ts`
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { Route } from '@angular/router';
|
|
77
|
+
|
|
78
|
+
import { ItemComponent, ListComponent } from './pages';
|
|
79
|
+
|
|
80
|
+
export const routes: Route[] = [
|
|
81
|
+
{
|
|
82
|
+
path: ':id',
|
|
83
|
+
component: ItemComponent,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
path: ':id/:slug',
|
|
87
|
+
component: ItemComponent,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
path: '',
|
|
91
|
+
component: ListComponent,
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Register in `apps/web/src/app/app.routes.ts`
|
|
97
|
+
|
|
98
|
+
Add the partners route to the `appRoutes` array (idempotently — if a `partnerzy` route already
|
|
99
|
+
exists, skip). Add it as a top-level route, and as a child of the `wyszukaj/:text` search route so
|
|
100
|
+
`/wyszukaj/:text/partnerzy` works and partners shows up among the search results.
|
|
101
|
+
|
|
102
|
+
Top-level — add to `appRoutes`:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
{
|
|
106
|
+
path: 'partnerzy',
|
|
107
|
+
loadChildren: () =>
|
|
108
|
+
import('@<PREFIX>/museum-partners/shell/angular').then(
|
|
109
|
+
(m) => m.MuseumPartnersModule,
|
|
110
|
+
),
|
|
111
|
+
},
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Search child — add to the `children` of the `wyszukaj/:text` route, before the
|
|
115
|
+
`{ path: '', redirectTo: 'obiekty' }` default:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
{
|
|
119
|
+
path: 'partnerzy',
|
|
120
|
+
loadChildren: () =>
|
|
121
|
+
import('@<PREFIX>/museum-partners/shell/angular').then(
|
|
122
|
+
(m) => m.MuseumPartnersModule,
|
|
123
|
+
),
|
|
124
|
+
},
|
|
125
|
+
```
|
|
@@ -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. Portable to macOS bash 3.2.
|
|
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,59 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# verify-module.sh <workspace-root> <PREFIX>
|
|
4
|
+
#
|
|
5
|
+
# Post-condition check for mobilems-partners: library created, ArticlesSharedModule wired over the
|
|
6
|
+
# Article type with its own reducer, the partner cards present in the partners lib, the partnerzy
|
|
7
|
+
# route registered (top-level + search child), and the Articles + objects-domain deps installed.
|
|
8
|
+
# macOS bash 3.2 safe.
|
|
9
|
+
set -eu
|
|
10
|
+
|
|
11
|
+
ROOT="${1:?usage: verify-module.sh <workspace-root> <PREFIX>}"
|
|
12
|
+
PREFIX="${2:?usage: verify-module.sh <workspace-root> <PREFIX>}"
|
|
13
|
+
cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
|
|
14
|
+
|
|
15
|
+
fail=0
|
|
16
|
+
pass() { echo " PASS: $1"; }
|
|
17
|
+
bad() { echo " FAIL: $1" >&2; fail=1; }
|
|
18
|
+
|
|
19
|
+
MOD=libs/museum-partners/shell/angular/src/lib/partners.module.ts
|
|
20
|
+
|
|
21
|
+
echo "== Library =="
|
|
22
|
+
[ -f "$MOD" ] && pass "MuseumPartnersModule present" || bad "partners.module.ts missing"
|
|
23
|
+
[ -f libs/museum-partners/domain/src/lib/entities/museum-partner.entity.ts ] && pass "MuseumPartner entity present" || bad "domain entity missing"
|
|
24
|
+
grep -q "class MuseumPartner" libs/museum-partners/domain/src/lib/entities/museum-partner.entity.ts 2>/dev/null && pass "MuseumPartner class declared" || bad "MuseumPartner class not declared"
|
|
25
|
+
grep -q "museum-partners/shell/angular" tsconfig.base.json && pass "tsconfig shell path added" || bad "tsconfig shell path missing"
|
|
26
|
+
grep -q "museum-partners/domain" tsconfig.base.json && pass "tsconfig domain path added" || bad "tsconfig domain path missing"
|
|
27
|
+
|
|
28
|
+
echo "== Module wiring =="
|
|
29
|
+
grep -q "ArticlesSharedModule.forFeature" "$MOD" 2>/dev/null && pass "ArticlesSharedModule.forFeature wired" || bad "ArticlesSharedModule.forFeature missing"
|
|
30
|
+
grep -q "entity: 'partners'" "$MOD" 2>/dev/null && pass "entity 'partners' configured" || bad "entity 'partners' missing"
|
|
31
|
+
grep -q "reducerFactory: getReducer" "$MOD" 2>/dev/null && pass "reducerFactory: getReducer configured" || bad "reducerFactory: getReducer missing"
|
|
32
|
+
|
|
33
|
+
echo "== +state =="
|
|
34
|
+
STATE=libs/museum-partners/shell/angular/src/lib/+state
|
|
35
|
+
[ -f "$STATE/partners.reducer.ts" ] && pass "reducer present" || bad "partners.reducer.ts missing"
|
|
36
|
+
grep -q "export function getReducer" "$STATE/partners.reducer.ts" 2>/dev/null && pass "getReducer exported" || bad "getReducer not exported in reducer"
|
|
37
|
+
|
|
38
|
+
echo "== UI components =="
|
|
39
|
+
COMP=libs/museum-partners/shell/angular/src/lib/components
|
|
40
|
+
[ -f "$COMP/grid-item/grid-item.component.ts" ] && pass "grid-item card present" || bad "components/grid-item missing"
|
|
41
|
+
[ -f "$COMP/list-item/list-item.component.ts" ] && pass "list-item card present" || bad "components/list-item missing"
|
|
42
|
+
|
|
43
|
+
echo "== Route registration =="
|
|
44
|
+
ROUTES=apps/web/src/app/app.routes.ts
|
|
45
|
+
grep -q "museum-partners/shell/angular" "$ROUTES" && pass "partners route registered" || bad "partners route not in app.routes.ts"
|
|
46
|
+
# top-level + search child — expect at least 2 occurrences
|
|
47
|
+
n=$(grep -c "museum-partners/shell/angular" "$ROUTES" || true)
|
|
48
|
+
[ "${n:-0}" -ge 2 ] && pass "registered top-level + search child ($n)" || bad "expected >=2 route registrations (top-level + search child), found $n"
|
|
49
|
+
grep -q "partnerzy" "$ROUTES" && pass "partnerzy path present" || bad "partnerzy path missing"
|
|
50
|
+
|
|
51
|
+
echo "== Deps =="
|
|
52
|
+
grep -q "@smartsoft001-mobilems/articles-domain" package.json && pass "articles-domain dep" || bad "articles-domain dep missing"
|
|
53
|
+
grep -q "@smartsoft001-mobilems/articles-shell-angular" package.json && pass "articles-shell-angular dep" || bad "articles-shell-angular dep missing"
|
|
54
|
+
grep -q "@smartsoft001-mobilems/articles-shell-dtos" package.json && pass "articles-shell-dtos dep" || bad "articles-shell-dtos dep missing"
|
|
55
|
+
grep -q "@smartsoft001-mobilems/objects-domain" package.json && pass "objects-domain dep" || bad "objects-domain dep missing"
|
|
56
|
+
|
|
57
|
+
echo
|
|
58
|
+
if [ "$fail" = 0 ]; then echo "VERIFY: all checks passed."; else echo "VERIFY: FAILED — fix the items above and re-run." >&2; fi
|
|
59
|
+
exit "$fail"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mobilems-project-init
|
|
3
|
+
description: Turn a generic scaffold-nx-workspace base into a MobilEMS museum web-app foundation — the MobilEMS DELTA (deps, Tailwind, runtime env, app.config providers, shared lib, static pages, app shell, .claude). Use whenever the user wants to initialize / bootstrap / set up a NEW MobilEMS or MuzaCMS museum app, "skonfiguruj projekt muzealny / mobilems", port a museum frontend, or asks to add the MobilEMS foundation on top of an existing Nx + Angular SSR scaffold. Produces the foundation ONLY — NO feature modules (objects, collections, articles, thematic publications); those are added by the mobilems-<module> skills afterwards.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# MobilEMS project init
|
|
9
|
+
|
|
10
|
+
Apply the **MobilEMS delta** on top of a base produced by `scaffold-nx-workspace` (the generic
|
|
11
|
+
smartsoft Nx + Angular-SSR shell). The base gives you a buildable empty workspace: prefix already
|
|
12
|
+
threaded, scss-only, minimal `app.config.ts`, empty `app.routes.ts`, a generic `server.ts`, and a
|
|
13
|
+
shared lib that exports only `environment` + `EnvironmentService`. This skill upgrades that shell
|
|
14
|
+
into a MobilEMS museum **foundation** — everything the museum app needs _except_ the feature
|
|
15
|
+
modules.
|
|
16
|
+
|
|
17
|
+
**Why a delta (not a from-scratch generator):** the museum shared lib is ~15k lines of bespoke
|
|
18
|
+
Angular (header, footer, page renderer, filters, gallery, map, widgets, WCAG, SEO, …) that is
|
|
19
|
+
**not** published to npm. It cannot be regenerated from prose, so reference mode copies it from an
|
|
20
|
+
existing MobilEMS repo and threads the workspace prefix. Foundation-only mode is a degraded
|
|
21
|
+
fallback when no reference is available.
|
|
22
|
+
|
|
23
|
+
## Scope — the foundation only
|
|
24
|
+
|
|
25
|
+
This skill builds the museum **foundation**: the workspace it produces builds and runs on its own,
|
|
26
|
+
with **no `libs/museum-*` libraries, no feature shared components, and no feature routes** yet. The
|
|
27
|
+
feature modules are built by `mobilems-objects`, `mobilems-object-collections`, `mobilems-articles`,
|
|
28
|
+
and `mobilems-thematic-publications`, each of which adds its own library, shared components, npm
|
|
29
|
+
package, and routes. Keeping feature code out of the foundation is the layering rule that lets the
|
|
30
|
+
foundation build standalone (its shared library imports no feature package).
|
|
31
|
+
|
|
32
|
+
## Steps
|
|
33
|
+
|
|
34
|
+
### 0. Precondition — confirm a scaffold-nx-workspace base
|
|
35
|
+
|
|
36
|
+
Run `scripts/detect-base.sh <workspace-root>`. It checks for the base shell (`apps/web/src/server.ts`,
|
|
37
|
+
an empty-ish `app.routes.ts`, a shared lib barrel exporting only environment/EnvironmentService) and
|
|
38
|
+
that the MobilEMS delta is **not** already applied. If the base is missing, stop and tell the user to
|
|
39
|
+
run `/smart:scaffold-nx-workspace` first — do not try to create the Nx workspace here.
|
|
40
|
+
|
|
41
|
+
### 1. Detect the prefix (never re-propose)
|
|
42
|
+
|
|
43
|
+
The base already derived/confirmed/threaded the import-alias prefix. Read it from
|
|
44
|
+
`tsconfig.base.json` `paths` — the key `@<PREFIX>/angular`. Hold `<PREFIX>`; thread it into every
|
|
45
|
+
copied file (the reference repo uses `@msr`, so replacing `@msr` → `@<PREFIX>` is the main rewrite).
|
|
46
|
+
|
|
47
|
+
### 2. Choose mode
|
|
48
|
+
|
|
49
|
+
Default = **reference mode**. Ask the user (`AskUserQuestion`) for the path/URL of an existing
|
|
50
|
+
MobilEMS repo to copy the shared lib + static pages from (a local clone of one of the team's museum
|
|
51
|
+
apps). If they decline, or if invoked with a `foundation-only` argument, use **foundation-only** mode
|
|
52
|
+
(see "Modes" below).
|
|
53
|
+
|
|
54
|
+
### 3. npm dependencies
|
|
55
|
+
|
|
56
|
+
Add the MobilEMS framework + state + i18n + shared-UI deps per `references/npm-deps.md`. Versions
|
|
57
|
+
are resolved at run time to match the installed `@smartsoft001/angular` line — **never hardcode**.
|
|
58
|
+
Do NOT add the per-feature packages (`*-objects-*`, `*-articles-*`, `*-public-collections-*`) or
|
|
59
|
+
`online-3d-viewer` — those belong to the module skills.
|
|
60
|
+
|
|
61
|
+
### 4. Tailwind on top of scss
|
|
62
|
+
|
|
63
|
+
Layer Tailwind (class prefix `smart-`) onto the base's scss per `references/tailwind-styles.md`:
|
|
64
|
+
add `tailwind.config.js` + postcss wiring, and overwrite `apps/web/src/styles.scss` with the museum
|
|
65
|
+
global stylesheet (font-fallbacks, `.smart-container`, WCAG, masonry, fullscreen-dialog rules). The
|
|
66
|
+
stylesheet is pure CSS — copying it verbatim is safe even before the components it targets exist.
|
|
67
|
+
|
|
68
|
+
### 5. Runtime env — multimediaUrl + translations
|
|
69
|
+
|
|
70
|
+
Extend the base runtime-env infra per `references/server-env.md`: add `multimediaUrl` to
|
|
71
|
+
`environment.ts`/`.prod.ts`, the `/env.js` template, and the SSR allowed-hosts list; and add the
|
|
72
|
+
`/api/translations` endpoint to `server.ts`.
|
|
73
|
+
|
|
74
|
+
### 6. app.config.ts → MobilEMS providers
|
|
75
|
+
|
|
76
|
+
Replace the base's minimal providers with the full MobilEMS provider set per
|
|
77
|
+
`references/config-providers.md` (ngrx root + devtools, ngx-translate + `provideAppInitializer`
|
|
78
|
+
translation bootstrap, `MasonryModule`, `SharedModule.forRoot`, the three HTTP interceptors, the
|
|
79
|
+
`StyleService`/`AppService`→`NoOpAppService`/`ErrorService` overrides, `SEARCH_CONFIG_TOKEN`, and
|
|
80
|
+
`withI18nSupport()` on hydration).
|
|
81
|
+
|
|
82
|
+
### 7. Shared lib — populate the placeholders (reference mode)
|
|
83
|
+
|
|
84
|
+
Follow `references/shared-lib.md`. Build the **foundation shared components** — every shared
|
|
85
|
+
component that depends only on Angular + the framework packages + other foundation shared components.
|
|
86
|
+
Run `python3 scripts/classify-shared-components.py` against the reference's
|
|
87
|
+
`libs/shared/angular/src/lib/components` to list which shared components are FOUNDATION (build here)
|
|
88
|
+
vs FEATURE-OWNED (built by a module skill, because they import a feature package). Build the
|
|
89
|
+
foundation components (including the home-page carousels) + all services/pipes/directives/tools/models
|
|
90
|
+
|
|
91
|
+
- `configs.module.ts`, thread `<PREFIX>`, export them from the barrels, and build the `widget`
|
|
92
|
+
container + `models/objects.model.ts` with their foundation parts. A shared component that imports a
|
|
93
|
+
feature domain package is feature code — a module skill builds it.
|
|
94
|
+
|
|
95
|
+
### 8. App shell + static pages + routes
|
|
96
|
+
|
|
97
|
+
Follow `references/app-shell.md`: copy the `App` root component (`app.ts`/`app.html`/`app.scss`) and
|
|
98
|
+
the static pages (`home`, `page`, `contact`, `search`, `not-found`) + their resolvers, thread
|
|
99
|
+
`<PREFIX>`, and build `app.routes.ts` with the app's static routes (home, pages, contact, search,
|
|
100
|
+
catch-all). Each module skill adds its own feature route to the `appRoutes` array when it runs
|
|
101
|
+
(`article-preview` is articles-coupled — `mobilems-articles` adds it, not the foundation).
|
|
102
|
+
|
|
103
|
+
### 9. QA hardening — SEO, i18n, WCAG, branding
|
|
104
|
+
|
|
105
|
+
Apply `references/qa-hardening.md` — a checklist distilled from real QA bug reports on live museum
|
|
106
|
+
deployments. The copied foundation carries the _reference museum's_ identity, so this step removes
|
|
107
|
+
leftovers and closes the recurring gaps QA always finds:
|
|
108
|
+
|
|
109
|
+
- dynamic `/sitemap.xml` + `/robots.txt` SSR endpoints driven by `SITE_URL` (never static copies —
|
|
110
|
+
they shipped another museum's domain twice),
|
|
111
|
+
- og/twitter images + URLs on the target domain, favicon set, skip links rewritten for this app,
|
|
112
|
+
- meta description + OpenGraph on home in both languages; `og:locale` follows the active language,
|
|
113
|
+
- default language = Polish, `<html lang>` synced on switch, zero hardcoded UI strings,
|
|
114
|
+
- WCAG: one `h1`, no skipped heading levels, contrast ≥4.5:1 (reference greys fail), heading font
|
|
115
|
+
with full Polish diacritics,
|
|
116
|
+
- warning-clean build & console (NG8107/NG8112/NG8113/NG0100/NG0913, Swiper loop, duplicate API
|
|
117
|
+
calls).
|
|
118
|
+
|
|
119
|
+
### 10. .claude extension
|
|
120
|
+
|
|
121
|
+
Apply `references/claude-config.md`: add `flow@mobilems` to `enabledPlugins`, grow the
|
|
122
|
+
MobilEMS-specific permissions / MCP allow-lists, and seed `tools/specs` + `tools/prompts` pointers.
|
|
123
|
+
|
|
124
|
+
### 11. Verify
|
|
125
|
+
|
|
126
|
+
Run `scripts/verify-init.sh <workspace-root> <PREFIX>`. It asserts the delta landed (Tailwind,
|
|
127
|
+
multimediaUrl, MobilEMS providers, populated shared-lib barrel), the QA-hardening basics (sitemap +
|
|
128
|
+
robots endpoints, favicon, non-empty heading font), AND the foundation is self-contained (no
|
|
129
|
+
`libs/museum-*`, no feature routes, and the build-safety invariant — no file under
|
|
130
|
+
`libs/shared/angular/src` imports a feature package). Fix and re-run until it passes. Gate
|
|
131
|
+
`npm install` behind an explicit ask — skip it during self-validation/eval.
|
|
132
|
+
|
|
133
|
+
## Modes
|
|
134
|
+
|
|
135
|
+
- **Reference mode (default):** copies the shared lib + pages from a real MobilEMS repo and threads
|
|
136
|
+
the prefix. This is the only mode that regenerates the full foundation, because the shared lib is
|
|
137
|
+
not in npm.
|
|
138
|
+
- **Foundation-only (fallback):** do steps 1, 3, 4, 5, 9 (deps, Tailwind, env, .claude) and scaffold a
|
|
139
|
+
reduced `app.config.ts` that does NOT import local shared-lib code (omit the interceptors + the
|
|
140
|
+
`App` shell that need it). Leave a `MOBILEMS-FOUNDATION-TODO.md` listing what reference mode would
|
|
141
|
+
have copied (shared lib, pages, full app.config, app shell) so the user can supply a reference
|
|
142
|
+
later. Be honest that this mode does not produce a runnable museum UI on its own.
|
|
143
|
+
|
|
144
|
+
## Notes
|
|
145
|
+
|
|
146
|
+
- This skill assumes the base Nx + Angular-SSR workspace already exists — it patches MobilEMS onto
|
|
147
|
+
it and never runs `create-nx-workspace`.
|
|
148
|
+
- Bundled `references/` + `scripts/` ship automatically (the whole `src/plugins/**` tree is copied to
|
|
149
|
+
dist). Do NOT hand-edit `plugin.json` — the version is auto-synced at build.
|
|
150
|
+
- The feature work — feature libs, their npm packages, routes, and feature shared components — is
|
|
151
|
+
built by the `mobilems-<module>` skills, not here.
|