@smartsoft001-mobilems/claude-plugins 2.88.0 → 2.90.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/README.md +27 -2
- package/package.json +1 -1
- package/plugins/mobilems/.claude-plugin/README.md +21 -2
- package/plugins/mobilems/.claude-plugin/plugin.json +1 -1
- package/plugins/mobilems/skills/mobilems-articles/SKILL.md +52 -20
- package/plugins/mobilems/skills/mobilems-articles/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-articles/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-articles/scripts/verify-module.sh +53 -0
- package/plugins/mobilems/skills/mobilems-devops-config/SKILL.md +141 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/docker.md +58 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/env-and-lint.md +59 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/github-hygiene.md +54 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/tooling.md +105 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/workflows.md +410 -0
- package/plugins/mobilems/skills/mobilems-devops-config/scripts/detect-foundation.sh +47 -0
- package/plugins/mobilems/skills/mobilems-devops-config/scripts/verify-devops.sh +96 -0
- package/plugins/mobilems/skills/mobilems-events/SKILL.md +27 -1
- package/plugins/mobilems/skills/mobilems-events/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-events/scripts/verify-module.sh +43 -0
- package/plugins/mobilems/skills/mobilems-object-collections/SKILL.md +46 -14
- package/plugins/mobilems/skills/mobilems-object-collections/references/lib-contents.md +11 -0
- package/plugins/mobilems/skills/mobilems-object-collections/references/module-and-routes.md +26 -0
- package/plugins/mobilems/skills/mobilems-object-collections/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-object-collections/scripts/verify-module.sh +54 -0
- package/plugins/mobilems/skills/mobilems-objects/SKILL.md +42 -10
- package/plugins/mobilems/skills/mobilems-objects/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-objects/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-objects/scripts/verify-module.sh +53 -0
- package/plugins/mobilems/skills/mobilems-partners/SKILL.md +30 -2
- package/plugins/mobilems/skills/mobilems-partners/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-partners/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-partners/scripts/verify-module.sh +53 -0
- package/plugins/mobilems/skills/mobilems-project-init/SKILL.md +68 -8
- package/plugins/mobilems/skills/mobilems-project-init/references/branding-reset.md +112 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/claude-config.md +7 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/config-providers.md +1 -1
- package/plugins/mobilems/skills/mobilems-project-init/references/qa-hardening.md +30 -30
- package/plugins/mobilems/skills/mobilems-project-init/references/shared-lib.md +42 -5
- package/plugins/mobilems/skills/mobilems-project-init/references/tailwind-styles.md +33 -23
- package/plugins/mobilems/skills/mobilems-project-init/scripts/verify-init.sh +62 -9
- package/plugins/mobilems/skills/mobilems-thematic-publications/SKILL.md +25 -1
- package/plugins/mobilems/skills/mobilems-thematic-publications/references/lib-contents.md +11 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/references/module-and-routes.md +26 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/verify-module.sh +43 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# verify-devops.sh <workspace-root> <NODE_VERSION>
|
|
4
|
+
#
|
|
5
|
+
# Post-condition check for mobilems-devops-config. Asserts the ops/deployment layer landed and that
|
|
6
|
+
# the node version is consistent across .nvmrc, the Dockerfile, and the workflows.
|
|
7
|
+
# Portable to macOS system bash (3.2). Exit 0 = all pass; exit 1 = one or more checks failed.
|
|
8
|
+
set -eu
|
|
9
|
+
|
|
10
|
+
ROOT="${1:?usage: verify-devops.sh <workspace-root> <NODE_VERSION> [SITE_HOST]}"
|
|
11
|
+
NODE_VERSION="${2:?usage: verify-devops.sh <workspace-root> <NODE_VERSION> [SITE_HOST]}"
|
|
12
|
+
# Optional: this project's expected site host, so a carried-over SITE_URL is caught. No project
|
|
13
|
+
# host is hardcoded in this script.
|
|
14
|
+
SITE_HOST="${3:-}"
|
|
15
|
+
cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
|
|
16
|
+
|
|
17
|
+
fail=0
|
|
18
|
+
pass() { echo " PASS: $1"; }
|
|
19
|
+
bad() { echo " FAIL: $1" >&2; fail=1; }
|
|
20
|
+
exists() { [ -e "$1" ] && pass "$1" || bad "$1 missing"; }
|
|
21
|
+
|
|
22
|
+
echo "== Docker =="
|
|
23
|
+
exists apps/web/Dockerfile
|
|
24
|
+
[ -f apps/web/Dockerfile ] && grep -q "server/server.mjs" apps/web/Dockerfile && pass "Dockerfile starts SSR server" || bad "Dockerfile CMD node server/server.mjs"
|
|
25
|
+
|
|
26
|
+
echo "== GitHub Actions =="
|
|
27
|
+
exists .github/actions/cached-deps/action.yml
|
|
28
|
+
exists .github/workflows/development.yml
|
|
29
|
+
exists .github/workflows/e2e.yml
|
|
30
|
+
exists .github/workflows/push-to-client.yml
|
|
31
|
+
exists .github/workflows/ai-plan.yml
|
|
32
|
+
exists .github/workflows/ai-impl.yml
|
|
33
|
+
exists .github/workflows/ci.yml.disabled
|
|
34
|
+
# secrets are referenced, not hardcoded
|
|
35
|
+
[ -f .github/workflows/development.yml ] && grep -q "secrets.GITLAB_TOKEN" .github/workflows/development.yml && pass "development.yml uses secrets" || bad "development.yml secrets.GITLAB_TOKEN"
|
|
36
|
+
|
|
37
|
+
echo "== .github hygiene (references/github-hygiene.md) =="
|
|
38
|
+
# agent config belongs in .claude/, not .github/ (Nx's Copilot-format scaffolding leftovers)
|
|
39
|
+
STRAY=""
|
|
40
|
+
for d in skills agents prompts instructions chatmodes copilot-instructions.md; do
|
|
41
|
+
[ -e ".github/$d" ] && STRAY="$STRAY .github/$d"
|
|
42
|
+
done
|
|
43
|
+
if [ -n "$STRAY" ]; then
|
|
44
|
+
bad "agent config left in .github (delete plugin duplicates, git mv bespoke ones to .claude/):$STRAY"
|
|
45
|
+
else
|
|
46
|
+
pass ".github holds only workflows/actions/GitHub metadata"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
echo "== Update tooling =="
|
|
50
|
+
exists tools/scripts/update.js
|
|
51
|
+
grep -q '"update"' package.json && pass "npm update script" || bad 'package.json "update" script'
|
|
52
|
+
# husky install via postinstall OR prepare
|
|
53
|
+
if grep -qE '"(postinstall|prepare)"[[:space:]]*:[[:space:]]*"husky install"' package.json; then
|
|
54
|
+
pass "husky install hook"
|
|
55
|
+
else
|
|
56
|
+
bad 'package.json postinstall/prepare = "husky install"'
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
echo "== Env / lint / node =="
|
|
60
|
+
exists .env.dev
|
|
61
|
+
[ -f .env.dev ] && grep -q "MULTIMEDIA_URL" .env.dev && pass ".env.dev has MULTIMEDIA_URL" || bad ".env.dev MULTIMEDIA_URL"
|
|
62
|
+
# SITE_URL drives og:url / sitemap.xml / robots.txt, so a value carried over from another project
|
|
63
|
+
# ships that project's domain in this app's SEO. Pass this project's expected host as $3 to check it.
|
|
64
|
+
if [ -n "${SITE_HOST:-}" ] && [ -f .env.dev ]; then
|
|
65
|
+
if grep -qE "^SITE_URL=https?://$SITE_HOST" .env.dev; then
|
|
66
|
+
pass "SITE_URL points at $SITE_HOST"
|
|
67
|
+
else
|
|
68
|
+
bad "SITE_URL does not point at the expected host ($SITE_HOST)"
|
|
69
|
+
fi
|
|
70
|
+
fi
|
|
71
|
+
# unreplaced template placeholders are worse than wrong values: the build silently ships '{{...}}'
|
|
72
|
+
if [ -f .env.dev ] && grep -q "{{" .env.dev; then
|
|
73
|
+
bad ".env.dev still contains {{...}} placeholders"
|
|
74
|
+
else
|
|
75
|
+
pass ".env.dev placeholders replaced"
|
|
76
|
+
fi
|
|
77
|
+
exists .eslintignore
|
|
78
|
+
exists .nvmrc
|
|
79
|
+
|
|
80
|
+
echo "== Node version consistency (expected: $NODE_VERSION) =="
|
|
81
|
+
NVMRC="$(tr -d '[:space:]' < .nvmrc 2>/dev/null || true)"
|
|
82
|
+
[ "$NVMRC" = "$NODE_VERSION" ] && pass ".nvmrc = $NODE_VERSION" || bad ".nvmrc is '$NVMRC', expected '$NODE_VERSION'"
|
|
83
|
+
[ -f apps/web/Dockerfile ] && grep -q "node:$NODE_VERSION-alpine" apps/web/Dockerfile && pass "Dockerfile base image node:$NODE_VERSION" || bad "Dockerfile base image not node:$NODE_VERSION-alpine"
|
|
84
|
+
# every workflow that pins a node-version should pin THIS version
|
|
85
|
+
DRIFT=""
|
|
86
|
+
for wf in .github/workflows/development.yml .github/workflows/e2e.yml; do
|
|
87
|
+
[ -f "$wf" ] || continue
|
|
88
|
+
if grep -qE "node-version:" "$wf" && ! grep -qE "node-version:[[:space:]]*'?${NODE_VERSION}'?" "$wf"; then
|
|
89
|
+
DRIFT="$DRIFT $wf"
|
|
90
|
+
fi
|
|
91
|
+
done
|
|
92
|
+
if [ -n "$DRIFT" ]; then bad "workflow node-version drift:$DRIFT"; else pass "workflow node-version matches $NODE_VERSION"; fi
|
|
93
|
+
|
|
94
|
+
echo
|
|
95
|
+
if [ "$fail" = 0 ]; then echo "VERIFY: all checks passed."; else echo "VERIFY: FAILED — fix the items above and re-run." >&2; fi
|
|
96
|
+
exit "$fail"
|
|
@@ -23,7 +23,33 @@ its own `shell/angular`.
|
|
|
23
23
|
Run `scripts/detect-init.sh <workspace-root>`. It confirms the MobilEMS foundation is present
|
|
24
24
|
(`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If not, tell the user to
|
|
25
25
|
run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`; the reference
|
|
26
|
-
MobilEMS app
|
|
26
|
+
MobilEMS app defines its own) and the reference repo path (same one init used).
|
|
27
|
+
|
|
28
|
+
## Framework first — MANDATORY
|
|
29
|
+
|
|
30
|
+
Read these before writing ANY component, service, pipe, directive or helper, and reuse what they
|
|
31
|
+
list — re-implementing framework code is a defect, not a shortcut:
|
|
32
|
+
|
|
33
|
+
0. [**The Framework API index**](https://github.com/smartsoft001/mobilems-framework/blob/main/README.md#-framework-api) — the map of every package to
|
|
34
|
+
its catalog. Start here when you do not already know which package owns what.
|
|
35
|
+
1. The framework API catalog — `packages/shared/angular/README.md` in this repo, installed as
|
|
36
|
+
`node_modules/@smartsoft001-mobilems/angular/README.md` in a museum app ([browse](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md)).
|
|
37
|
+
This is the path `scripts/verify-module.sh` names when it fails.
|
|
38
|
+
2. `libs/shared/angular/README.md` — the app's own shared lib (built by `mobilems-project-init`)
|
|
39
|
+
|
|
40
|
+
Events is a standalone booking module (no feature package of its own), so it leans on the framework
|
|
41
|
+
hardest:
|
|
42
|
+
|
|
43
|
+
| Do not hand-roll | Use |
|
|
44
|
+
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| a masonry grid (`new Masonry(...)`, per-page masonry JS/CSS) | `<smart-mobilems-masonry-grid>` (`MasonryGridComponent`) with a `#masonryContentTpl`; the `Masonry` provider on the list route only feeds it |
|
|
46
|
+
| filter/search state and `query` building | `FiltersContext` via `FiltersBaseComponent` (or `QueryFilterService` for plain query strings) |
|
|
47
|
+
| confirm/cancel dialogs | `ModalService.open()` + `ModalRef` (and the shared `confirmation-modal` service) |
|
|
48
|
+
| media URLs, meta/OG tags, dictionaries, WCAG, contrast | `FileUrlService`, `SeoService`/`MetaService`, `DictionaryService`, `WcagService`, `ContrastService` |
|
|
49
|
+
| a calendar/date-picker helper duplicated per page | the shared lib's `calendar` component — built once, reused |
|
|
50
|
+
|
|
51
|
+
Anything the framework genuinely lacks goes into `libs/shared/angular` **once** (and into its
|
|
52
|
+
README) — never a per-module copy. `scripts/verify-module.sh` fails the run on hand-rolled masonry.
|
|
27
53
|
|
|
28
54
|
## Steps
|
|
29
55
|
|
|
@@ -89,6 +89,30 @@ export const eventsRoutes: Route[] = [
|
|
|
89
89
|
];
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
> **The `Masonry` provider exists only to feed the framework's `MasonryGridComponent`.** It is DI
|
|
93
|
+
> plumbing, not a grid — never call `new Masonry(...)`, keep a `MasonryInstance`, or re-implement the
|
|
94
|
+
> layout/`reloadItems` dance in a page component. A hand-rolled CSS-grid or column layout is the same
|
|
95
|
+
> defect. `scripts/verify-module.sh` fails the run on both.
|
|
96
|
+
|
|
97
|
+
The list page renders the grid like this — copy this shape, it is the required one:
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<smart-mobilems-masonry-grid [list]="list()" [options]="{ gutter: 24 }">
|
|
101
|
+
<ng-template #masonryContentTpl>
|
|
102
|
+
<div class="masonry-grid-sizer"></div>
|
|
103
|
+
@for (item of list(); track item.id) {
|
|
104
|
+
<div class="masonry-item"><app-events-grid-item [item]="item" /></div>
|
|
105
|
+
}
|
|
106
|
+
</ng-template>
|
|
107
|
+
</smart-mobilems-masonry-grid>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`.masonry-grid-sizer` and `.masonry-item` are required — they are the component's default
|
|
111
|
+
`columnWidth` / `itemSelector`. The card (lib-contents.md) is the only module-specific part. Inputs,
|
|
112
|
+
public methods and the SSR provider are documented in the framework catalog,
|
|
113
|
+
[`packages/shared/angular/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md#masonrygridcomponent)
|
|
114
|
+
(installed as `node_modules/@smartsoft001-mobilems/angular/README.md`).
|
|
115
|
+
|
|
92
116
|
> Route order matters: the specific reservation sub-routes (`:id/potwierdzenie`, `:id/anulowanie`,
|
|
93
117
|
> `:id/booking`) come before the generic `:id/:slug` and `:id` detail routes, and the bare list route
|
|
94
118
|
> `''` comes last. Keep this order.
|
|
@@ -54,6 +54,49 @@ echo "== Foundation deps =="
|
|
|
54
54
|
grep -q "@smartsoft001-mobilems/angular" package.json && pass "@smartsoft001-mobilems/angular present" || bad "@smartsoft001-mobilems/angular missing"
|
|
55
55
|
grep -q "@smartsoft001-mobilems/models" package.json && pass "@smartsoft001-mobilems/models present" || bad "@smartsoft001-mobilems/models missing"
|
|
56
56
|
|
|
57
|
+
echo "== Framework first (packages/shared/angular/README.md) =="
|
|
58
|
+
LIBDIR="libs/museum-events/shell/angular/src/lib"
|
|
59
|
+
# 1. masonry belongs to the framework's MasonryGridComponent — no module-level instances
|
|
60
|
+
MAS="$(grep -rlnE "new Masonry\(|MasonryInstance" libs apps/web/src 2>/dev/null || true)"
|
|
61
|
+
if [ -n "$MAS" ]; then
|
|
62
|
+
bad "hand-rolled masonry — render grids with <smart-mobilems-masonry-grid> (MasonryGridComponent):"
|
|
63
|
+
printf ' %s\n' $MAS >&2
|
|
64
|
+
else
|
|
65
|
+
pass "no hand-rolled masonry instances"
|
|
66
|
+
fi
|
|
67
|
+
# 2. the Masonry provider exists ONLY to feed MasonryGridComponent. Providing it without ever
|
|
68
|
+
# rendering the component means the grid was hand-rolled some other way (CSS columns, flex, …),
|
|
69
|
+
# which the `new Masonry(` grep above cannot see.
|
|
70
|
+
if grep -rq "provide: Masonry" "$LIBDIR" 2>/dev/null; then
|
|
71
|
+
if grep -rq "smart-mobilems-masonry-grid" "$LIBDIR" 2>/dev/null; then
|
|
72
|
+
pass "grids render via <smart-mobilems-masonry-grid>"
|
|
73
|
+
else
|
|
74
|
+
bad "module provides Masonry but never renders <smart-mobilems-masonry-grid> — hand-rolled grid"
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
# 3. a list page must render through the framework grid
|
|
78
|
+
LISTP="$(find "$LIBDIR/pages" -maxdepth 1 -type d -name 'list*' 2>/dev/null || true)"
|
|
79
|
+
if [ -n "$LISTP" ]; then
|
|
80
|
+
if grep -rq "smart-mobilems-masonry-grid" $LISTP 2>/dev/null; then
|
|
81
|
+
pass "list page renders via <smart-mobilems-masonry-grid>"
|
|
82
|
+
else
|
|
83
|
+
bad "list page does not render <smart-mobilems-masonry-grid> (README.md#masonrygridcomponent)"
|
|
84
|
+
fi
|
|
85
|
+
fi
|
|
86
|
+
# 4. filters must EXTEND the framework base class — `context.` alone proves nothing, and a
|
|
87
|
+
# differently-named dir must not silently skip the check
|
|
88
|
+
FILT="$(find "$LIBDIR/components" -maxdepth 1 -type d \( -name 'filters*' -o -name 'search*' -o -name 'facet*' \) 2>/dev/null || true)"
|
|
89
|
+
if [ -n "$FILT" ]; then
|
|
90
|
+
if grep -rqE "FiltersBaseComponent|QueryFilterService" $FILT 2>/dev/null; then
|
|
91
|
+
pass "filters use the framework filter API"
|
|
92
|
+
else
|
|
93
|
+
bad "filters use neither FiltersBaseComponent nor QueryFilterService — no ad-hoc query building:"
|
|
94
|
+
printf ' %s\n' $FILT >&2
|
|
95
|
+
fi
|
|
96
|
+
else
|
|
97
|
+
pass "no filters component in this module"
|
|
98
|
+
fi
|
|
99
|
+
|
|
57
100
|
echo
|
|
58
101
|
if [ "$fail" = 0 ]; then echo "VERIFY: all checks passed."; else echo "VERIFY: FAILED — fix the items above and re-run." >&2; fi
|
|
59
102
|
exit "$fail"
|
|
@@ -19,7 +19,30 @@ Collections is a list/detail view backed by `PublicCollectionsSharedModule.forFe
|
|
|
19
19
|
Run `scripts/detect-init.sh <workspace-root>`. It confirms the MobilEMS foundation is present
|
|
20
20
|
(`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If not, tell the user to
|
|
21
21
|
run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`; reference uses
|
|
22
|
-
|
|
22
|
+
own) and the reference repo path (same one init used).
|
|
23
|
+
|
|
24
|
+
## Framework first — MANDATORY
|
|
25
|
+
|
|
26
|
+
Read these before writing ANY component, service, pipe, directive or helper, and reuse what they
|
|
27
|
+
list — re-implementing framework code is a defect, not a shortcut:
|
|
28
|
+
|
|
29
|
+
0. [**The Framework API index**](https://github.com/smartsoft001/mobilems-framework/blob/main/README.md#-framework-api) — the map of every package to
|
|
30
|
+
its catalog. Start here when you do not already know which package owns what.
|
|
31
|
+
1. The framework API catalog — `packages/shared/angular/README.md` in this repo, installed as
|
|
32
|
+
`node_modules/@smartsoft001-mobilems/angular/README.md` in a museum app ([browse](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md)).
|
|
33
|
+
This is the path `scripts/verify-module.sh` names when it fails.
|
|
34
|
+
2. [`packages/public-collections/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/public-collections/README.md) — this module's package catalog
|
|
35
|
+
3. `libs/shared/angular/README.md` — the app's own shared lib (built by `mobilems-project-init`)
|
|
36
|
+
|
|
37
|
+
| Do not hand-roll | Use |
|
|
38
|
+
| ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
39
|
+
| a masonry grid (`new Masonry(...)`, per-page masonry JS/CSS) | `<smart-mobilems-masonry-grid>` (`MasonryGridComponent`) with a `#masonryContentTpl`; the `Masonry` provider only feeds it |
|
|
40
|
+
| filter state / hand-built `query` arrays | `FiltersBaseComponent<PublicCollection>` + its `FiltersContext` accessors (`searchText`, `keywords`, `cycles`, `page`, `sort`, …) |
|
|
41
|
+
| list/detail plumbing (facade wiring, id-from-route) | the `PublicCollectionsListComponent` / `PublicCollectionsItemComponent` base classes |
|
|
42
|
+
| media URLs, meta/OG tags, filter dictionaries, modals, WCAG | `FileUrlService`, `SeoService`/`MetaService`, `DictionaryService`, `ModalService`, `WcagService` |
|
|
43
|
+
|
|
44
|
+
Anything the framework genuinely lacks goes into `libs/shared/angular` **once** (and into its
|
|
45
|
+
README) — never a per-module copy. `scripts/verify-module.sh` fails the run on hand-rolled masonry.
|
|
23
46
|
|
|
24
47
|
## Steps
|
|
25
48
|
|
|
@@ -27,10 +50,16 @@ run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`;
|
|
|
27
50
|
`@smartsoft001-mobilems/public-collections-shell-angular`,
|
|
28
51
|
`@smartsoft001-mobilems/public-collections-shell-dtos` (match the installed mobilems line).
|
|
29
52
|
Resolve versions at run time; do not `npm install` unless asked. No extra third-party viewer deps.
|
|
30
|
-
2. **Library** —
|
|
31
|
-
copy
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
2. **Library** — scaffold `libs/museum-collections` with this package's Nx generator (deterministic — do
|
|
54
|
+
NOT copy files by hand when the generator is available):
|
|
55
|
+
```bash
|
|
56
|
+
npx nx g @smartsoft001-mobilems/public-collections-shell-angular:museum-module \
|
|
57
|
+
--referenceRoot=<reference-repo> --prefix=<PREFIX>
|
|
58
|
+
```
|
|
59
|
+
It copies the lib from the reference, threads `@<PREFIX>/`, adds both `tsconfig.base.json`
|
|
60
|
+
paths, normalizes `project.json` paths and copies the module's translation groups (pl+en).
|
|
61
|
+
Fallback (older framework line without the generator): do the same by hand per
|
|
62
|
+
`references/lib-contents.md`. The `museum-collections` commit scope registers automatically from the `libs/` dir name.
|
|
34
63
|
3. **Module + routes** — see `references/module-and-routes.md`: `MuseumCollectionsModule` wraps
|
|
35
64
|
`PublicCollectionsSharedModule.forFeature({ routing: false, config: { type: MuseumCollection, apiUrl, entity: 'publicCollections' } })`
|
|
36
65
|
with the `Masonry` SSR factory and the `CrudService → CollectionsService` provider. Add the lazy
|
|
@@ -40,20 +69,23 @@ run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`;
|
|
|
40
69
|
`components/collections-grid-item` (it imports `public-collections-domain`) in the shared lib and
|
|
41
70
|
export it from `components/index.ts`. Collections contributes no widget-map entry and no model type
|
|
42
71
|
(those are objects-only).
|
|
43
|
-
5. **
|
|
72
|
+
5. **Translations** — copy the module's translation groups (`COLLECTIONS`) from the reference
|
|
73
|
+
repo's `apps/web/public/translations.json` into the app's file, for BOTH `pl` and `en`.
|
|
74
|
+
Complete group = every reference key present; every key referenced in the module's code
|
|
75
|
+
must exist in both languages — a raw `GROUP.key` rendered in the UI is a defect.
|
|
76
|
+
6. **Verify** — run `REF_ROOT=<reference-repo> scripts/verify-module.sh <workspace-root> <PREFIX>`: asserts the lib +
|
|
44
77
|
`MuseumCollection` entity exist, the `forFeature` is wired, the `kolekcje` route is registered
|
|
45
78
|
(top-level + search child), the `collections-grid-item` component is built and exported, and the
|
|
46
79
|
three public-collections deps are present. Fix and re-run.
|
|
47
80
|
|
|
48
|
-
## Known
|
|
81
|
+
## Known pitfalls (fix while copying, not after)
|
|
49
82
|
|
|
50
|
-
|
|
51
|
-
Tomaszów MOB-956). The cross-cutting rules live in `mobilems-project-init`
|
|
83
|
+
The cross-cutting rules live in `mobilems-project-init`
|
|
52
84
|
`references/qa-hardening.md`; these are the collections-specific ones:
|
|
53
85
|
|
|
54
86
|
- **One card component, reused everywhere.** The home page and the collection detail must render the
|
|
55
87
|
SAME shared `collections-grid-item` / object card components as the list views — a second ad-hoc
|
|
56
|
-
card on the home page
|
|
88
|
+
card on the home page is a defect. Never fork a card per page.
|
|
57
89
|
- **No hardcoded UI strings.** The detail heading was shipped as a literal `Zbiory {{ title }}` and
|
|
58
90
|
stayed Polish in EN — every static label goes through a translation key (PL + EN in
|
|
59
91
|
`translations.json`). The list page needs a VISIBLE translated heading (not only `sr-only`), and
|
|
@@ -64,10 +96,10 @@ Tomaszów MOB-956). The cross-cutting rules live in `mobilems-project-init`
|
|
|
64
96
|
objects.
|
|
65
97
|
- **Search debounce 250 ms** in the filters (`@debounce(250)`), total count + shared
|
|
66
98
|
`NextPageComponent` on the list — consistent with objects/news.
|
|
67
|
-
- **Keyword/cycle chips wrap
|
|
68
|
-
- **Heading font must cover Polish diacritics** —
|
|
69
|
-
|
|
70
|
-
|
|
99
|
+
- **Keyword/cycle chips must wrap at 360 px** — unwrapped chips cause page-level horizontal scroll.
|
|
100
|
+
- **Heading font must cover Polish diacritics** — a serif lacking `ą ć ę ł ń ś ź ż` renders card
|
|
101
|
+
titles in a silent fallback; verify the Tailwind heading `fontFamily` is non-empty and latin-ext
|
|
102
|
+
complete (see qa-hardening).
|
|
71
103
|
- **Warning-clean templates:** remove needless `?.` on non-nullable signals (NG8107) after copying.
|
|
72
104
|
|
|
73
105
|
## Notes
|
|
@@ -74,3 +74,14 @@ export class MuseumCollection extends PublicCollection {
|
|
|
74
74
|
The `components/filters` component renders the keyword/cycle facets exposed by the entity's
|
|
75
75
|
`connections` block; the list/detail rendering itself comes from `PublicCollectionsSharedModule`.
|
|
76
76
|
No third-party viewer deps are needed (unlike the objects 3D tab).
|
|
77
|
+
|
|
78
|
+
It **must** extend the framework base class rather than build `query` arrays by hand — that base is
|
|
79
|
+
what wires the debounced writes back through `CrudFacade`:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
export class FiltersComponent extends FiltersBaseComponent<PublicCollection> {}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Read filter state through the inherited `context` accessors (`searchText`, `keywords`, `cycles`,
|
|
86
|
+
`page`, `sort`), never by assembling a `query` yourself. `scripts/verify-module.sh` fails the run if
|
|
87
|
+
this component does not extend `FiltersBaseComponent`.
|
|
@@ -68,6 +68,32 @@ import { CollectionsService } from './services';
|
|
|
68
68
|
export class MuseumCollectionsModule {}
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
> **The `Masonry` provider exists only to feed the framework's `MasonryGridComponent`.** It is DI
|
|
72
|
+
> plumbing, not a grid — never call `new Masonry(...)`, keep a `MasonryInstance`, or re-implement the
|
|
73
|
+
> layout/`reloadItems` dance in a page component. A hand-rolled CSS-grid or column layout is the same
|
|
74
|
+
> defect. `scripts/verify-module.sh` fails the run on both.
|
|
75
|
+
|
|
76
|
+
The list page renders the grid like this — copy this shape, it is the required one:
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<smart-mobilems-masonry-grid [list]="list()" [options]="{ gutter: 24 }">
|
|
80
|
+
<ng-template #masonryContentTpl>
|
|
81
|
+
<div class="masonry-grid-sizer"></div>
|
|
82
|
+
@for (item of list(); track item.id) {
|
|
83
|
+
<div class="masonry-item">
|
|
84
|
+
<smart-collections-grid-item [item]="item" />
|
|
85
|
+
</div>
|
|
86
|
+
}
|
|
87
|
+
</ng-template>
|
|
88
|
+
</smart-mobilems-masonry-grid>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`.masonry-grid-sizer` and `.masonry-item` are required — they are the component's default
|
|
92
|
+
`columnWidth` / `itemSelector`. The card (shared-components.md) is the only module-specific part. Inputs,
|
|
93
|
+
public methods and the SSR provider are documented in the framework catalog,
|
|
94
|
+
[`packages/shared/angular/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md#masonrygridcomponent)
|
|
95
|
+
(installed as `node_modules/@smartsoft001-mobilems/angular/README.md`).
|
|
96
|
+
|
|
71
97
|
## `collections.routes.ts`
|
|
72
98
|
|
|
73
99
|
Identical shape to the objects routes (`:id/:slug` → Item, `:id` → Item, `''` → List).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* check-translations.js "<GROUP GROUP …>" [reference-root]
|
|
4
|
+
*
|
|
5
|
+
* Run from the workspace root (verify-module.sh cd's there). Verifies the
|
|
6
|
+
* module's translation groups in apps/web/public/translations.json:
|
|
7
|
+
* 1. each group exists and is non-empty in BOTH pl and en,
|
|
8
|
+
* 2. every literal GROUP.* key referenced in libs/ or apps/web/src exists
|
|
9
|
+
* in both languages (dead references = defect),
|
|
10
|
+
* 3. with a reference root: every reference key of the group exists in the
|
|
11
|
+
* generated file — missing keys are listed (the module is incomplete).
|
|
12
|
+
* Prints PASS/FAIL lines in the verify-module.sh idiom; exit 1 on any FAIL.
|
|
13
|
+
*/
|
|
14
|
+
const fs = require("fs");
|
|
15
|
+
|
|
16
|
+
const [groupsArg, refRoot] = process.argv.slice(2);
|
|
17
|
+
const groups = (groupsArg || "").split(/\s+/).filter(Boolean);
|
|
18
|
+
if (!groups.length) { console.log(" FAIL: no translation groups given"); process.exit(1); }
|
|
19
|
+
|
|
20
|
+
const TRANS = "apps/web/public/translations.json";
|
|
21
|
+
let fail = 0;
|
|
22
|
+
const pass = (m) => console.log(` PASS: ${m}`);
|
|
23
|
+
const bad = (m) => { console.log(` FAIL: ${m}`); fail = 1; };
|
|
24
|
+
|
|
25
|
+
const flat = (obj, prefix) => Object.entries(obj || {}).flatMap(([k, v]) =>
|
|
26
|
+
v && typeof v === "object" ? flat(v, `${prefix}.${k}`) : [`${prefix}.${k}`]);
|
|
27
|
+
|
|
28
|
+
const readTrans = (p) => { try { return JSON.parse(fs.readFileSync(p, "utf8")).data.translations; } catch { return null; } };
|
|
29
|
+
|
|
30
|
+
const gen = readTrans(TRANS);
|
|
31
|
+
if (!gen) { bad(`cannot read ${TRANS}`); process.exit(1); }
|
|
32
|
+
|
|
33
|
+
// 1. groups present + non-empty in both languages
|
|
34
|
+
const genKeys = { pl: new Set(), en: new Set() };
|
|
35
|
+
for (const lang of ["pl", "en"]) {
|
|
36
|
+
for (const g of groups) {
|
|
37
|
+
const keys = flat(gen[lang]?.[g], g);
|
|
38
|
+
keys.forEach((k) => genKeys[lang].add(k));
|
|
39
|
+
if (keys.length) pass(`${g} present in ${lang} (${keys.length} keys)`);
|
|
40
|
+
else bad(`${g} missing or empty in ${lang}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. dead references — literal GROUP.* keys used in sources must exist.
|
|
45
|
+
// Dynamic tails (`GROUP.${x}`) don't match the literal regex, and a matched
|
|
46
|
+
// PREFIX that is a parent of existing keys counts as present.
|
|
47
|
+
const walk = (dir) => {
|
|
48
|
+
let out = [];
|
|
49
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
50
|
+
if (e.name === "node_modules" || e.name.startsWith(".")) continue;
|
|
51
|
+
const p = `${dir}/${e.name}`;
|
|
52
|
+
if (e.isDirectory()) out = out.concat(walk(p));
|
|
53
|
+
else if (/\.(ts|html)$/.test(e.name)) out.push(p);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
};
|
|
57
|
+
let src = "";
|
|
58
|
+
for (const d of ["libs", "apps/web/src"]) {
|
|
59
|
+
if (fs.existsSync(d)) src += walk(d).map((f) => { try { return fs.readFileSync(f, "utf8"); } catch { return ""; } }).join("\n");
|
|
60
|
+
}
|
|
61
|
+
const re = new RegExp(`\\b(?:${groups.join("|")})(?:\\.[A-Za-z0-9_]+)+`, "g");
|
|
62
|
+
const used = new Set(src.match(re) || []);
|
|
63
|
+
const dead = { pl: [], en: [] };
|
|
64
|
+
for (const key of used) {
|
|
65
|
+
for (const lang of ["pl", "en"]) {
|
|
66
|
+
const ks = genKeys[lang];
|
|
67
|
+
if (!ks.has(key) && ![...ks].some((k) => k.startsWith(`${key}.`))) dead[lang].push(key);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
for (const lang of ["pl", "en"]) {
|
|
71
|
+
if (dead[lang].length) bad(`${lang}: keys referenced in code but missing in ${TRANS}: ${dead[lang].slice(0, 8).join(", ")}${dead[lang].length > 8 ? ` (+${dead[lang].length - 8})` : ""}`);
|
|
72
|
+
else pass(`no dead ${lang} references (${used.size} literal keys checked)`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 3. completeness vs reference
|
|
76
|
+
if (refRoot) {
|
|
77
|
+
const ref = readTrans(`${refRoot}/apps/web/public/translations.json`);
|
|
78
|
+
if (!ref) bad(`cannot read reference translations under ${refRoot}`);
|
|
79
|
+
else {
|
|
80
|
+
for (const lang of ["pl", "en"]) {
|
|
81
|
+
const missing = groups.flatMap((g) => flat(ref[lang]?.[g], g)).filter((k) => !genKeys[lang].has(k));
|
|
82
|
+
if (missing.length) bad(`${lang}: ${missing.length} reference keys missing: ${missing.slice(0, 8).join(", ")}${missing.length > 8 ? " …" : ""}`);
|
|
83
|
+
else pass(`${lang}: all reference keys covered`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
console.log(" SKIP: no reference root given — completeness vs reference not checked");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
process.exit(fail);
|
|
@@ -9,6 +9,7 @@ set -eu
|
|
|
9
9
|
|
|
10
10
|
ROOT="${1:?usage: verify-module.sh <workspace-root> <PREFIX>}"
|
|
11
11
|
PREFIX="${2:?usage: verify-module.sh <workspace-root> <PREFIX>}"
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
12
13
|
cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
|
|
13
14
|
|
|
14
15
|
fail=0
|
|
@@ -39,6 +40,59 @@ grep -q "@smartsoft001-mobilems/public-collections-domain" package.json && pass
|
|
|
39
40
|
grep -q "@smartsoft001-mobilems/public-collections-shell-angular" package.json && pass "public-collections-shell-angular dep" || bad "public-collections-shell-angular dep missing"
|
|
40
41
|
grep -q "@smartsoft001-mobilems/public-collections-shell-dtos" package.json && pass "public-collections-shell-dtos dep" || bad "public-collections-shell-dtos dep missing"
|
|
41
42
|
|
|
43
|
+
echo "== Framework first (packages/shared/angular/README.md) =="
|
|
44
|
+
LIBDIR="libs/museum-collections/shell/angular/src/lib"
|
|
45
|
+
# 1. masonry belongs to the framework's MasonryGridComponent — no module-level instances
|
|
46
|
+
MAS="$(grep -rlnE "new Masonry\(|MasonryInstance" libs apps/web/src 2>/dev/null || true)"
|
|
47
|
+
if [ -n "$MAS" ]; then
|
|
48
|
+
bad "hand-rolled masonry — render grids with <smart-mobilems-masonry-grid> (MasonryGridComponent):"
|
|
49
|
+
printf ' %s\n' $MAS >&2
|
|
50
|
+
else
|
|
51
|
+
pass "no hand-rolled masonry instances"
|
|
52
|
+
fi
|
|
53
|
+
# 2. the Masonry provider exists ONLY to feed MasonryGridComponent. Providing it without ever
|
|
54
|
+
# rendering the component means the grid was hand-rolled some other way (CSS columns, flex, …),
|
|
55
|
+
# which the `new Masonry(` grep above cannot see.
|
|
56
|
+
if grep -rq "provide: Masonry" "$LIBDIR" 2>/dev/null; then
|
|
57
|
+
if grep -rq "smart-mobilems-masonry-grid" "$LIBDIR" 2>/dev/null; then
|
|
58
|
+
pass "grids render via <smart-mobilems-masonry-grid>"
|
|
59
|
+
else
|
|
60
|
+
bad "module provides Masonry but never renders <smart-mobilems-masonry-grid> — hand-rolled grid"
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
# 3. the ITEM page renders its objects grid through the framework grid — in the
|
|
64
|
+
# reference the collections LIST is a plain grid; masonry lives on the item page
|
|
65
|
+
ITEMP="$(find "$LIBDIR/pages" -maxdepth 1 -type d -name 'item*' 2>/dev/null || true)"
|
|
66
|
+
if [ -n "$ITEMP" ]; then
|
|
67
|
+
if grep -rq "smart-mobilems-masonry-grid" $ITEMP 2>/dev/null; then
|
|
68
|
+
pass "item page renders via <smart-mobilems-masonry-grid>"
|
|
69
|
+
else
|
|
70
|
+
bad "item page does not render <smart-mobilems-masonry-grid> (README.md#masonrygridcomponent)"
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
# 4. filters must EXTEND the framework base class — `context.` alone proves nothing, and a
|
|
74
|
+
# differently-named dir must not silently skip the check
|
|
75
|
+
FILT="$(find "$LIBDIR/components" -maxdepth 1 -type d \( -name 'filters*' -o -name 'search*' -o -name 'facet*' \) 2>/dev/null || true)"
|
|
76
|
+
if [ -n "$FILT" ]; then
|
|
77
|
+
if grep -rqE "FiltersBaseComponent" $FILT 2>/dev/null; then
|
|
78
|
+
pass "filters use the framework filter API"
|
|
79
|
+
else
|
|
80
|
+
bad "filters do not extend FiltersBaseComponent — no ad-hoc query building:"
|
|
81
|
+
printf ' %s\n' $FILT >&2
|
|
82
|
+
fi
|
|
83
|
+
else
|
|
84
|
+
pass "no filters component in this module"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
echo "== Translations (REF_ROOT=${REF_ROOT:-unset}) =="
|
|
88
|
+
# Groups: present in pl+en, no dead references, complete vs the reference repo
|
|
89
|
+
# (export REF_ROOT=<reference-repo-root> to enable the completeness diff).
|
|
90
|
+
if node "$SCRIPT_DIR/check-translations.js" "COLLECTIONS" "${REF_ROOT:-}"; then
|
|
91
|
+
pass "translation groups complete"
|
|
92
|
+
else
|
|
93
|
+
bad "translation gaps — fix the FAIL lines above"
|
|
94
|
+
fi
|
|
95
|
+
|
|
42
96
|
echo
|
|
43
97
|
if [ "$fail" = 0 ]; then echo "VERIFY: all checks passed."; else echo "VERIFY: FAILED — fix the items above and re-run." >&2; fi
|
|
44
98
|
exit "$fail"
|
|
@@ -20,7 +20,30 @@ map, audio, 3D model, animation, virtual walk) backed by `ObjectsSharedModule.fo
|
|
|
20
20
|
Run `scripts/detect-init.sh <workspace-root>`. It confirms the MobilEMS foundation is present
|
|
21
21
|
(`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If not, tell the user to
|
|
22
22
|
run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`; reference uses
|
|
23
|
-
|
|
23
|
+
own) and the reference repo path (same one init used).
|
|
24
|
+
|
|
25
|
+
## Framework first — MANDATORY
|
|
26
|
+
|
|
27
|
+
Read these before writing ANY component, service, pipe, directive or helper, and reuse what they
|
|
28
|
+
list — re-implementing framework code is a defect, not a shortcut:
|
|
29
|
+
|
|
30
|
+
0. [**The Framework API index**](https://github.com/smartsoft001/mobilems-framework/blob/main/README.md#-framework-api) — the map of every package to
|
|
31
|
+
its catalog. Start here when you do not already know which package owns what.
|
|
32
|
+
1. The framework API catalog — `packages/shared/angular/README.md` in this repo, installed as
|
|
33
|
+
`node_modules/@smartsoft001-mobilems/angular/README.md` in a museum app ([browse](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md)).
|
|
34
|
+
This is the path `scripts/verify-module.sh` names when it fails.
|
|
35
|
+
2. [`packages/objects/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/objects/README.md) — this module's package catalog
|
|
36
|
+
3. `libs/shared/angular/README.md` — the app's own shared lib (built by `mobilems-project-init`)
|
|
37
|
+
|
|
38
|
+
| Do not hand-roll | Use |
|
|
39
|
+
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
40
|
+
| a masonry grid (`new Masonry(...)`, per-page masonry JS/CSS) | `<smart-mobilems-masonry-grid>` (`MasonryGridComponent`) with a `#masonryContentTpl`; the `Masonry` provider in step 3 only feeds it |
|
|
41
|
+
| filter state / hand-built `query` arrays | `FiltersBaseComponent<MsObject>` + its `FiltersContext` accessors (`searchText`, `keywords`, `page`, `sort`, `onlyImage`, …) |
|
|
42
|
+
| list/detail plumbing (facade wiring, id-from-route, PDF) | the `ObjectsListComponent` / `ObjectsItemComponent` base classes |
|
|
43
|
+
| media URLs, meta/OG tags, filter dictionaries, modals, WCAG | `FileUrlService`, `SeoService`/`MetaService`, `DictionaryService`, `ModalService`, `WcagService` |
|
|
44
|
+
|
|
45
|
+
Anything the framework genuinely lacks goes into `libs/shared/angular` **once** (and into its
|
|
46
|
+
README) — never a per-module copy. `scripts/verify-module.sh` fails the run on hand-rolled masonry.
|
|
24
47
|
|
|
25
48
|
## Steps
|
|
26
49
|
|
|
@@ -28,10 +51,16 @@ run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`;
|
|
|
28
51
|
`@smartsoft001-mobilems/objects-shell-dtos` (match the installed mobilems line) and
|
|
29
52
|
`online-3d-viewer` (the 3D tab; `leaflet`/`openseadragon`/`swiper` are already foundation deps).
|
|
30
53
|
Resolve versions at run time; do not `npm install` unless asked.
|
|
31
|
-
2. **Library** —
|
|
32
|
-
copy
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
2. **Library** — scaffold `libs/museum-objects` with this package's Nx generator (deterministic — do
|
|
55
|
+
NOT copy files by hand when the generator is available):
|
|
56
|
+
```bash
|
|
57
|
+
npx nx g @smartsoft001-mobilems/objects-shell-angular:museum-module \
|
|
58
|
+
--referenceRoot=<reference-repo> --prefix=<PREFIX>
|
|
59
|
+
```
|
|
60
|
+
It copies the lib from the reference, threads `@<PREFIX>/`, adds both `tsconfig.base.json`
|
|
61
|
+
paths, normalizes `project.json` paths and copies the module's translation groups (pl+en).
|
|
62
|
+
Fallback (older framework line without the generator): do the same by hand per
|
|
63
|
+
`references/lib-contents.md`. The `museum-objects` commit scope registers automatically from the `libs/` dir name.
|
|
35
64
|
3. **Module + routes** — see `references/module-and-routes.md`: `MuseumObjectsModule` wraps
|
|
36
65
|
`ObjectsSharedModule.forFeature({ routing: false, config: { type: MsObject, apiUrl, entity: 'objects' } })`
|
|
37
66
|
with the `Masonry` SSR factory. Add the lazy `obiekty` route to `appRoutes` in
|
|
@@ -40,15 +69,18 @@ run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`;
|
|
|
40
69
|
`objects-list-item`, and the object gallery widget (`widget/gallery-object`) in the shared lib +
|
|
41
70
|
export them; register the object gallery widget in the widget content-type map (`GALLERY_OBJECT`);
|
|
42
71
|
and add `ISpecificObject` (extends `MsObject`) to `models/objects.model.ts`.
|
|
43
|
-
5. **
|
|
72
|
+
5. **Translations** — copy the module's translation groups (`OBJECTS`, `DETAILS_PAGE`, `GALLERY`) from the reference
|
|
73
|
+
repo's `apps/web/public/translations.json` into the app's file, for BOTH `pl` and `en`.
|
|
74
|
+
Complete group = every reference key present; every key referenced in the module's code
|
|
75
|
+
must exist in both languages — a raw `GROUP.key` rendered in the UI is a defect.
|
|
76
|
+
6. **Verify** — run `REF_ROOT=<reference-repo> scripts/verify-module.sh <workspace-root> <PREFIX>`: asserts the lib exists, the
|
|
44
77
|
`obiekty` route is registered (top-level + search child), the objects shared components are built
|
|
45
78
|
and exported, the widget map registers the object gallery widget, and `ISpecificObject` is present.
|
|
46
79
|
Fix and re-run.
|
|
47
80
|
|
|
48
|
-
## Known
|
|
81
|
+
## Known pitfalls (fix while copying, not after)
|
|
49
82
|
|
|
50
|
-
|
|
51
|
-
Tomaszów MOB-949/993). The cross-cutting rules live in `mobilems-project-init`
|
|
83
|
+
The cross-cutting rules live in `mobilems-project-init`
|
|
52
84
|
`references/qa-hardening.md`; these are the objects-specific ones:
|
|
53
85
|
|
|
54
86
|
- **Search debounce.** The panel full-text search (`app-objects-searchbar variant="panel"`) must
|
|
@@ -63,7 +95,7 @@ Tomaszów MOB-949/993). The cross-cutting rules live in `mobilems-project-init`
|
|
|
63
95
|
is valid; `Number('') === 0` — reject empty strings explicitly). Duplicating the predicate in two
|
|
64
96
|
components caused a "map never renders" bug.
|
|
65
97
|
- **Field labels match the client's field spec everywhere** — list card, detail and filters must use
|
|
66
|
-
the same names (
|
|
98
|
+
the same names (e.g. do not render "Materiał" where the mockup says "Tworzywo").
|
|
67
99
|
- **Keywords section** maps the API `tags[]` field (not `keywords[]`) and renders clickable links
|
|
68
100
|
that filter the list.
|
|
69
101
|
- **Warning-clean templates:** after copying, remove needless `?.` on non-nullable signals (NG8107)
|