@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.
Files changed (47) hide show
  1. package/package.json +1 -1
  2. package/plugins/mobilems/.claude-plugin/plugin.json +1 -1
  3. package/plugins/mobilems/skills/mobilems-articles/SKILL.md +108 -0
  4. package/plugins/mobilems/skills/mobilems-articles/references/lib-contents.md +164 -0
  5. package/plugins/mobilems/skills/mobilems-articles/references/module-and-routes.md +146 -0
  6. package/plugins/mobilems/skills/mobilems-articles/references/shared-components.md +56 -0
  7. package/plugins/mobilems/skills/mobilems-articles/scripts/detect-init.sh +29 -0
  8. package/plugins/mobilems/skills/mobilems-articles/scripts/verify-module.sh +61 -0
  9. package/plugins/mobilems/skills/mobilems-events/SKILL.md +65 -0
  10. package/plugins/mobilems/skills/mobilems-events/references/lib-contents.md +212 -0
  11. package/plugins/mobilems/skills/mobilems-events/references/module-and-routes.md +113 -0
  12. package/plugins/mobilems/skills/mobilems-events/scripts/detect-init.sh +29 -0
  13. package/plugins/mobilems/skills/mobilems-events/scripts/verify-module.sh +59 -0
  14. package/plugins/mobilems/skills/mobilems-object-collections/SKILL.md +77 -0
  15. package/plugins/mobilems/skills/mobilems-object-collections/references/lib-contents.md +76 -0
  16. package/plugins/mobilems/skills/mobilems-object-collections/references/module-and-routes.md +116 -0
  17. package/plugins/mobilems/skills/mobilems-object-collections/references/shared-components.md +61 -0
  18. package/plugins/mobilems/skills/mobilems-object-collections/scripts/detect-init.sh +29 -0
  19. package/plugins/mobilems/skills/mobilems-object-collections/scripts/verify-module.sh +44 -0
  20. package/plugins/mobilems/skills/mobilems-objects/SKILL.md +80 -0
  21. package/plugins/mobilems/skills/mobilems-objects/references/lib-contents.md +68 -0
  22. package/plugins/mobilems/skills/mobilems-objects/references/module-and-routes.md +108 -0
  23. package/plugins/mobilems/skills/mobilems-objects/references/shared-components.md +69 -0
  24. package/plugins/mobilems/skills/mobilems-objects/scripts/detect-init.sh +29 -0
  25. package/plugins/mobilems/skills/mobilems-objects/scripts/verify-module.sh +51 -0
  26. package/plugins/mobilems/skills/mobilems-partners/SKILL.md +65 -0
  27. package/plugins/mobilems/skills/mobilems-partners/references/lib-contents.md +160 -0
  28. package/plugins/mobilems/skills/mobilems-partners/references/module-and-routes.md +125 -0
  29. package/plugins/mobilems/skills/mobilems-partners/scripts/detect-init.sh +29 -0
  30. package/plugins/mobilems/skills/mobilems-partners/scripts/verify-module.sh +59 -0
  31. package/plugins/mobilems/skills/mobilems-project-init/SKILL.md +151 -0
  32. package/plugins/mobilems/skills/mobilems-project-init/references/app-shell.md +115 -0
  33. package/plugins/mobilems/skills/mobilems-project-init/references/claude-config.md +44 -0
  34. package/plugins/mobilems/skills/mobilems-project-init/references/config-providers.md +203 -0
  35. package/plugins/mobilems/skills/mobilems-project-init/references/npm-deps.md +66 -0
  36. package/plugins/mobilems/skills/mobilems-project-init/references/qa-hardening.md +124 -0
  37. package/plugins/mobilems/skills/mobilems-project-init/references/server-env.md +97 -0
  38. package/plugins/mobilems/skills/mobilems-project-init/references/shared-lib.md +125 -0
  39. package/plugins/mobilems/skills/mobilems-project-init/references/tailwind-styles.md +112 -0
  40. package/plugins/mobilems/skills/mobilems-project-init/scripts/classify-shared-components.py +114 -0
  41. package/plugins/mobilems/skills/mobilems-project-init/scripts/detect-base.sh +49 -0
  42. package/plugins/mobilems/skills/mobilems-project-init/scripts/verify-init.sh +81 -0
  43. package/plugins/mobilems/skills/mobilems-thematic-publications/SKILL.md +60 -0
  44. package/plugins/mobilems/skills/mobilems-thematic-publications/references/lib-contents.md +134 -0
  45. package/plugins/mobilems/skills/mobilems-thematic-publications/references/module-and-routes.md +107 -0
  46. package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/detect-init.sh +29 -0
  47. package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/verify-module.sh +56 -0
@@ -0,0 +1,212 @@
1
+ # libs/museum-events — structure to create
2
+
3
+ Copy from the reference MobilEMS app's `libs/museum-events` and thread `<PREFIX>`. Events ships a
4
+ single project: `shell/angular`. It is a standalone booking module — it has no `domain` library, no
5
+ NgRx `+state`, and no shared-module `forFeature`. Everything it needs lives in `shell/angular`.
6
+
7
+ ```
8
+ libs/museum-events/
9
+ └── shell/angular/ # project: museum-events-shell-angular
10
+ ├── eslint.config.mjs
11
+ ├── jest.config.ts
12
+ ├── project.json
13
+ ├── tsconfig.json
14
+ ├── tsconfig.lib.json
15
+ ├── tsconfig.spec.json
16
+ └── src/
17
+ ├── index.ts # export * from './lib';
18
+ ├── test-setup.ts # setupZoneTestEnv(...) from jest-preset-angular
19
+ ├── __mocks__/
20
+ │ └── @smartsoft001-mobilems/
21
+ │ ├── angular.ts # jest mock — keep
22
+ │ └── models.ts # jest mock — keep
23
+ └── lib/
24
+ ├── index.ts # export * from './events.module'; export * from './events.routes';
25
+ ├── events.module.ts # MuseumEventsModule (references/module-and-routes.md)
26
+ ├── events.routes.ts # eventsRoutes — lazy loadComponent per page + Masonry on list
27
+ ├── models/
28
+ │ └── events.models.ts # IEvent, IEventResponse, IEventsSearchResponse, IEventPeriod,
29
+ │ # IEventPeriodsResponse, IReservationRequest, IReservationResponse,
30
+ │ # IReservationValidationError
31
+ ├── pages/
32
+ │ ├── list/ # EventsListComponent (+ template/styles)
33
+ │ ├── item/ # EventItemComponent
34
+ │ ├── booking/ # BookingComponent (+ .spec)
35
+ │ ├── confirm/ # ConfirmComponent (+ .spec)
36
+ │ └── cancel/ # CancelComponent (+ .spec)
37
+ ├── services/
38
+ │ └── events-reservation.service.ts # EventsReservationService (+ .spec)
39
+ └── pipes/
40
+ └── time-format.pipe.ts # TimeFormatPipe (standalone)
41
+ ```
42
+
43
+ > Copy only what the reference has. There is **no `domain/` directory** for events and **no
44
+ > `+state/` directory** — its event/reservation shapes are plain TypeScript interfaces in
45
+ > `models/events.models.ts`.
46
+
47
+ ## `shell/angular/src/index.ts` and `lib/index.ts`
48
+
49
+ The public barrel re-exports the lib barrel, which exports the module + the routes:
50
+
51
+ ```ts
52
+ // src/index.ts
53
+ export * from './lib';
54
+ ```
55
+
56
+ ```ts
57
+ // src/lib/index.ts
58
+ export * from './events.module';
59
+ export * from './events.routes';
60
+ ```
61
+
62
+ ## Models — `lib/models/events.models.ts` (verbatim — pure types)
63
+
64
+ Plain interfaces describing the events API and the reservation flow. Thread nothing; copy verbatim.
65
+
66
+ ```ts
67
+ export interface IEvent {
68
+ id: number;
69
+ name: string;
70
+ image: string | null;
71
+ description: string | null;
72
+ startDate: string | null;
73
+ endDate: string | null;
74
+ ticketsLeft: number;
75
+ price: number;
76
+ location: {
77
+ name: string | null;
78
+ latitude: number | null;
79
+ longitude: number | null;
80
+ } | null;
81
+ }
82
+
83
+ export interface IEventResponse {
84
+ data: IEvent;
85
+ status: number;
86
+ message: string | null;
87
+ }
88
+
89
+ export interface IEventsSearchResponse {
90
+ data: {
91
+ paginatorDetails: {
92
+ currentPage: number;
93
+ maxPerPage: number;
94
+ totalItemsCount: number;
95
+ totalPagesCount: number;
96
+ };
97
+ items: IEvent[];
98
+ };
99
+ status: number;
100
+ message: string | null;
101
+ }
102
+
103
+ export interface IEventPeriod {
104
+ id: number;
105
+ start_time: string;
106
+ end_time: string;
107
+ available_seats: number;
108
+ }
109
+
110
+ export interface IEventPeriodsResponse {
111
+ periods: IEventPeriod[];
112
+ }
113
+
114
+ export interface IReservationRequest {
115
+ email: string;
116
+ firstName: string;
117
+ lastName?: string;
118
+ numberOfParticipants: number;
119
+ periodId: number;
120
+ eventId: number;
121
+ date: string;
122
+ }
123
+
124
+ export interface IReservationResponse {
125
+ message: string;
126
+ code: number;
127
+ }
128
+
129
+ export interface IReservationValidationError {
130
+ errors?: Record<string, string>;
131
+ message?: string;
132
+ code: number;
133
+ }
134
+ ```
135
+
136
+ ## Service — `lib/services/events-reservation.service.ts`
137
+
138
+ `EventsReservationService` is `@Injectable({ providedIn: 'root' })`. It injects `HttpClient`, reads
139
+ `environment.apiUrl` from `@<PREFIX>/angular`, and calls the events API: `getEvent`,
140
+ `getEvents` (paged search), `getAvailableDates` and `getPeriods` (the calendar / time-slot picker),
141
+ and `register` / `confirm` / `cancel` (the reservation lifecycle). Copy verbatim and thread `<PREFIX>`
142
+ on the `environment` import:
143
+
144
+ ```ts
145
+ import { environment } from '@<PREFIX>/angular';
146
+ ```
147
+
148
+ It depends on no shared module — the booking flow is entirely self-contained.
149
+
150
+ ## Pipe — `lib/pipes/time-format.pipe.ts` (verbatim)
151
+
152
+ A small standalone pipe that trims a time string to `HH:mm`:
153
+
154
+ ```ts
155
+ import { Pipe, PipeTransform } from '@angular/core';
156
+
157
+ @Pipe({ name: 'timeFormat', standalone: true })
158
+ export class TimeFormatPipe implements PipeTransform {
159
+ transform(value: string): string {
160
+ if (!value) return value;
161
+ return value.substring(0, 5);
162
+ }
163
+ }
164
+ ```
165
+
166
+ ## Pages — `lib/pages/{list,item,booking,confirm,cancel}` (copy verbatim, thread `<PREFIX>`)
167
+
168
+ All five are standalone components, lazy-loaded by `events.routes.ts`:
169
+
170
+ - **list** — `EventsListComponent`: the Masonry grid of events (uses `getEvents`).
171
+ - **item** — `EventItemComponent`: a single event's detail (uses `getEvent`).
172
+ - **booking** — `BookingComponent`: the reservation form (date picker via `getAvailableDates`,
173
+ time-slot via `getPeriods`, submit via `register`).
174
+ - **confirm** — `ConfirmComponent`: confirmation landing for the `:id/potwierdzenie` route
175
+ (`confirm(token)`).
176
+ - **cancel** — `CancelComponent`: cancellation landing for the `:id/anulowanie` route
177
+ (`cancel(token)`).
178
+
179
+ Thread `<PREFIX>` on any `@<PREFIX>/angular` imports. The components also use `@ngx-translate/core`,
180
+ `@smartsoft001-mobilems/angular`, `@smartsoft001/angular`, and `@thisissoon/angular-masonry` — all
181
+ foundation packages.
182
+
183
+ ## Jest mocks — `src/__mocks__/@smartsoft001-mobilems/{angular,models}.ts` (keep)
184
+
185
+ Copy both mock files verbatim. The jest config maps `@smartsoft001-mobilems/angular` and
186
+ `@smartsoft001-mobilems/models` to these mocks (see `moduleNameMapper` below) so the lib's specs run
187
+ without pulling the real packages.
188
+
189
+ ## Wiring
190
+
191
+ - **tsconfig.base.json path** — add the single shell path (events has no domain path):
192
+ ```json
193
+ "@<PREFIX>/museum-events/shell/angular": ["libs/museum-events/shell/angular/src/index.ts"]
194
+ ```
195
+ - **project.json** — copy the project config (name `museum-events-shell-angular`) and normalize every
196
+ path field to the lib's real location `libs/museum-events/shell/angular/...`. The reference's
197
+ `sourceRoot` is stale (`libs/museum-events/src` — missing the `shell/angular` segment); set it to
198
+ `libs/museum-events/shell/angular/src`. The `jestConfig` / `tsConfig` target options already point
199
+ at `libs/museum-events/shell/angular/...` — verify each field against a known-good sibling and fix
200
+ only the stale ones.
201
+ - **jest.config.ts** — copy with `displayName: 'museum-events-shell-angular'`,
202
+ `coverageDirectory: '../../../../coverage/libs/museum-events/shell/angular'`, and the
203
+ `moduleNameMapper` pointing `@smartsoft001-mobilems/angular` and `@smartsoft001-mobilems/models` at
204
+ the `src/__mocks__/...` files. Copy `tsconfig.json`, `tsconfig.lib.json`, `tsconfig.spec.json`,
205
+ `eslint.config.mjs`, and `test-setup.ts` as-is.
206
+ - **commit scope** — `commitlint.config.js` reads `libs/` dir names, so `museum-events` becomes a
207
+ valid scope automatically; no edit needed.
208
+
209
+ Events imports only foundation packages (`@smartsoft001-mobilems/angular`,
210
+ `@smartsoft001-mobilems/models`, `@smartsoft001/angular`, `@thisissoon/angular-masonry`,
211
+ `@ngx-translate/core`), all installed by `mobilems-project-init`. It adds no new npm dependency (see
212
+ SKILL.md step 1).
@@ -0,0 +1,113 @@
1
+ # MuseumEventsModule + route registration
2
+
3
+ ## `libs/museum-events/shell/angular/src/lib/events.module.ts`
4
+
5
+ `MuseumEventsModule` is a pure routing host: it imports `RouterModule.forChild(eventsRoutes)` and
6
+ nothing else. There is no shared-module `forFeature` and no NgRx state — each page is a standalone
7
+ component pulled in lazily by the routes, and data access goes through the standalone
8
+ `EventsReservationService` (`providedIn: 'root'`). Copy this module verbatim; it has no `<PREFIX>` to
9
+ thread.
10
+
11
+ ```ts
12
+ import { NgModule } from '@angular/core';
13
+ import { RouterModule } from '@angular/router';
14
+
15
+ import { eventsRoutes } from './events.routes';
16
+
17
+ @NgModule({
18
+ imports: [RouterModule.forChild(eventsRoutes)],
19
+ })
20
+ export class MuseumEventsModule {}
21
+ ```
22
+
23
+ ## `events.routes.ts`
24
+
25
+ Every page is loaded lazily with `loadComponent`. The list route additionally provides the `Masonry`
26
+ SSR factory (the real library in the browser, a no-op mock during SSR). Copy verbatim — there is no
27
+ `<PREFIX>` to thread here either.
28
+
29
+ ```ts
30
+ import { Route } from '@angular/router';
31
+ import { Masonry } from '@thisissoon/angular-masonry';
32
+
33
+ export const eventsRoutes: Route[] = [
34
+ {
35
+ path: ':id/potwierdzenie',
36
+ loadComponent: () =>
37
+ import('./pages/confirm/confirm.component').then(
38
+ (m) => m.ConfirmComponent,
39
+ ),
40
+ },
41
+ {
42
+ path: ':id/anulowanie',
43
+ loadComponent: () =>
44
+ import('./pages/cancel/cancel.component').then((m) => m.CancelComponent),
45
+ },
46
+ {
47
+ path: ':id/booking',
48
+ loadComponent: () =>
49
+ import('./pages/booking/booking.component').then(
50
+ (m) => m.BookingComponent,
51
+ ),
52
+ },
53
+ {
54
+ path: ':id/:slug',
55
+ loadComponent: () =>
56
+ import('./pages/item/item.component').then((m) => m.EventItemComponent),
57
+ },
58
+ {
59
+ path: ':id',
60
+ loadComponent: () =>
61
+ import('./pages/item/item.component').then((m) => m.EventItemComponent),
62
+ },
63
+ {
64
+ path: '',
65
+ loadComponent: () =>
66
+ import('./pages/list/list.component').then((m) => m.EventsListComponent),
67
+ providers: [
68
+ {
69
+ provide: Masonry,
70
+ useFactory: () => {
71
+ if (typeof window !== 'undefined' && (window as any)['Masonry']) {
72
+ return (window as any)['Masonry'];
73
+ }
74
+ return class {
75
+ layout() {
76
+ return;
77
+ }
78
+ reloadItems() {
79
+ return;
80
+ }
81
+ destroy() {
82
+ return;
83
+ }
84
+ };
85
+ },
86
+ },
87
+ ],
88
+ },
89
+ ];
90
+ ```
91
+
92
+ > Route order matters: the specific reservation sub-routes (`:id/potwierdzenie`, `:id/anulowanie`,
93
+ > `:id/booking`) come before the generic `:id/:slug` and `:id` detail routes, and the bare list route
94
+ > `''` comes last. Keep this order.
95
+
96
+ ## Register in `apps/web/src/app/app.routes.ts`
97
+
98
+ Events registers a single **top-level** route. Add the `wydarzenia` route to the `appRoutes` array
99
+ (idempotently — if a `wydarzenia` route already exists, skip). Events is a booking destination
100
+ browsed from the top level, so it is added at the top level only and is NOT placed under the
101
+ `wyszukaj/:text` search route — events do not appear in search results.
102
+
103
+ Top-level — add to `appRoutes`:
104
+
105
+ ```ts
106
+ {
107
+ path: 'wydarzenia',
108
+ loadChildren: () =>
109
+ import('@<PREFIX>/museum-events/shell/angular').then(
110
+ (m) => m.MuseumEventsModule,
111
+ ),
112
+ },
113
+ ```
@@ -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-events: the standalone booking library created
6
+ # (shell/angular only — no domain, no forFeature), MuseumEventsModule wiring RouterModule.forChild,
7
+ # lazy routes, the five pages + reservation service + models + time-format pipe present, the
8
+ # wydarzenia route registered as a single top-level route, and the foundation deps it reuses present.
9
+ # macOS bash 3.2 safe.
10
+ set -eu
11
+
12
+ ROOT="${1:?usage: verify-module.sh <workspace-root> <PREFIX>}"
13
+ PREFIX="${2:?usage: verify-module.sh <workspace-root> <PREFIX>}"
14
+ cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
15
+
16
+ fail=0
17
+ pass() { echo " PASS: $1"; }
18
+ bad() { echo " FAIL: $1" >&2; fail=1; }
19
+
20
+ SHELL_LIB=libs/museum-events/shell/angular
21
+ LIB="$SHELL_LIB/src/lib"
22
+ MOD="$LIB/events.module.ts"
23
+ ROUTES="$LIB/events.routes.ts"
24
+
25
+ echo "== Library =="
26
+ [ -d "$SHELL_LIB" ] && pass "shell/angular project present" || bad "libs/museum-events/shell/angular missing"
27
+ [ -f "$MOD" ] && pass "events.module.ts present" || bad "events.module.ts missing"
28
+ grep -q "class MuseumEventsModule" "$MOD" 2>/dev/null && pass "MuseumEventsModule class declared" || bad "MuseumEventsModule class not declared"
29
+ grep -q "RouterModule.forChild" "$MOD" 2>/dev/null && pass "RouterModule.forChild wired" || bad "RouterModule.forChild missing in module"
30
+ [ -f "$ROUTES" ] && pass "events.routes.ts present" || bad "events.routes.ts missing"
31
+ grep -q "@$PREFIX/museum-events/shell/angular" tsconfig.base.json && pass "tsconfig shell path added" || bad "tsconfig @$PREFIX/museum-events/shell/angular path missing"
32
+
33
+ echo "== Pages =="
34
+ for p in list item booking confirm cancel; do
35
+ [ -f "$LIB/pages/$p/$p.component.ts" ] && pass "$p page present" || bad "pages/$p/$p.component.ts missing"
36
+ done
37
+
38
+ echo "== Service, models, pipe =="
39
+ [ -f "$LIB/services/events-reservation.service.ts" ] && pass "events-reservation.service.ts present" || bad "services/events-reservation.service.ts missing"
40
+ grep -q "class EventsReservationService" "$LIB/services/events-reservation.service.ts" 2>/dev/null && pass "EventsReservationService class declared" || bad "EventsReservationService class not declared"
41
+ [ -f "$LIB/models/events.models.ts" ] && pass "events.models.ts present" || bad "models/events.models.ts missing"
42
+ [ -f "$LIB/pipes/time-format.pipe.ts" ] && pass "time-format.pipe.ts present" || bad "pipes/time-format.pipe.ts missing"
43
+ grep -q "class TimeFormatPipe" "$LIB/pipes/time-format.pipe.ts" 2>/dev/null && pass "TimeFormatPipe class declared" || bad "TimeFormatPipe class not declared"
44
+
45
+ echo "== Route registration =="
46
+ APP_ROUTES=apps/web/src/app/app.routes.ts
47
+ grep -q "@$PREFIX/museum-events/shell/angular" "$APP_ROUTES" && pass "events route registered" || bad "events route not in app.routes.ts"
48
+ # single top-level route — expect exactly 1 occurrence
49
+ n=$(grep -c "@$PREFIX/museum-events/shell/angular" "$APP_ROUTES" || true)
50
+ [ "${n:-0}" -eq 1 ] && pass "registered as a single top-level route ($n)" || bad "expected exactly 1 route registration, found $n"
51
+ grep -q "wydarzenia" "$APP_ROUTES" && pass "wydarzenia path present" || bad "wydarzenia path missing"
52
+
53
+ echo "== Foundation deps =="
54
+ grep -q "@smartsoft001-mobilems/angular" package.json && pass "@smartsoft001-mobilems/angular present" || bad "@smartsoft001-mobilems/angular missing"
55
+ grep -q "@smartsoft001-mobilems/models" package.json && pass "@smartsoft001-mobilems/models present" || bad "@smartsoft001-mobilems/models 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,77 @@
1
+ ---
2
+ name: mobilems-object-collections
3
+ description: Add the museum COLLECTIONS feature module to a MobilEMS app initialized with mobilems-project-init. Use whenever the user wants to add / scaffold / wire the collections (kolekcje) module, the public collections module, the collection list + detail pages, the collections filters (keywords / cycles), or asks to "dodaj moduł kolekcji". Creates libs/museum-collections (domain + shell/angular), registers the /kolekcje route, builds the collections shared component, and adds the public-collections 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 collections module
9
+
10
+ Add the **collections** (kolekcje) feature module to a MobilEMS app whose foundation was built by
11
+ `mobilems-project-init`. This skill builds the `libs/museum-collections` library, the collections
12
+ shared component, the collections route, and adds the public-collections npm packages.
13
+
14
+ Collections is a list/detail view backed by `PublicCollectionsSharedModule.forFeature` over the
15
+ `MuseumCollection` entity, with a `filters` component that narrows the list by keywords and cycles.
16
+
17
+ ## Precondition
18
+
19
+ Run `scripts/detect-init.sh <workspace-root>`. It confirms the MobilEMS foundation is present
20
+ (`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If not, tell the user to
21
+ run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`; reference uses
22
+ `@msr`) and the reference repo path (same one init used).
23
+
24
+ ## Steps
25
+
26
+ 1. **npm deps** — add `@smartsoft001-mobilems/public-collections-domain`,
27
+ `@smartsoft001-mobilems/public-collections-shell-angular`,
28
+ `@smartsoft001-mobilems/public-collections-shell-dtos` (match the installed mobilems line).
29
+ Resolve versions at run time; do not `npm install` unless asked. No extra third-party viewer deps.
30
+ 2. **Library** — create `libs/museum-collections/{domain,shell/angular}` per `references/lib-contents.md`:
31
+ copy the domain entity (`MuseumCollection extends PublicCollection`) and the shell
32
+ (`MuseumCollectionsModule`, routes, `components/filters`, `pages/{list,item}`, `services`), thread
33
+ `<PREFIX>`. Add the two `tsconfig.base.json` paths and the `museum-collections` commit scope.
34
+ 3. **Module + routes** — see `references/module-and-routes.md`: `MuseumCollectionsModule` wraps
35
+ `PublicCollectionsSharedModule.forFeature({ routing: false, config: { type: MuseumCollection, apiUrl, entity: 'publicCollections' } })`
36
+ with the `Masonry` SSR factory and the `CrudService → CollectionsService` provider. Add the lazy
37
+ `kolekcje` route to `appRoutes` in `apps/web/src/app/app.routes.ts` and a matching child under the
38
+ `wyszukaj/:text` search route.
39
+ 4. **Collections shared component** — see `references/shared-components.md`: build
40
+ `components/collections-grid-item` (it imports `public-collections-domain`) in the shared lib and
41
+ export it from `components/index.ts`. Collections contributes no widget-map entry and no model type
42
+ (those are objects-only).
43
+ 5. **Verify** — run `scripts/verify-module.sh <workspace-root> <PREFIX>`: asserts the lib +
44
+ `MuseumCollection` entity exist, the `forFeature` is wired, the `kolekcje` route is registered
45
+ (top-level + search child), the `collections-grid-item` component is built and exported, and the
46
+ three public-collections deps are present. Fix and re-run.
47
+
48
+ ## Known QA pitfalls (from live deployments — fix while copying, not after)
49
+
50
+ Each of these was a real Linear bug on a museum project built with this skill (Ostrowiec MOB-961/994,
51
+ Tomaszów MOB-956). The cross-cutting rules live in `mobilems-project-init`
52
+ `references/qa-hardening.md`; these are the collections-specific ones:
53
+
54
+ - **One card component, reused everywhere.** The home page and the collection detail must render the
55
+ SAME shared `collections-grid-item` / object card components as the list views — a second ad-hoc
56
+ card on the home page was a High-priority bug (MOB-994). Never fork a card per page.
57
+ - **No hardcoded UI strings.** The detail heading was shipped as a literal `Zbiory {{ title }}` and
58
+ stayed Polish in EN — every static label goes through a translation key (PL + EN in
59
+ `translations.json`). The list page needs a VISIBLE translated heading (not only `sr-only`), and
60
+ exactly one `h1` per page.
61
+ - **Card image fallback:** when a collection thumbnail fails to load, show the app's `no-image`
62
+ placeholder instead of a broken image.
63
+ - **Hover:** underline the card **title** on hover (not the "Zobacz więcej" link) — same pattern as
64
+ objects.
65
+ - **Search debounce 250 ms** in the filters (`@debounce(250)`), total count + shared
66
+ `NextPageComponent` on the list — consistent with objects/news.
67
+ - **Keyword/cycle chips wrap on 360 px** — chips caused page-level horizontal scroll on mobile.
68
+ - **Heading font must cover Polish diacritics** — collection card titles rendered in a fallback
69
+ font because the configured serif (Bacasime Antique) lacks `ą ć ę ł ń ś ź ż`; verify the Tailwind
70
+ heading `fontFamily` is non-empty and latin-ext complete (see qa-hardening).
71
+ - **Warning-clean templates:** remove needless `?.` on non-nullable signals (NG8107) after copying.
72
+
73
+ ## Notes
74
+
75
+ - This skill builds only the collections module + its shared component + its route. It never adds
76
+ other feature modules and never re-runs the foundation.
77
+ - Bundled `references/` + `scripts/` ship automatically. Do not hand-edit `plugin.json`.
@@ -0,0 +1,76 @@
1
+ # libs/museum-collections — structure to create
2
+
3
+ Copy from the reference repo's `libs/museum-collections` and thread `<PREFIX>`. The lib has two
4
+ projects: a `domain` (the entity) and a `shell/angular` (the Angular module + UI). Both rely on Nx
5
+ inferred targets + the `tsconfig.base.json` path mappings — there are **no** `project.json` /
6
+ `package.json` / `tsconfig.*.json` / `jest.config` / `__mocks__` files under either project (only
7
+ `src/`). This is leaner than `museum-objects`; do not invent config files the reference doesn't have.
8
+
9
+ ```
10
+ libs/museum-collections/
11
+ ├── domain/ # project: museum-collections-domain (inferred)
12
+ │ └── src/
13
+ │ ├── index.ts # export * from './lib/entities';
14
+ │ └── lib/
15
+ │ └── entities/ # MuseumCollection extends PublicCollection (+ index)
16
+ └── shell/angular/ # project: museum-collections-shell-angular (inferred)
17
+ └── src/
18
+ ├── index.ts # export * from './lib/pages'; ./lib/components; ./lib/collections.module
19
+ └── lib/
20
+ ├── collections.module.ts # MuseumCollectionsModule (references/module-and-routes.md)
21
+ ├── collections.routes.ts
22
+ ├── components/ # filters (keywords/cycles) + index
23
+ ├── pages/ # list (ListComponent), item (ItemComponent) + index
24
+ └── services/ # collections.service.ts + index
25
+ ```
26
+
27
+ There are no `enums/`, `specifications/`, or `value-objects/` dirs in the domain — the connection
28
+ shape (keywords/cycles) is inlined on the entity below.
29
+
30
+ ## Domain entity (verbatim, thread nothing — pure types)
31
+
32
+ `MuseumCollection extends PublicCollection` (from `@smartsoft001-mobilems/public-collections-domain`)
33
+ adds `paginatorDetails?` and a `connections?` block (keywords + cycles). Reproduce exactly:
34
+
35
+ ```ts
36
+ import { Model } from '@smartsoft001/models';
37
+ import { PublicCollection } from '@smartsoft001-mobilems/public-collections-domain';
38
+
39
+ @Model({})
40
+ export class MuseumCollection extends PublicCollection {
41
+ paginatorDetails?: {
42
+ currentPage: number;
43
+ maxPerPage: number;
44
+ totalItemsCount: number;
45
+ totalPagesCount: number;
46
+ };
47
+
48
+ connections?: {
49
+ keywords: {
50
+ id: number;
51
+ name: string;
52
+ count: number;
53
+ }[];
54
+ cycles: {
55
+ id: number;
56
+ name: string;
57
+ count: number;
58
+ }[];
59
+ };
60
+ }
61
+ ```
62
+
63
+ ## Wiring
64
+
65
+ - **tsconfig.base.json paths** (add both):
66
+ ```json
67
+ "@<PREFIX>/museum-collections/domain": ["libs/museum-collections/domain/src/index.ts"],
68
+ "@<PREFIX>/museum-collections/shell/angular": ["libs/museum-collections/shell/angular/src/index.ts"]
69
+ ```
70
+ - **project.json** — none in the reference; both projects use Nx inferred targets. Do not add one.
71
+ - **commit scope** — `commitlint.config.js` reads `libs/` dir names, so `museum-collections` becomes
72
+ a valid scope automatically; no edit needed.
73
+
74
+ The `components/filters` component renders the keyword/cycle facets exposed by the entity's
75
+ `connections` block; the list/detail rendering itself comes from `PublicCollectionsSharedModule`.
76
+ No third-party viewer deps are needed (unlike the objects 3D tab).