@smartsoft001-mobilems/objects-shell-angular 2.125.0 → 2.127.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.
@@ -1,10 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { TemplateRef, ModuleWithProviders, Signal, AfterViewInit, WritableSignal } from '@angular/core';
2
+ import { TemplateRef, ModuleWithProviders, Signal, AfterViewInit, WritableSignal, OnInit, ResourceRef } from '@angular/core';
3
3
  import * as _smartsoft001_crud_shell_angular from '@smartsoft001/crud-shell-angular';
4
4
  import { ICrudModuleOptionsWithoutRoutng, CrudFacade, State as State$1, CrudListPageBaseComponent, CrudItemPageBaseComponent, CrudService, ICrudFilter } from '@smartsoft001/crud-shell-angular';
5
5
  import { IEntity } from '@smartsoft001/domain-core';
6
6
  import * as i1$1 from '@smartsoft001-mobilems/angular';
7
- import { FiltersBaseComponent, CrudBaseService, SeoService, TranslationService, ICatalogOptions, ICatalog } from '@smartsoft001-mobilems/angular';
7
+ import { FiltersBaseComponent, CrudBaseService, SeoService, TranslationService, IDictionaryPage, ICatalogOptions, ICatalog, ISeoMetadata, INamedEntrySource, INamedEntryParts } from '@smartsoft001-mobilems/angular';
8
8
  import * as i1 from '@angular/common';
9
9
  import { MsObject } from '@smartsoft001-mobilems/objects-domain';
10
10
  import * as dist_packages_objects_domain_src from 'dist/packages/objects/domain/src';
@@ -14,6 +14,7 @@ import * as _ngrx_store from '@ngrx/store';
14
14
  import { Action } from '@ngrx/store';
15
15
  import { MsFile } from '@smartsoft001-mobilems/models';
16
16
  import { ListMode } from '@smartsoft001/angular';
17
+ import { Observable } from 'rxjs';
17
18
 
18
19
  declare class FiltersComponent extends FiltersBaseComponent<MsObject> {
19
20
  static ɵfac: i0.ɵɵFactoryDeclaration<FiltersComponent, never>;
@@ -126,17 +127,113 @@ declare abstract class ObjectsItemComponent extends CrudItemPageBaseComponent<Ms
126
127
  facade: ObjectsFacade;
127
128
  readonly loading: i0.Signal<boolean>;
128
129
  readonly item: i0.Signal<MsObject>;
130
+ /** @deprecated No museum uses it; download the card through the app's own download service. */
129
131
  readonly isPdfDownloading: i0.WritableSignal<boolean>;
130
132
  protected constructor();
133
+ /** @deprecated No museum uses it; download the card through the app's own download service. */
131
134
  onDownload(): Promise<void>;
132
135
  static ɵfac: i0.ɵɵFactoryDeclaration<ObjectsItemComponent, never>;
133
136
  static ɵdir: i0.ɵɵDirectiveDeclaration<ObjectsItemComponent, never, never, {}, {}, never, never, true, never>;
134
137
  }
135
138
 
136
- declare const SERVICES: (typeof ObjectsService | {
137
- provide: typeof CrudService;
138
- useClass: typeof ObjectsService;
139
- })[];
139
+ /** Response of `GET object/:id/related`. */
140
+ interface IObjectRelated {
141
+ objects: Array<{
142
+ id: number | string;
143
+ }>;
144
+ collections: Array<{
145
+ id: number | string;
146
+ }>;
147
+ articles: Array<{
148
+ id: number | string;
149
+ }>;
150
+ }
151
+ /**
152
+ * A public (thematic) collection from the `GET public-collections/page/{n}`
153
+ * list — as much as the object card ("Related collections") and the filters
154
+ * panel need.
155
+ */
156
+ interface IObjectCollectionSummary {
157
+ id: number | string;
158
+ title: string;
159
+ publicationDate: string | null;
160
+ /** Number of objects in the collection — the page size of the membership check. */
161
+ itemCount: number | null;
162
+ }
163
+ /**
164
+ * Segregations of the `ArchaeologicalData` dictionary — per the MobileMS task
165
+ * (Kanbanize 50040, MOB-1844): site towns, periods (epochs) and cultures.
166
+ */
167
+ type ArchaeologicalSegregation = 'ArcheoTown' | 'ArcheoPeriod' | 'ArcheoCulture';
168
+ /**
169
+ * `ObjectsService` plus the reads of the object details page and the filter
170
+ * dictionaries. A subclass, so `ObjectsService` (the `CrudService` of the
171
+ * objects module) stays unchanged. Request URLs, parameter order included,
172
+ * are transfer cache keys: keep them as they are.
173
+ */
174
+ declare class ObjectsDataService extends ObjectsService {
175
+ private readonly objectsDataIsServer;
176
+ /**
177
+ * Counts a visit of the object card: `POST api/object/:id/visit`. Browser
178
+ * only — the caller runs it under `isPlatformBrowser` and catches its errors.
179
+ */
180
+ logVisit(id: number | string): Promise<void>;
181
+ /**
182
+ * "You may also be interested in" — `GET object/:id/related` (objects,
183
+ * collections, articles). A network error (status 0) is retried once; any
184
+ * other error, or a second failed request, goes on to the `rxResource`
185
+ * (error state — the section stays hidden, `reload()` retries). In dev mode
186
+ * the error is logged, except a 404 on the server: there `related` starts
187
+ * next to the card (MOB-1850), so a missing object gives two 404s and the
188
+ * card view shows the error. In the browser `related` follows the card — a
189
+ * 404 is unexpected there and stays in the console.
190
+ */
191
+ getRelated(id: number | string): Observable<IObjectRelated>;
192
+ /**
193
+ * One page of the public collections list, newest publication first
194
+ * (`sort=publicationDate-desc`), with the phrase in `filter[phrase]` (the
195
+ * "Field description" document: "Search in collections: filter:phrase").
196
+ * 404 `NO_RESULTS_FOR_GIVEN_QUERY` is an empty page, any other error propagates.
197
+ */
198
+ loadPublicCollectionsPage(page?: number, query?: string): Observable<{
199
+ items: IObjectCollectionSummary[];
200
+ totalPagesCount: number;
201
+ }>;
202
+ /**
203
+ * Public collections as the dictionary of the "Thematic collection" field in
204
+ * the advanced filters (MOB-1844): `filter[entryCollections]` in
205
+ * `search/Object` takes ids from `public-collections`, not from the
206
+ * `ECollection` dictionary.
207
+ */
208
+ loadCollectionsDictionary(page?: number, query?: string): Observable<IDictionaryPage>;
209
+ /**
210
+ * Dictionary of the "Archaeological filters" fields (MOB-1844). In Muza it is
211
+ * `ArchaeologicalData` segregated into `ArcheoTown|ArcheoPeriod|ArcheoCulture`
212
+ * (Kanbanize 50040), but MobileMS exposed each segregation as its own
213
+ * dictionary type: `dictionary/ArcheoTown/search/page/{n}` (develop
214
+ * 2026-10-21 — the type is recognised, `ArchaeologicalData` still 409). Same
215
+ * path and shape as the framework's `DataService.loadFilterDictionary`. Until
216
+ * production has these types the API answers 409 and the field shows a load error.
217
+ */
218
+ loadArchaeologicalDictionary(segregation: ArchaeologicalSegregation, page?: number, query?: string): Observable<IDictionaryPage>;
219
+ /** All public collections (consecutive pages of the list). */
220
+ loadAllPublicCollections(): Observable<IObjectCollectionSummary[]>;
221
+ /**
222
+ * "Related collections" of the object card per the "Field description"
223
+ * document: `search/Object` with `filter[entryCollections]`, sorted by
224
+ * publication date. The API gives no reverse direction (`portalCollections`
225
+ * and `related.collections` are empty also for objects in a collection), so
226
+ * for each public collection we check whether its objects contain this
227
+ * object — a few collections at a time, in list order. A collection that
228
+ * could not be checked is skipped; a failed list means no section (an empty
229
+ * list), not a page error.
230
+ */
231
+ loadRelatedCollections(objectId: number | string): Observable<IObjectCollectionSummary[]>;
232
+ /** Whether `search/Object` with the collection's `filter[entryCollections]` contains the object; an error → unknown → `false`. */
233
+ private objectsDataCollectionContains;
234
+ static ɵfac: i0.ɵɵFactoryDeclaration<ObjectsDataService, never>;
235
+ static ɵprov: i0.ɵɵInjectableDeclaration<ObjectsDataService>;
236
+ }
140
237
 
141
238
  type SortType = 'random' | 'date-asc' | 'date-desc' | 'name-asc' | 'name-desc';
142
239
  type IKeyValue = {
@@ -252,6 +349,41 @@ interface ISpecificObject extends MsObject {
252
349
  shareMuseumObjectMultimediaEnabled?: boolean;
253
350
  }
254
351
 
352
+ /** A gallery item (full size, for fullscreen). */
353
+ interface IObjectGalleryItem {
354
+ title?: string;
355
+ image?: MsFile;
356
+ file?: MsFile;
357
+ }
358
+ /**
359
+ * Shared listing of an object's media (the main image + `additionalImages`
360
+ * filtered by `formFeature`) — used by the details page image viewer
361
+ * (MOB-1801), the hero gallery, the thumbnails and the HD images section.
362
+ */
363
+ declare class ObjectMediaService {
364
+ private readonly objectMediaFileUrl;
365
+ /** Gallery items (full size, for fullscreen). */
366
+ galleryItems(obj: MsObject | undefined, types: FormFeatureType[], includeMainImage?: boolean): IObjectGalleryItem[];
367
+ /** Thumbnail URLs (`cache` mode + a forced `.jpg` for the CDN). */
368
+ thumbnailUrls(obj: MsObject | undefined, types: FormFeatureType[], includeMainImage?: boolean): string[];
369
+ /**
370
+ * Whether the object has files of the types (video, 3D model) — by
371
+ * `filePath`, because `FileUrlService` returns a placeholder for
372
+ * non-image extensions.
373
+ */
374
+ hasFiles(obj: MsObject | undefined, types: FormFeatureType[]): boolean;
375
+ private objectMediaAdditional;
376
+ /** Replaces the extension with `.jpg` — CDN cache compatibility. */
377
+ private objectMediaJpg;
378
+ static ɵfac: i0.ɵɵFactoryDeclaration<ObjectMediaService, never>;
379
+ static ɵprov: i0.ɵɵInjectableDeclaration<ObjectMediaService>;
380
+ }
381
+
382
+ declare const SERVICES: (typeof ObjectsService | {
383
+ provide: typeof CrudService;
384
+ useClass: typeof ObjectsService;
385
+ })[];
386
+
255
387
  /**
256
388
  * Filter defaults of the objects list (Muzeum Podlasia): 20 objects per page,
257
389
  * newest first. **The key order is part of the contract** — the first read
@@ -384,5 +516,198 @@ declare abstract class ObjectsCatalogBaseComponent extends ObjectsListComponent
384
516
  static ɵdir: i0.ɵɵDirectiveDeclaration<ObjectsCatalogBaseComponent, never, never, {}, {}, never, never, true, never>;
385
517
  }
386
518
 
387
- export { AdvancedFilterType, ComponentsModule, FiltersComponent, FormFeatureType, ListComponent, MULTIMEDIA_DICTIONARY, OBJECTS_CATALOG_DEFAULTS, OBJECTS_CATALOG_SEO, ObjectsCatalogBaseComponent, ObjectsFacade, ObjectsItemComponent, ObjectsListComponent, ObjectsModule, ObjectsService, ObjectsSharedModule, SERVICES, getObjectsState, getReducer, initialState };
388
- export type { FileType, IAnimationObject, IKeyValue, IModel3DObject, IMultimediaDictionaryItem, IObjectsCatalogLoadMore, IObjectsCatalogOptions, IObjectsCatalogSeo, ISpecificObject, SortType, State };
519
+ /** Options of an object details page, passed to `super(options)`. */
520
+ interface IObjectDetailsOptions {
521
+ /** Translation key of the site name in the title. Default `MODEL.mainTitle`. */
522
+ siteNameKey?: string;
523
+ /** Opt-in „You may also be interested in” (`GET object/:id/related`). Default `false`. */
524
+ related?: boolean;
525
+ /** Opt-in „Related collections” (reverse `search/Object` lookup, browser only). Default `false`. */
526
+ relatedCollections?: boolean;
527
+ }
528
+ /**
529
+ * Base of an object details page (`/obiekty/:id`, MOB-1801), from Muzeum
530
+ * Podlasia's item page: the SEO (also in SSR), the item effect with the
531
+ * `onObjectShown` hook (default: scroll to the top in the browser), the
532
+ * browser-only visit counter, `hasError`, `retryLoad`, `altTitle` and two
533
+ * opt-in resources — `related` and `relatedCollections`. The page keeps its
534
+ * template.
535
+ *
536
+ * ```ts
537
+ * // Muzeum Podlasia
538
+ * export class ItemComponent extends ObjectDetailsBaseComponent {
539
+ * constructor() {
540
+ * super({ related: true, relatedCollections: true });
541
+ * }
542
+ * }
543
+ * ```
544
+ */
545
+ declare abstract class ObjectDetailsBaseComponent extends ObjectsItemComponent implements OnInit {
546
+ protected readonly objectDetailsService: ObjectsDataService;
547
+ private readonly objectDetailsSeoService;
548
+ private readonly objectDetailsFileUrl;
549
+ private readonly objectDetailsDocument;
550
+ private readonly objectDetailsBrowser;
551
+ private readonly objectDetailsServer;
552
+ private readonly objectDetailsOptions;
553
+ /** Object id from the route (`/obiekty/:id` and `/obiekty/:id/:slug`). */
554
+ private readonly objectDetailsRouteId;
555
+ /**
556
+ * Id for „related” (MOB-1850). On the server from the route:
557
+ * `GET object/:id/related` starts together with `GET object/:id`, so SSR
558
+ * waits for the longer of the two requests, not their sum (card 5233:
559
+ * ~400 → ~290 ms locally). In the browser unchanged — after the card (no
560
+ * extra 404 for a missing object, „Try again” works as before). A
561
+ * non-numeric id waits for the card. `computed`, because `rxResource`
562
+ * does not compare the `params` result — every change of a read signal
563
+ * would repeat the request.
564
+ */
565
+ private readonly objectDetailsRelatedId;
566
+ /** The card failed — „related” started on the server has nothing to wait for. */
567
+ private readonly objectDetailsCardFailed$;
568
+ readonly hasError: Signal<any>;
569
+ /** The object title without markup — image `alt`s. `''` without an object. */
570
+ readonly altTitle: Signal<string>;
571
+ /**
572
+ * „You may also be interested in” — `GET object/:id/related`, only with
573
+ * the `related` option. A card error stops the request (the error page
574
+ * waits neither for „related” nor for its retry after a network error);
575
+ * the resource then ends in an error state, the section is gone anyway.
576
+ */
577
+ readonly related: ResourceRef<IObjectRelated | undefined>;
578
+ /**
579
+ * „Related collections” per the „Field description” document: thematic
580
+ * collections whose `search/Object` with `filter[entryCollections]`
581
+ * contains the object (the service gives an empty list also after an
582
+ * error — the section then disappears). Only with the
583
+ * `relatedCollections` option and only in the browser: the reverse lookup
584
+ * is one `search/Object` request per collection, and its responses (full
585
+ * object cards) must not reach SSR nor the transfer cache HTML — the
586
+ * server renders without this section, the client adds it after
587
+ * hydration.
588
+ */
589
+ readonly relatedCollections: ResourceRef<IObjectCollectionSummary[]>;
590
+ protected constructor(options?: IObjectDetailsOptions);
591
+ /**
592
+ * Visit counter — browser only (SSR would count the visit a second time),
593
+ * and its error (e.g. a 429 of the API limit) must neither reach the
594
+ * console as an unhandled `HttpErrorResponse` nor break the view. Once per
595
+ * page instance: in-page navigation to another object sends no new visit.
596
+ */
597
+ ngOnInit(): void;
598
+ retryLoad(): void;
599
+ /** SEO of the object: `<title> - <site>`, description, image and its alt. */
600
+ protected objectDetailsSeo(item: MsObject): ISeoMetadata;
601
+ /**
602
+ * Called (untracked) after the SEO for every object the facade selects.
603
+ * Default: scroll to the top in the browser. Override to keep the scroll
604
+ * on a re-read of the same card (MOB-1961) or to reset a gallery.
605
+ */
606
+ protected onObjectShown(item: MsObject): void;
607
+ private objectDetailsFetchSsrSeo;
608
+ static ɵfac: i0.ɵɵFactoryDeclaration<ObjectDetailsBaseComponent, never>;
609
+ static ɵdir: i0.ɵɵDirectiveDeclaration<ObjectDetailsBaseComponent, never, never, {}, {}, never, never, true, never>;
610
+ }
611
+
612
+ /**
613
+ * Generic readers of an object card from `GET object/:id` for a details page
614
+ * (MOB-1801), per the MobileMS „Opis pól” document (2026-10-05). A field
615
+ * without data gives an empty list — the template then hides the whole row
616
+ * with its label, nothing is made up. The layout groupings (header facts,
617
+ * metrics columns, hero crops) stay in each museum.
618
+ */
619
+ /** A field value: `name [role] (comment)`; `nameKey` — a translation instead of the name. */
620
+ type ObjectDetailValue = INamedEntryParts & {
621
+ nameKey?: string;
622
+ };
623
+ /** A related link (article, collection) of the details page. */
624
+ interface IObjectDetailLink {
625
+ id: number | string;
626
+ title: string;
627
+ route: string;
628
+ }
629
+ /** One bibliography item; `permalink` makes it a link. */
630
+ interface IObjectBibliographyEntry {
631
+ text: string;
632
+ permalink: string | null;
633
+ }
634
+ /**
635
+ * The card fields these tools read. Both `MsObject` and a museum's wider card
636
+ * type (e.g. Podlasie's `ObjectCard`) are assignable to it.
637
+ */
638
+ interface IObjectDetailSource {
639
+ title?: string | null;
640
+ name?: string | null;
641
+ noEvidence?: string | null;
642
+ seriesTitles?: ReadonlyArray<INamedEntrySource | null | undefined> | null;
643
+ bibliographyMNK?: string | null | ReadonlyArray<string | {
644
+ text?: string | null;
645
+ permalink?: string | null;
646
+ }>;
647
+ publicDescriptions?: ReadonlyArray<{
648
+ text?: string | null;
649
+ } | null> | null;
650
+ description?: string | null;
651
+ articles?: ReadonlyArray<{
652
+ id: number | string;
653
+ title?: string | null;
654
+ }> | null;
655
+ }
656
+ /** The object title — the document says `name`, the details API returns `title`. */
657
+ declare function objectTitle(obj: IObjectDetailSource): string;
658
+ /** Header tags: `seriesTitles` (the `Cycle` dictionary) → a list with the `cycles` filter. */
659
+ declare function tagValues(obj: IObjectDetailSource): ObjectDetailValue[];
660
+ /** Inventory number: `noEvidence`. */
661
+ declare function inventoryValues(obj: IObjectDetailSource): ObjectDetailValue[];
662
+ /**
663
+ * Bibliography as in OSSO (`museum-objects` item/main-tab): every
664
+ * `bibliographyMNK[]` entry is a separate item, and its `text` (HTML from the
665
+ * API) goes whole — OSSO does not split the text on full stops. An entry
666
+ * without text is dropped; `permalink` makes the item a link.
667
+ */
668
+ declare function bibliography(obj: IObjectDetailSource): IObjectBibliographyEntry[];
669
+ /**
670
+ * Description (HTML from the API): `publicDescriptions[].text` (the
671
+ * popular-science description), and without it `description` (the physical
672
+ * description) — the „Opis pól” rule of MOB-1563 („popular-science
673
+ * description, and if there is none — the physical one”); object 5233 has
674
+ * only `description` (MOB-1801). The description does not depend on media.
675
+ */
676
+ declare function descriptionHtml(obj: IObjectDetailSource): string[];
677
+ /**
678
+ * „Powiązane artykuły” — the card's `articles` field; the title without HTML
679
+ * (MOB-1845). `routePrefix` is the articles list route of the museum.
680
+ */
681
+ declare function relatedArticles(obj: IObjectDetailSource, routePrefix?: string): IObjectDetailLink[];
682
+
683
+ /**
684
+ * „Zapytaj o obiekt” keys (MOB-1869) — the mail address and text live in the
685
+ * CMS translations, so a museum changes them without a deployment.
686
+ */
687
+ declare const ASK_ABOUT_OBJECT_KEYS: {
688
+ readonly email: "DETAILS_PAGE.detail.askEmail";
689
+ readonly subject: "DETAILS_PAGE.detail.askSubject";
690
+ readonly body: "DETAILS_PAGE.detail.askBody";
691
+ readonly objectWithInventory: "DETAILS_PAGE.detail.askObjectWithInventory";
692
+ readonly portal: "MODEL.mainTitle";
693
+ };
694
+ /** `TranslateService.instant` — a key without a translation comes back as itself. */
695
+ type AskTranslate = (key: string, params?: Record<string, string>) => string;
696
+ interface IAskAboutObject {
697
+ /** The title without HTML. */
698
+ title: string;
699
+ inventory?: string | null;
700
+ /** The full address of the object card on the portal. */
701
+ url?: string | null;
702
+ }
703
+ /**
704
+ * The `mailto:` of the „Zapytaj o obiekt” button: the address from
705
+ * `askEmail`, the subject with the title (and the inventory number), the body
706
+ * with a greeting, the object, a link and room for the question. Without a
707
+ * valid address — `null` (no button); a missing subject or body is simply
708
+ * skipped. Body lines break as CRLF, as RFC 6068 requires.
709
+ */
710
+ declare function askAboutObjectHref(translate: AskTranslate, object: IAskAboutObject): string | null;
711
+
712
+ export { ASK_ABOUT_OBJECT_KEYS, AdvancedFilterType, ComponentsModule, FiltersComponent, FormFeatureType, ListComponent, MULTIMEDIA_DICTIONARY, OBJECTS_CATALOG_DEFAULTS, OBJECTS_CATALOG_SEO, ObjectDetailsBaseComponent, ObjectMediaService, ObjectsCatalogBaseComponent, ObjectsDataService, ObjectsFacade, ObjectsItemComponent, ObjectsListComponent, ObjectsModule, ObjectsService, ObjectsSharedModule, SERVICES, askAboutObjectHref, bibliography, descriptionHtml, getObjectsState, getReducer, initialState, inventoryValues, objectTitle, relatedArticles, tagValues };
713
+ export type { ArchaeologicalSegregation, AskTranslate, FileType, IAnimationObject, IAskAboutObject, IKeyValue, IModel3DObject, IMultimediaDictionaryItem, IObjectBibliographyEntry, IObjectCollectionSummary, IObjectDetailLink, IObjectDetailSource, IObjectDetailsOptions, IObjectGalleryItem, IObjectRelated, IObjectsCatalogLoadMore, IObjectsCatalogOptions, IObjectsCatalogSeo, ISpecificObject, ObjectDetailValue, SortType, State };