PageSourceSearch

https://stridenavigation.com/chunk-HDZGJ576.js

js stridenavigation.com collected 2026-09-24 14:53:36 UTC 33,965 bytes, 724 lines download raw bytes

1import {
2  SyncService
3} from "./chunk-FX3P6WNJ.js";
4import "./chunk-OTQ6GTAJ.js";
5import {
6  IonBadge,
7  IonButtons,
8  IonCard,
9  IonCardContent,
10  IonCardHeader,
11  IonContent,
12  IonFab,
13  IonFabButton,
14  IonHeader,
15  IonMenuButton,
16  IonRefresher,
17  IonRefresherContent,
18  IonSpinner,
19  IonTitle,
20  IonToolbar
21} from "./chunk-RLZ522BW.js";
22import {
23  DatabaseService,
24  EntityType
25} from "./chunk-RLJCY5HS.js";
26import "./chunk-GR5DEE5Y.js";
27import {
28  TranslateModule,
29  TranslatePipe
30} from "./chunk-O5XYYQCR.js";
31import {
32  AuthService,
33  UserRole
34} from "./chunk-QGEJYSW7.js";
35import {
36  ApiUrlService
37} from "./chunk-MAIAEXGG.js";
38import "./chunk-L4O6SE3A.js";
39import "./chunk-INK5THDW.js";
40import {
41  NgIcon,
42  lucideCalendar,
43  lucideChevronRight,
44  lucideNewspaper,
45  lucidePlus,
46  lucideSearchX,
47  lucideStar,
48  lucideUser,
49  provideIcons
50} from "./chunk-GMPIA5L4.js";
51import {
52  CommonModule,
53  Component,
54  DatePipe,
55  EventEmitter,
56  Input,
57  Output,
58  Router,
59  setClassMetadata,
60  ɵsetClassDebugInfo,
61  ɵɵProvidersFeature,
62  ɵɵadvance,
63  ɵɵconditional,
64  ɵɵdefineComponent,
65  ɵɵdirectiveInject,
66  ɵɵelement,
67  ɵɵelementEnd,
68  ɵɵelementStart,
69  ɵɵgetCurrentView,
70  ɵɵlistener,
71  ɵɵnextContext,
72  ɵɵpipe,
73  ɵɵpipeBind1,
74  ɵɵpipeBind2,
75  ɵɵproperty,
76  ɵɵrepeater,
77  ɵɵrepeaterCreate,
78  ɵɵresetView,
79  ɵɵrestoreView,
80  ɵɵsanitizeUrl,
81  ɵɵtemplate,
82  ɵɵtext,
83  ɵɵtextInterpolate,
84  ɵɵtextInterpolate1
85} from "./chunk-UQ6LLOJ6.js";
86import "./chunk-BD3NMR2S.js";
87import "./chunk-6HPUB4U6.js";
88import "./chunk-WGFJL45C.js";
89import "./chunk-7WVISTDL.js";
90import "./chunk-2S57T3GS.js";
91import "./chunk-R4QHACMO.js";
92import "./chunk-4MEJEBWM.js";
93import "./chunk-RIBVNXHQ.js";
94import "./chunk-Z42OVMMO.js";
95import "./chunk-O5S42IXR.js";
96import "./chunk-QBR2RTFG.js";
97import "./chunk-MIOLVGSM.js";
98import "./chunk-V3YFOG6Q.js";
99import "./chunk-LPMBLWVS.js";
100import "./chunk-ITE2OHLM.js";
101import "./chunk-C46CB72A.js";
102import "./chunk-SRPSML5O.js";
103import "./chunk-NRSVX4KN.js";
104import "./chunk-T4GYAOTA.js";
105import "./chunk-2VHYZHLL.js";
106import "./chunk-S6LHOZ5A.js";
107import "./chunk-Y6HZOH3Q.js";
108import "./chunk-TWQHADEW.js";
109import "./chunk-3J4L7S6G.js";
110import "./chunk-IL42VKT6.js";
111import "./chunk-LONO5O5R.js";
112import "./chunk-KFBF5L4X.js";
113import "./chunk-PWC4K6CW.js";
114import "./chunk-R6JLIESI.js";
115import "./chunk-UG6K57PI.js";
116import "./chunk-UR6VIRNL.js";
117import "./chunk-OJO5RD3M.js";
118import {
119  __async
120} from "./chunk-CO64IJQE.js";
121
122// src/app/components/news-card/news-card.component.ts
123function NewsCardComponent_Conditional_0_Conditional_1_Template(rf, ctx) {
124  if (rf & 1) {
125    \u0275\u0275elementStart(0, "div", 3);
126    \u0275\u0275element(1, "img", 10);
127    \u0275\u0275elementEnd();
128  }
129  if (rf & 2) {
130    const ctx_r1 = \u0275\u0275nextContext(2);
131    \u0275\u0275advance();
132    \u0275\u0275property("src", ctx_r1.imageUrl, \u0275\u0275sanitizeUrl)("alt", ctx_r1.news.entity.title);
133  }
134}
135function NewsCardComponent_Conditional_0_Conditional_3_Template(rf, ctx) {
136  if (rf & 1) {
137    \u0275\u0275elementStart(0, "ion-badge", 4);
138    \u0275\u0275element(1, "ng-icon", 11);
139    \u0275\u0275text(2);
140    \u0275\u0275pipe(3, "translate");
141    \u0275\u0275elementEnd();
142  }
143  if (rf & 2) {
144    \u0275\u0275advance(2);
145    \u0275\u0275textInterpolate1(" ", \u0275\u0275pipeBind1(3, 1, "news.featured"), " ");
146  }
147}
148function NewsCardComponent_Conditional_0_Conditional_14_Template(rf, ctx) {
149  if (rf & 1) {
150    \u0275\u0275elementStart(0, "span", 8);
151    \u0275\u0275element(1, "ng-icon", 12);
152    \u0275\u0275text(2);
153    \u0275\u0275elementEnd();
154  }
155  if (rf & 2) {
156    const ctx_r1 = \u0275\u0275nextContext(2);
157    \u0275\u0275advance(2);
158    \u0275\u0275textInterpolate1(" ", ctx_r1.authorName, " ");
159  }
160}
161function NewsCardComponent_Conditional_0_Template(rf, ctx) {
162  if (rf & 1) {
163    const _r1 = \u0275\u0275getCurrentView();
164    \u0275\u0275elementStart(0, "ion-card", 2);
165    \u0275\u0275listener("click", function NewsCardComponent_Conditional_0_Template_ion_card_click_0_listener() {
166      \u0275\u0275restoreView(_r1);
167      const ctx_r1 = \u0275\u0275nextContext();
168      return \u0275\u0275resetView(ctx_r1.onClick());
169    });
170    \u0275\u0275template(1, NewsCardComponent_Conditional_0_Conditional_1_Template, 2, 2, "div", 3);
171    \u0275\u0275elementStart(2, "ion-card-header");
172    \u0275\u0275template(3, NewsCardComponent_Conditional_0_Conditional_3_Template, 4, 3, "ion-badge", 4);
173    \u0275\u0275elementStart(4, "h2", 5);
174    \u0275\u0275text(5);
175    \u0275\u0275elementEnd()();
176    \u0275\u0275elementStart(6, "ion-card-content")(7, "p", 6);
177    \u0275\u0275text(8);
178    \u0275\u0275elementEnd();
179    \u0275\u0275elementStart(9, "div", 7)(10, "span", 8);
180    \u0275\u0275element(11, "ng-icon", 9);
181    \u0275\u0275text(12);
182    \u0275\u0275pipe(13, "date");
183    \u0275\u0275elementEnd();
184    \u0275\u0275template(14, NewsCardComponent_Conditional_0_Conditional_14_Template, 3, 1, "span", 8);
185    \u0275\u0275elementEnd()()();
186  }
187  if (rf & 2) {
188    const ctx_r1 = \u0275\u0275nextContext();
189    \u0275\u0275advance();
190    \u0275\u0275conditional(ctx_r1.imageUrl ? 1 : -1);
191    \u0275\u0275advance(2);
192    \u0275\u0275conditional(ctx_r1.news.entity.isFeatured ? 3 : -1);
193    \u0275\u0275advance(2);
194    \u0275\u0275textInterpolate(ctx_r1.news.entity.title);
195    \u0275\u0275advance(3);
196    \u0275\u0275textInterpolate(ctx_r1.excerpt);
197    \u0275\u0275advance(4);
198    \u0275\u0275textInterpolate1(" ", \u0275\u0275pipeBind2(13, 6, ctx_r1.publishedDate, "mediumDate"), " ");
199    \u0275\u0275advance(2);
200    \u0275\u0275conditional(ctx_r1.authorName ? 14 : -1);
201  }
202}
203function NewsCardComponent_Conditional_1_Conditional_2_Template(rf, ctx) {
204  if (rf & 1) {
205    \u0275\u0275elementStart(0, "div", 15);
206    \u0275\u0275element(1, "img", 10);
207    \u0275\u0275elementEnd();
208  }
209  if (rf & 2) {
210    const ctx_r1 = \u0275\u0275nextContext(2);
211    \u0275\u0275advance();
212    \u0275\u0275property("src", ctx_r1.imageUrl, \u0275\u0275sanitizeUrl)("alt", ctx_r1.news.entity.title);
213  }
214}
215function NewsCardComponent_Conditional_1_Conditional_3_Template(rf, ctx) {
216  if (rf & 1) {
217    \u0275\u0275elementStart(0, "div", 16);
218    \u0275\u0275element(1, "ng-icon", 22);
219    \u0275\u0275elementEnd();
220  }
221}
222function NewsCardComponent_Conditional_1_Conditional_5_Template(rf, ctx) {
223  if (rf & 1) {
224    \u0275\u0275elementStart(0, "ion-badge", 18);
225    \u0275\u0275text(1);
226    \u0275\u0275pipe(2, "translate");
227    \u0275\u0275elementEnd();
228  }
229  if (rf & 2) {
230    \u0275\u0275advance();
231    \u0275\u0275textInterpolate1(" ", \u0275\u0275pipeBind1(2, 1, "news.featured"), " ");
232  }
233}
234function NewsCardComponent_Conditional_1_Template(rf, ctx) {
235  if (rf & 1) {
236    const _r3 = \u0275\u0275getCurrentView();
237    \u0275\u0275elementStart(0, "ion-card", 13);
238    \u0275\u0275listener("click", function NewsCardComponent_Conditional_1_Template_ion_card_click_0_listener() {
239      \u0275\u0275restoreView(_r3);
240      const ctx_r1 = \u0275\u0275nextContext();
241      return \u0275\u0275resetView(ctx_r1.onClick());
242    });
243    \u0275\u0275elementStart(1, "div", 14);
244    \u0275\u0275template(2, NewsCardComponent_Conditional_1_Conditional_2_Template, 2, 2, "div", 15)(3, NewsCardComponent_Conditional_1_Conditional_3_Template, 2, 0, "div", 16);
245    \u0275\u0275elementStart(4, "div", 17);
246    \u0275\u0275template(5, NewsCardComponent_Conditional_1_Conditional_5_Template, 3, 3, "ion-badge", 18);
247    \u0275\u0275elementStart(6, "h3", 5);
248    \u0275\u0275text(7);
249    \u0275\u0275elementEnd();
250    \u0275\u0275elementStart(8, "p", 6);
251    \u0275\u0275text(9);
252    \u0275\u0275elementEnd();
253    \u0275\u0275elementStart(10, "div", 7)(11, "span", 8);
254    \u0275\u0275element(12, "ng-icon", 19);
255    \u0275\u0275text(13);
256    \u0275\u0275pipe(14, "date");
257    \u0275\u0275elementEnd()()();
258    \u0275\u0275elementStart(15, "div", 20);
259    \u0275\u0275element(16, "ng-icon", 21);
260    \u0275\u0275elementEnd()()();
261  }
262  if (rf & 2) {
263    const ctx_r1 = \u0275\u0275nextContext();
264    \u0275\u0275advance(2);
265    \u0275\u0275conditional(ctx_r1.imageUrl ? 2 : 3);
266    \u0275\u0275advance(3);
267    \u0275\u0275conditional(ctx_r1.news.entity.isFeatured ? 5 : -1);
268    \u0275\u0275advance(2);
269    \u0275\u0275textInterpolate(ctx_r1.news.entity.title);
270    \u0275\u0275advance(2);
271    \u0275\u0275textInterpolate(ctx_r1.excerpt);
272    \u0275\u0275advance(4);
273    \u0275\u0275textInterpolate1(" ", \u0275\u0275pipeBind2(14, 5, ctx_r1.publishedDate, "mediumDate"), " ");
274  }
275}
276var _NewsCardComponent = class _NewsCardComponent {
277  constructor(apiUrlService) {
278    this.apiUrlService = apiUrlService;
279    this.variant = "compact";
280    this.cardClick = new EventEmitter();
281  }
282  get imageUrl() {
283    if (!this.news.entity.imageUploadId)
284      return null;
285    return `${this.apiUrlService.getSelectedUrl()}/api/v1/media/${this.news.entity.imageUploadId}`;
286  }
287  get authorName() {
288    var _a;
289    return ((_a = this.news.author) == null ? void 0 : _a.entity.name) || null;
290  }
291  get publishedDate() {
292    if (!this.news.entity.publishedAt)
293      return null;
294    return new Date(this.news.entity.publishedAt);
295  }
296  get excerpt() {
297    const content = this.news.entity.content || "";
298    const maxLength = this.variant === "featured" ? 150 : 80;
299    if (content.length <= maxLength)
300      return content;
301    return content.substring(0, maxLength).trim() + "...";
302  }
303  onClick() {
304    this.cardClick.emit(this.news);
305  }
306};
307_NewsCardComponent.\u0275fac = function NewsCardComponent_Factory(__ngFactoryType__) {
308  return new (__ngFactoryType__ || _NewsCardComponent)(\u0275\u0275directiveInject(ApiUrlService));
309};
310_NewsCardComponent.\u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _NewsCardComponent, selectors: [["app-news-card"]], inputs: { news: "news", variant: "variant" }, outputs: { cardClick: "cardClick" }, features: [\u0275\u0275ProvidersFeature([], [provideIcons({ lucideCalendar, lucideUser, lucideChevronRight, lucideStar })])], decls: 2, vars: 1, consts: [[1, "news-card", "featured"], [1, "news-card", "compact"], [1, "news-card", "featured", 3, "click"], [1, "card-image", "featured-image"], ["color", "warning", 1, "featured-badge"], [1, "card-title"], [1, "card-excerpt"], [1, "card-meta"], [1, "meta-item"], ["name", "lucideCalendar", "size", "1em"], [3, "src", "alt"], ["name", "lucideStar", "size", "0.9em"], ["name", "lucideUser", "size", "1em"], [1, "news-card", "compact", 3, "click"], [1, "compact-layout"], [1, "card-image", "compact-image"], [1, "card-image", "compact-image", "placeholder"], [1, "card-content"], ["color", "warning", 1, "featured-badge", "small"], ["name", "lucideCalendar", "size", "0.9em"], [1, "card-arrow"], ["name", "lucideChevronRight", "size", "1.2em"], ["name", "lucideStar", "size", "1.5em"]], template: function NewsCardComponent_Template(rf, ctx) {
311  if (rf & 1) {
312    \u0275\u0275template(0, NewsCardComponent_Conditional_0_Template, 15, 9, "ion-card", 0)(1, NewsCardComponent_Conditional_1_Template, 17, 8, "ion-card", 1);
313  }
314  if (rf & 2) {
315    \u0275\u0275conditional(ctx.variant === "featured" ? 0 : 1);
316  }
317}, dependencies: [
318  CommonModule,
319  DatePipe,
320  TranslateModule,
321  TranslatePipe,
322  NgIcon,
323  IonCard,
324  IonCardHeader,
325  IonCardContent,
326  IonBadge
327], styles: ["\n\n.news-card[_ngcontent-%COMP%] {\n  cursor: pointer;\n  margin: 0 0 12px 0;\n  border-radius: 12px;\n  overflow: hidden;\n  transition: transform 0.2s ease, box-shadow 0.2s ease;\n}\n.news-card[_ngcontent-%COMP%]:hover {\n  transform: translateY(-2px);\n  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n.news-card[_ngcontent-%COMP%]:active {\n  transform: translateY(0);\n}\n.news-card.featured[_ngcontent-%COMP%]   .featured-image[_ngcontent-%COMP%] {\n  width: 100%;\n  height: 180px;\n  overflow: hidden;\n}\n.news-card.featured[_ngcontent-%COMP%]   .featured-image[_ngcontent-%COMP%]   img[_ngcontent-%COMP%] {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n}\n.news-card.featured[_ngcontent-%COMP%]   ion-card-header[_ngcontent-%COMP%] {\n  padding: 12px 16px 8px;\n}\n.news-card.featured[_ngcontent-%COMP%]   ion-card-content[_ngcontent-%COMP%] {\n  padding: 0 16px 16px;\n}\n.news-card.featured[_ngcontent-%COMP%]   .card-title[_ngcontent-%COMP%] {\n  font-size: 1.25rem;\n  font-weight: 600;\n  margin: 8px 0;\n  line-height: 1.3;\n  color: var(--ion-text-color);\n}\n.news-card.featured[_ngcontent-%COMP%]   .card-excerpt[_ngcontent-%COMP%] {\n  font-size: 0.95rem;\n  color: var(--ion-color-medium);\n  margin: 0 0 12px;\n  line-height: 1.5;\n}\n.news-card.compact[_ngcontent-%COMP%]   .compact-layout[_ngcontent-%COMP%] {\n  display: flex;\n  align-items: stretch;\n  padding: 12px;\n  gap: 12px;\n}\n.news-card.compact[_ngcontent-%COMP%]   .compact-image[_ngcontent-%COMP%] {\n  flex-shrink: 0;\n  width: 70px;\n  height: 70px;\n  border-radius: 8px;\n  overflow: hidden;\n  background: var(--ion-color-light);\n}\n.news-card.compact[_ngcontent-%COMP%]   .compact-image[_ngcontent-%COMP%]   img[_ngcontent-%COMP%] {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n}\n.news-card.compact[_ngcontent-%COMP%]   .compact-image.placeholder[_ngcontent-%COMP%] {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: var(--ion-color-medium);\n}\n.news-card.compact[_ngcontent-%COMP%]   .card-content[_ngcontent-%COMP%] {\n  flex: 1;\n  min-width: 0;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n}\n.news-card.compact[_ngcontent-%COMP%]   .card-title[_ngcontent-%COMP%] {\n  font-size: 1rem;\n  font-weight: 600;\n  margin: 0 0 4px;\n  line-height: 1.3;\n  color: var(--ion-text-color);\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n.news-card.compact[_ngcontent-%COMP%]   .card-excerpt[_ngcontent-%COMP%] {\n  font-size: 0.85rem;\n  color: var(--ion-color-medium);\n  margin: 0 0 6px;\n  line-height: 1.4;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n.news-card.compact[_ngcontent-%COMP%]   .card-arrow[_ngcontent-%COMP%] {\n  flex-shrink: 0;\n  display: flex;\n  align-items: center;\n  color: var(--ion-color-medium);\n}\n.featured-badge[_ngcontent-%COMP%] {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n  font-size: 0.75rem;\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n}\n.featured-badge.small[_ngcontent-%COMP%] {\n  font-size: 0.65rem;\n  padding: 2px 6px;\n  margin-bottom: 4px;\n}\n.card-meta[_ngcontent-%COMP%] {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  font-size: 0.8rem;\n  color: var(--ion-color-medium);\n}\n.card-meta[_ngcontent-%COMP%]   .meta-item[_ngcontent-%COMP%] {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n}"] });
328var NewsCardComponent = _NewsCardComponent;
329(() => {
330  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NewsCardComponent, [{
331    type: Component,
332    args: [{ selector: "app-news-card", standalone: true, imports: [
333      CommonModule,
334      TranslateModule,
335      NgIcon,
336      IonCard,
337      IonCardHeader,
338      IonCardContent,
339      IonBadge
340    ], viewProviders: [provideIcons({ lucideCalendar, lucideUser, lucideChevronRight, lucideStar })], template: `@if (variant === 'featured') {
341  <ion-card class="news-card featured" (click)="onClick()">
342    @if (imageUrl) {
343      <div class="card-image featured-image">
344        <img [src]="imageUrl" [alt]="news.entity.title" />
345      </div>
346    }
347    <ion-card-header>
348      @if (news.entity.isFeatured) {
349        <ion-badge color="warning" class="featured-badge">
350          <ng-icon name="lucideStar" size="0.9em"></ng-icon>
351          {{ 'news.featured' | translate }}
352        </ion-badge>
353      }
354      <h2 class="card-title">{{ news.entity.title }}</h2>
355    </ion-card-header>
356    <ion-card-content>
357      <p class="card-excerpt">{{ excerpt }}</p>
358      <div class="card-meta">
359        <span class="meta-item">
360          <ng-icon name="lucideCalendar" size="1em"></ng-icon>
361          {{ publishedDate | date:'mediumDate' }}
362        </span>
363        @if (authorName) {
364          <span class="meta-item">
365            <ng-icon name="lucideUser" size="1em"></ng-icon>
366            {{ authorName }}
367          </span>
368        }
369      </div>
370    </ion-card-content>
371  </ion-card>
372} @else {
373  <ion-card class="news-card compact" (click)="onClick()">
374    <div class="compact-layout">
375      @if (imageUrl) {
376        <div class="card-image compact-image">
377          <img [src]="imageUrl" [alt]="news.entity.title" />
378        </div>
379      } @else {
380        <div class="card-image compact-image placeholder">
381          <ng-icon name="lucideStar" size="1.5em"></ng-icon>
382        </div>
383      }
384      <div class="card-content">
385        @if (news.entity.isFeatured) {
386          <ion-badge color="warning" class="featured-badge small">
387            {{ 'news.featured' | translate }}
388          </ion-badge>
389        }
390        <h3 class="card-title">{{ news.entity.title }}</h3>
391        <p class="card-excerpt">{{ excerpt }}</p>
392        <div class="card-meta">
393          <span class="meta-item">
394            <ng-icon name="lucideCalendar" size="0.9em"></ng-icon>
395            {{ publishedDate | date:'mediumDate' }}
396          </span>
397        </div>
398      </div>
399      <div class="card-arrow">
400        <ng-icon name="lucideChevronRight" size="1.2em"></ng-icon>
401      </div>
402    </div>
403  </ion-card>
404}
405`, styles: ["/* src/app/components/news-card/news-card.component.scss */\n.news-card {\n  cursor: pointer;\n  margin: 0 0 12px 0;\n  border-radius: 12px;\n  overflow: hidden;\n  transition: transform 0.2s ease, box-shadow 0.2s ease;\n}\n.news-card:hover {\n  transform: translateY(-2px);\n  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n.news-card:active {\n  transform: translateY(0);\n}\n.news-card.featured .featured-image {\n  width: 100%;\n  height: 180px;\n  overflow: hidden;\n}\n.news-card.featured .featured-image img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n}\n.news-card.featured ion-card-header {\n  padding: 12px 16px 8px;\n}\n.news-card.featured ion-card-content {\n  padding: 0 16px 16px;\n}\n.news-card.featured .card-title {\n  font-size: 1.25rem;\n  font-weight: 600;\n  margin: 8px 0;\n  line-height: 1.3;\n  color: var(--ion-text-color);\n}\n.news-card.featured .card-excerpt {\n  font-size: 0.95rem;\n  color: var(--ion-color-medium);\n  margin: 0 0 12px;\n  line-height: 1.5;\n}\n.news-card.compact .compact-layout {\n  display: flex;\n  align-items: stretch;\n  padding: 12px;\n  gap: 12px;\n}\n.news-card.compact .compact-image {\n  flex-shrink: 0;\n  width: 70px;\n  height: 70px;\n  border-radius: 8px;\n  overflow: hidden;\n  background: var(--ion-color-light);\n}\n.news-card.compact .compact-image img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n}\n.news-card.compact .compact-image.placeholder {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: var(--ion-color-medium);\n}\n.news-card.compact .card-content {\n  flex: 1;\n  min-width: 0;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n}\n.news-card.compact .card-title {\n  font-size: 1rem;\n  font-weight: 600;\n  margin: 0 0 4px;\n  line-height: 1.3;\n  color: var(--ion-text-color);\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n.news-card.compact .card-excerpt {\n  font-size: 0.85rem;\n  color: var(--ion-color-medium);\n  margin: 0 0 6px;\n  line-height: 1.4;\n  display: -webkit-box;\n  -webkit-line-clamp: 2;\n  -webkit-box-orient: vertical;\n  overflow: hidden;\n}\n.news-card.compact .card-arrow {\n  flex-shrink: 0;\n  display: flex;\n  align-items: center;\n  color: var(--ion-color-medium);\n}\n.featured-badge {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n  font-size: 0.75rem;\n  text-transform: uppercase;\n  letter-spacing: 0.5px;\n}\n.featured-badge.small {\n  font-size: 0.65rem;\n  padding: 2px 6px;\n  margin-bottom: 4px;\n}\n.card-meta {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n  font-size: 0.8rem;\n  color: var(--ion-color-medium);\n}\n.card-meta .meta-item {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n}\n"] }]
406  }], () => [{ type: ApiUrlService }], { news: [{
407    type: Input
408  }], variant: [{
409    type: Input
410  }], cardClick: [{
411    type: Output
412  }] });
413})();
414(() => {
415  (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(NewsCardComponent, { className: "NewsCardComponent", filePath: "src/app/components/news-card/news-card.component.ts", lineNumber: 27 });
416})();
417
418// src/app/pages/news/news.page.ts
419var _forTrack0 = ($index, $item) => $item.entity.id;
420function NewsPage_Conditional_10_Template(rf, ctx) {
421  if (rf & 1) {
422    \u0275\u0275elementStart(0, "div", 2);
423    \u0275\u0275element(1, "ion-spinner", 6);
424    \u0275\u0275elementStart(2, "p");
425    \u0275\u0275text(3);
426    \u0275\u0275pipe(4, "translate");
427    \u0275\u0275elementEnd()();
428  }
429  if (rf & 2) {
430    \u0275\u0275advance(3);
431    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(4, 1, "news.loading"));
432  }
433}
434function NewsPage_Conditional_11_Template(rf, ctx) {
435  if (rf & 1) {
436    \u0275\u0275elementStart(0, "div", 3);
437    \u0275\u0275element(1, "ng-icon", 7);
438    \u0275\u0275elementStart(2, "h3");
439    \u0275\u0275text(3);
440    \u0275\u0275pipe(4, "translate");
441    \u0275\u0275elementEnd();
442    \u0275\u0275elementStart(5, "p");
443    \u0275\u0275text(6);
444    \u0275\u0275pipe(7, "translate");
445    \u0275\u0275elementEnd()();
446  }
447  if (rf & 2) {
448    \u0275\u0275advance(3);
449    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(4, 2, "news.empty"));
450    \u0275\u0275advance(3);
451    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(7, 4, "news.emptyDescription"));
452  }
453}
454function NewsPage_Conditional_12_For_2_Template(rf, ctx) {
455  if (rf & 1) {
456    const _r1 = \u0275\u0275getCurrentView();
457    \u0275\u0275elementStart(0, "app-news-card", 10);
458    \u0275\u0275listener("cardClick", function NewsPage_Conditional_12_For_2_Template_app_news_card_cardClick_0_listener($event) {
459      \u0275\u0275restoreView(_r1);
460      const ctx_r1 = \u0275\u0275nextContext(2);
461      return \u0275\u0275resetView(ctx_r1.onNewsClick($event));
462    });
463    \u0275\u0275elementEnd();
464  }
465  if (rf & 2) {
466    const news_r3 = ctx.$implicit;
467    \u0275\u0275property("news", news_r3);
468  }
469}
470function NewsPage_Conditional_12_For_4_Template(rf, ctx) {
471  if (rf & 1) {
472    const _r4 = \u0275\u0275getCurrentView();
473    \u0275\u0275elementStart(0, "app-news-card", 11);
474    \u0275\u0275listener("cardClick", function NewsPage_Conditional_12_For_4_Template_app_news_card_cardClick_0_listener($event) {
475      \u0275\u0275restoreView(_r4);
476      const ctx_r1 = \u0275\u0275nextContext(2);
477      return \u0275\u0275resetView(ctx_r1.onNewsClick($event));
478    });
479    \u0275\u0275elementEnd();
480  }
481  if (rf & 2) {
482    const news_r5 = ctx.$implicit;
483    \u0275\u0275property("news", news_r5);
484  }
485}
486function NewsPage_Conditional_12_Template(rf, ctx) {
487  if (rf & 1) {
488    \u0275\u0275elementStart(0, "div", 4);
489    \u0275\u0275repeaterCreate(1, NewsPage_Conditional_12_For_2_Template, 1, 1, "app-news-card", 8, _forTrack0);
490    \u0275\u0275repeaterCreate(3, NewsPage_Conditional_12_For_4_Template, 1, 1, "app-news-card", 9, _forTrack0);
491    \u0275\u0275elementEnd();
492  }
493  if (rf & 2) {
494    const ctx_r1 = \u0275\u0275nextContext();
495    \u0275\u0275advance();
496    \u0275\u0275repeater(ctx_r1.featuredNews);
497    \u0275\u0275advance(2);
498    \u0275\u0275repeater(ctx_r1.regularNews);
499  }
500}
501function NewsPage_Conditional_13_Template(rf, ctx) {
502  if (rf & 1) {
503    const _r6 = \u0275\u0275getCurrentView();
504    \u0275\u0275elementStart(0, "ion-fab", 5)(1, "ion-fab-button", 12);
505    \u0275\u0275listener("click", function NewsPage_Conditional_13_Template_ion_fab_button_click_1_listener() {
506      \u0275\u0275restoreView(_r6);
507      const ctx_r1 = \u0275\u0275nextContext();
508      return \u0275\u0275resetView(ctx_r1.onCreateNews());
509    });
510    \u0275\u0275element(2, "ng-icon", 13);
511    \u0275\u0275elementEnd()();
512  }
513}
514var _NewsPage = class _NewsPage {
515  constructor(databaseService, authService, syncService, router) {
516    this.databaseService = databaseService;
517    this.authService = authService;
518    this.syncService = syncService;
519    this.router = router;
520    this.loading = true;
521    this.allNews = [];
522  }
523  ngOnInit() {
524    this.loadNews();
525  }
526  get canCreateNews() {
527    return this.authService.hasRole(UserRole.Moderator);
528  }
529  get userCampusIds() {
530    var _a;
531    const campusIds = [];
532    for (const [, model] of this.databaseService.Models) {
533      if (model.type === EntityType.PERSON_CAMPUS) {
534        const pc = model.entity;
535        if (pc.personId === ((_a = this.authService.currentUserValue) == null ? void 0 : _a.id) && pc.campusId) {
536          campusIds.push(pc.campusId);
537        }
538      }
539    }
540    if (this.authService.hasRole(UserRole.Admin)) {
541      for (const [, model] of this.databaseService.Models) {
542        if (model.type === EntityType.CAMPUS && !model.entity.isDeleted) {
543          if (!campusIds.includes(model.entity.id)) {
544            campusIds.push(model.entity.id);
545          }
546        }
547      }
548    }
549    return campusIds;
550  }
551  get sortedNews() {
552    return this.allNews.filter((n) => !n.entity.isDeleted).filter((n) => this.userCampusIds.includes(n.entity.campusId)).sort((a, b) => {
553      if (a.entity.isFeatured && !b.entity.isFeatured)
554        return -1;
555      if (!a.entity.isFeatured && b.entity.isFeatured)
556        return 1;
557      const dateA = new Date(a.entity.publishedAt || 0).getTime();
558      const dateB = new Date(b.entity.publishedAt || 0).getTime();
559      return dateB - dateA;
560    });
561  }
562  get featuredNews() {
563    return this.sortedNews.filter((n) => n.entity.isFeatured);
564  }
565  get regularNews() {
566    return this.sortedNews.filter((n) => !n.entity.isFeatured);
567  }
568  loadNews() {
569    this.allNews = [];
570    for (const [, model] of this.databaseService.Models) {
571      if (model.type === EntityType.NEWS) {
572        this.allNews.push(model);
573      }
574    }
575    this.loading = false;
576  }
577  doRefresh(event) {
578    return __async(this, null, function* () {
579      try {
580        yield this.syncService.pullData();
581        this.loadNews();
582      } finally {
583        event.target.complete();
584      }
585    });
586  }
587  onNewsClick(news) {
588    this.router.navigate(["/news", news.entity.id]);
589  }
590  onCreateNews() {
591    this.router.navigate(["/news/new"]);
592  }
593};
594_NewsPage.\u0275fac = function NewsPage_Factory(__ngFactoryType__) {
595  return new (__ngFactoryType__ || _NewsPage)(\u0275\u0275directiveInject(DatabaseService), \u0275\u0275directiveInject(AuthService), \u0275\u0275directiveInject(SyncService), \u0275\u0275directiveInject(Router));
596};
597_NewsPage.\u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _NewsPage, selectors: [["app-news"]], features: [\u0275\u0275ProvidersFeature([], [provideIcons({ lucideNewspaper, lucidePlus, lucideSearchX })])], decls: 14, vars: 5, consts: [["slot", "start"], ["slot", "fixed", 3, "ionRefresh"], [1, "loading-container"], [1, "empty-state"], [1, "news-list"], ["vertical", "bottom", "horizontal", "end", "slot", "fixed"], ["name", "crescent"], ["name", "lucideNewspaper", "size", "3em"], ["variant", "featured", 3, "news"], ["variant", "compact", 3, "news"], ["variant", "featured", 3, "cardClick", "news"], ["variant", "compact", 3, "cardClick", "news"], [3, "click"], ["name", "lucidePlus", "size", "1.5em"]], template: function NewsPage_Template(rf, ctx) {
598  if (rf & 1) {
599    \u0275\u0275elementStart(0, "ion-header")(1, "ion-toolbar")(2, "ion-buttons", 0);
600    \u0275\u0275element(3, "ion-menu-button");
601    \u0275\u0275elementEnd();
602    \u0275\u0275elementStart(4, "ion-title");
603    \u0275\u0275text(5);
604    \u0275\u0275pipe(6, "translate");
605    \u0275\u0275elementEnd()()();
606    \u0275\u0275elementStart(7, "ion-content")(8, "ion-refresher", 1);
607    \u0275\u0275listener("ionRefresh", function NewsPage_Template_ion_refresher_ionRefresh_8_listener($event) {
608      return ctx.doRefresh($event);
609    });
610    \u0275\u0275element(9, "ion-refresher-content");
611    \u0275\u0275elementEnd();
612    \u0275\u0275template(10, NewsPage_Conditional_10_Template, 5, 3, "div", 2)(11, NewsPage_Conditional_11_Template, 8, 6, "div", 3)(12, NewsPage_Conditional_12_Template, 5, 0, "div", 4)(13, NewsPage_Conditional_13_Template, 3, 0, "ion-fab", 5);
613    \u0275\u0275elementEnd();
614  }
615  if (rf & 2) {
616    \u0275\u0275advance(5);
617    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(6, 3, "news.title"));
618    \u0275\u0275advance(5);
619    \u0275\u0275conditional(ctx.loading ? 10 : ctx.sortedNews.length === 0 ? 11 : 12);
620    \u0275\u0275advance(3);
621    \u0275\u0275conditional(ctx.canCreateNews ? 13 : -1);
622  }
623}, dependencies: [
624  CommonModule,
625  TranslateModule,
626  TranslatePipe,
627  NgIcon,
628  NewsCardComponent,
629  IonHeader,
630  IonToolbar,
631  IonButtons,
632  IonMenuButton,
633  IonTitle,
634  IonContent,
635  IonFab,
636  IonFabButton,
637  IonRefresher,
638  IonRefresherContent,
639  IonSpinner
640], styles: ["\n\n.loading-container[_ngcontent-%COMP%] {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  height: 50vh;\n  gap: 16px;\n  color: var(--ion-color-medium);\n}\n.loading-container[_ngcontent-%COMP%]   p[_ngcontent-%COMP%] {\n  margin: 0;\n  font-size: 0.95rem;\n}\n.empty-state[_ngcontent-%COMP%] {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  height: 50vh;\n  padding: 24px;\n  text-align: center;\n  color: var(--ion-color-medium);\n}\n.empty-state[_ngcontent-%COMP%]   ng-icon[_ngcontent-%COMP%] {\n  margin-bottom: 16px;\n  opacity: 0.6;\n}\n.empty-state[_ngcontent-%COMP%]   h3[_ngcontent-%COMP%] {\n  margin: 0 0 8px;\n  font-size: 1.25rem;\n  font-weight: 600;\n  color: var(--ion-text-color);\n}\n.empty-state[_ngcontent-%COMP%]   p[_ngcontent-%COMP%] {\n  margin: 0;\n  font-size: 0.95rem;\n}\n.news-list[_ngcontent-%COMP%] {\n  padding: 16px;\n  padding-bottom: 80px;\n}\n@media (min-width: 768px) {\n  .news-list[_ngcontent-%COMP%] {\n    max-width: 800px;\n    margin: 0 auto;\n  }\n}\nion-fab-button[_ngcontent-%COMP%] {\n  --background: var(--ion-color-primary);\n  --color: var(--ion-color-primary-contrast);\n}\nion-fab-button[_ngcontent-%COMP%]   ng-icon[_ngcontent-%COMP%] {\n  color: inherit;\n}"] });
641var NewsPage = _NewsPage;
642(() => {
643  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NewsPage, [{
644    type: Component,
645    args: [{ selector: "app-news", standalone: true, imports: [
646      CommonModule,
647      TranslateModule,
648      NgIcon,
649      NewsCardComponent,
650      IonHeader,
651      IonToolbar,
652      IonButtons,
653      IonMenuButton,
654      IonTitle,
655      IonContent,
656      IonFab,
657      IonFabButton,
658      IonRefresher,
659      IonRefresherContent,
660      IonSpinner
661    ], viewProviders: [provideIcons({ lucideNewspaper, lucidePlus, lucideSearchX })], template: `<ion-header>
662  <ion-toolbar>
663    <ion-buttons slot="start">
664      <ion-menu-button></ion-menu-button>
665    </ion-buttons>
666    <ion-title>{{ 'news.title' | translate }}</ion-title>
667  </ion-toolbar>
668</ion-header>
669
670<ion-content>
671  <ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
672    <ion-refresher-content></ion-refresher-content>
673  </ion-refresher>
674
675  @if (loading) {
676    <div class="loading-container">
677      <ion-spinner name="crescent"></ion-spinner>
678      <p>{{ 'news.loading' | translate }}</p>
679    </div>
680  } @else if (sortedNews.length === 0) {
681    <div class="empty-state">
682      <ng-icon name="lucideNewspaper" size="3em"></ng-icon>
683      <h3>{{ 'news.empty' | translate }}</h3>
684      <p>{{ 'news.emptyDescription' | translate }}</p>
685    </div>
686  } @else {
687    <div class="news-list">
688      <!-- Featured news as hero cards -->
689      @for (news of featuredNews; track news.entity.id) {
690        <app-news-card
691          [news]="news"
692          variant="featured"
693          (cardClick)="onNewsClick($event)">
694        </app-news-card>
695      }
696
697      <!-- Regular news as compact cards -->
698      @for (news of regularNews; track news.entity.id) {
699        <app-news-card
700          [news]="news"
701          variant="compact"
702          (cardClick)="onNewsClick($event)">
703        </app-news-card>
704      }
705    </div>
706  }
707
708  @if (canCreateNews) {
709    <ion-fab vertical="bottom" horizontal="end" slot="fixed">
710      <ion-fab-button (click)="onCreateNews()">
711        <ng-icon name="lucidePlus" size="1.5em"></ng-icon>
712      </ion-fab-button>
713    </ion-fab>
714  }
715</ion-content>
716`, styles: ["/* src/app/pages/news/news.page.scss */\n.loading-container {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  height: 50vh;\n  gap: 16px;\n  color: var(--ion-color-medium);\n}\n.loading-container p {\n  margin: 0;\n  font-size: 0.95rem;\n}\n.empty-state {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  height: 50vh;\n  padding: 24px;\n  text-align: center;\n  color: var(--ion-color-medium);\n}\n.empty-state ng-icon {\n  margin-bottom: 16px;\n  opacity: 0.6;\n}\n.empty-state h3 {\n  margin: 0 0 8px;\n  font-size: 1.25rem;\n  font-weight: 600;\n  color: var(--ion-text-color);\n}\n.empty-state p {\n  margin: 0;\n  font-size: 0.95rem;\n}\n.news-list {\n  padding: 16px;\n  padding-bottom: 80px;\n}\n@media (min-width: 768px) {\n  .news-list {\n    max-width: 800px;\n    margin: 0 auto;\n  }\n}\nion-fab-button {\n  --background: var(--ion-color-primary);\n  --color: var(--ion-color-primary-contrast);\n}\nion-fab-button ng-icon {\n  color: inherit;\n}\n"] }]
717  }], () => [{ type: DatabaseService }, { type: AuthService }, { type: SyncService }, { type: Router }], null);
718})();
719(() => {
720  (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(NewsPage, { className: "NewsPage", filePath: "src/app/pages/news/news.page.ts", lineNumber: 45 });
721})();
722export {
723  NewsPage
724};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.