PageSourceSearch

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

js stridenavigation.com collected 2026-09-24 14:53:54 UTC 86,800 bytes, 2,021 lines download raw bytes

1import {
2  PROBLEM_REPORT_CATEGORIES,
3  PROBLEM_SEVERITIES,
4  ProblemReportStateService
5} from "./chunk-AFCLXHHF.js";
6import {
7  Network
8} from "./chunk-53QL3RYY.js";
9import {
10  ApiService
11} from "./chunk-UN7A4OF7.js";
12import {
13  LocationService
14} from "./chunk-K52XM66R.js";
15import "./chunk-PYYASMRY.js";
16import "./chunk-63DLEHZF.js";
17import "./chunk-PFRFKH6U.js";
18import "./chunk-UZKEIBGO.js";
19import "./chunk-2E4THSOE.js";
20import {
21  GPSService
22} from "./chunk-JISY7VJD.js";
23import "./chunk-KP4RHBP7.js";
24import "./chunk-QI7JV4VS.js";
25import "./chunk-OTQ6GTAJ.js";
26import {
27  IonBackButton,
28  IonButton,
29  IonButtons,
30  IonCard,
31  IonCardContent,
32  IonContent,
33  IonHeader,
34  IonSelect,
35  IonSelectOption,
36  IonSpinner,
37  IonTextarea,
38  IonTitle,
39  IonToolbar
40} from "./chunk-RLZ522BW.js";
41import {
42  DatabaseService
43} from "./chunk-RLJCY5HS.js";
44import {
45  ToastService
46} from "./chunk-GR5DEE5Y.js";
47import {
48  TranslateModule,
49  TranslatePipe,
50  TranslateService
51} from "./chunk-O5XYYQCR.js";
52import "./chunk-QGEJYSW7.js";
53import {
54  StorageService
55} from "./chunk-MAIAEXGG.js";
56import "./chunk-L4O6SE3A.js";
57import "./chunk-INK5THDW.js";
58import {
59  NgIcon,
60  lucideAccessibility,
61  lucideBuilding,
62  lucideCamera,
63  lucideCircleHelp,
64  lucideDroplets,
65  lucideImage,
66  lucideMapPin,
67  lucideSend,
68  lucideSparkles,
69  lucideTriangleAlert,
70  lucideWifi,
71  lucideWifiOff,
72  lucideX,
73  lucideZap,
74  provideIcons
75} from "./chunk-GMPIA5L4.js";
76import {
77  BehaviorSubject,
78  Component,
79  EventEmitter,
80  FormsModule,
81  Injectable,
82  Input,
83  MaxLengthValidator,
84  NgControlStatus,
85  NgControlStatusGroup,
86  NgForm,
87  NgModel,
88  Output,
89  Router,
90  Subject,
91  catchError,
92  concatMap,
93  delay,
94  firstValueFrom,
95  from,
96  of,
97  setClassMetadata,
98  takeUntil,
99  tap,
100  ɵNgNoValidate,
101  ɵsetClassDebugInfo,
102  ɵɵProvidersFeature,
103  ɵɵadvance,
104  ɵɵclassProp,
105  ɵɵconditional,
106  ɵɵdefineComponent,
107  ɵɵdefineInjectable,
108  ɵɵdirectiveInject,
109  ɵɵelement,
110  ɵɵelementEnd,
111  ɵɵelementStart,
112  ɵɵgetCurrentView,
113  ɵɵinject,
114  ɵɵlistener,
115  ɵɵnextContext,
116  ɵɵpipe,
117  ɵɵpipeBind1,
118  ɵɵpipeBind2,
119  ɵɵproperty,
120  ɵɵpureFunction1,
121  ɵɵrepeater,
122  ɵɵrepeaterCreate,
123  ɵɵresetView,
124  ɵɵrestoreView,
125  ɵɵsanitizeUrl,
126  ɵɵtemplate,
127  ɵɵtext,
128  ɵɵtextInterpolate,
129  ɵɵtextInterpolate1,
130  ɵɵtwoWayBindingSet,
131  ɵɵtwoWayListener,
132  ɵɵtwoWayProperty
133} from "./chunk-UQ6LLOJ6.js";
134import "./chunk-BD3NMR2S.js";
135import "./chunk-6HPUB4U6.js";
136import "./chunk-JTG2GCS6.js";
137import "./chunk-WGFJL45C.js";
138import "./chunk-7WVISTDL.js";
139import "./chunk-2S57T3GS.js";
140import "./chunk-R4QHACMO.js";
141import "./chunk-4MEJEBWM.js";
142import "./chunk-RIBVNXHQ.js";
143import "./chunk-Z42OVMMO.js";
144import "./chunk-O5S42IXR.js";
145import "./chunk-QBR2RTFG.js";
146import "./chunk-MIOLVGSM.js";
147import "./chunk-V3YFOG6Q.js";
148import "./chunk-LPMBLWVS.js";
149import {
150  CapacitorException,
151  WebPlugin,
152  registerPlugin
153} from "./chunk-5LKHIIQE.js";
154import "./chunk-ITE2OHLM.js";
155import "./chunk-C46CB72A.js";
156import "./chunk-SRPSML5O.js";
157import "./chunk-NRSVX4KN.js";
158import "./chunk-T4GYAOTA.js";
159import "./chunk-2VHYZHLL.js";
160import "./chunk-S6LHOZ5A.js";
161import "./chunk-Y6HZOH3Q.js";
162import "./chunk-TWQHADEW.js";
163import "./chunk-3J4L7S6G.js";
164import "./chunk-IL42VKT6.js";
165import "./chunk-LONO5O5R.js";
166import "./chunk-KFBF5L4X.js";
167import "./chunk-PWC4K6CW.js";
168import "./chunk-R6JLIESI.js";
169import "./chunk-UG6K57PI.js";
170import "./chunk-UR6VIRNL.js";
171import "./chunk-OJO5RD3M.js";
172import {
173  __async
174} from "./chunk-CO64IJQE.js";
175
vendor: 2,941 bytes, lines 176-237
176// node_modules/@capacitor/camera/dist/esm/definitions.js
177var CameraSource;
178(function(CameraSource2) {
179  CameraSource2["Prompt"] = "PROMPT";
180  CameraSource2["Camera"] = "CAMERA";
181  CameraSource2["Photos"] = "PHOTOS";
182})(CameraSource || (CameraSource = {}));
183var CameraDirection;
184(function(CameraDirection2) {
185  CameraDirection2["Rear"] = "REAR";
186  CameraDirection2["Front"] = "FRONT";
187})(CameraDirection || (CameraDirection = {}));
188var CameraResultType;
189(function(CameraResultType2) {
190  CameraResultType2["Uri"] = "uri";
191  CameraResultType2["Base64"] = "base64";
192  CameraResultType2["DataUrl"] = "dataUrl";
193})(CameraResultType || (CameraResultType = {}));
194var MediaType;
195(function(MediaType2) {
196  MediaType2[MediaType2["Photo"] = 0] = "Photo";
197  MediaType2[MediaType2["Video"] = 1] = "Video";
198})(MediaType || (MediaType = {}));
199var MediaTypeSelection;
200(function(MediaTypeSelection2) {
201  MediaTypeSelection2[MediaTypeSelection2["Photo"] = 0] = "Photo";
202  MediaTypeSelection2[MediaTypeSelection2["Video"] = 1] = "Video";
203  MediaTypeSelection2[MediaTypeSelection2["All"] = 2] = "All";
204})(MediaTypeSelection || (MediaTypeSelection = {}));
205var EncodingType;
206(function(EncodingType2) {
207  EncodingType2[EncodingType2["JPEG"] = 0] = "JPEG";
208  EncodingType2[EncodingType2["PNG"] = 1] = "PNG";
209})(EncodingType || (EncodingType = {}));
210var CameraErrorCode;
211(function(CameraErrorCode2) {
212  CameraErrorCode2["CameraPermissionDenied"] = "OS-PLUG-CAMR-0003";
213  CameraErrorCode2["GalleryPermissionDenied"] = "OS-PLUG-CAMR-0005";
214  CameraErrorCode2["NoCameraAvailable"] = "OS-PLUG-CAMR-0007";
215  CameraErrorCode2["TakePhotoCancelled"] = "OS-PLUG-CAMR-0006";
216  CameraErrorCode2["TakePhotoFailed"] = "OS-PLUG-CAMR-0010";
217  CameraErrorCode2["TakePhotoInvalidArguments"] = "OS-PLUG-CAMR-0014";
218  CameraErrorCode2["InvalidImageData"] = "OS-PLUG-CAMR-0008";
219  CameraErrorCode2["EditPhotoFailed"] = "OS-PLUG-CAMR-0009";
220  CameraErrorCode2["EditPhotoCancelled"] = "OS-PLUG-CAMR-0013";
221  CameraErrorCode2["EditPhotoEmptyUri"] = "OS-PLUG-CAMR-0024";
222  CameraErrorCode2["ImageNotFound"] = "OS-PLUG-CAMR-0011";
223  CameraErrorCode2["ProcessImageFailed"] = "OS-PLUG-CAMR-0012";
224  CameraErrorCode2["ChooseMediaFailed"] = "OS-PLUG-CAMR-0018";
225  CameraErrorCode2["ChooseMediaCancelled"] = "OS-PLUG-CAMR-0020";
226  CameraErrorCode2["MediaPathError"] = "OS-PLUG-CAMR-0021";
227  CameraErrorCode2["FetchImageFromUriFailed"] = "OS-PLUG-CAMR-0028";
228  CameraErrorCode2["RecordVideoFailed"] = "OS-PLUG-CAMR-0016";
229  CameraErrorCode2["RecordVideoCancelled"] = "OS-PLUG-CAMR-0017";
230  CameraErrorCode2["VideoNotFound"] = "OS-PLUG-CAMR-0025";
231  CameraErrorCode2["PlayVideoFailed"] = "OS-PLUG-CAMR-0023";
232  CameraErrorCode2["EncodeResultFailed"] = "OS-PLUG-CAMR-0019";
233  CameraErrorCode2["FileNotFound"] = "OS-PLUG-CAMR-0027";
234  CameraErrorCode2["InvalidArgument"] = "OS-PLUG-CAMR-0031";
235  CameraErrorCode2["GeneralError"] = "OS-PLUG-CAMR-0026";
236})(CameraErrorCode || (CameraErrorCode = {}));
237
vendor: 17,193 bytes, lines 238-746
238// node_modules/@capacitor/camera/dist/esm/web.js
239var CameraWeb = class extends WebPlugin {
240  takePhoto(options) {
241    return __async(this, null, function* () {
242      return new Promise((resolve, reject) => __async(this, null, function* () {
243        if (options.webUseInput) {
244          this.takePhotoCameraInputExperience(options, resolve, reject);
245        } else {
246          this.takePhotoCameraExperience(options, resolve, reject);
247        }
248      }));
249    });
250  }
251  recordVideo(_options) {
252    return __async(this, null, function* () {
253      throw this.unimplemented("recordVideo is not implemented on Web.");
254    });
255  }
256  playVideo(_options) {
257    return __async(this, null, function* () {
258      throw this.unimplemented("playVideo is not implemented on Web.");
259    });
260  }
261  chooseFromGallery(options) {
262    return __async(this, null, function* () {
263      return new Promise((resolve, reject) => __async(this, null, function* () {
264        this.galleryInputExperience(options, resolve, reject);
265      }));
266    });
267  }
268  editPhoto(_options) {
269    return __async(this, null, function* () {
270      throw this.unimplemented("editPhoto is not implemented on Web.");
271    });
272  }
273  editURIPhoto(_options) {
274    return __async(this, null, function* () {
275      throw this.unimplemented("editURIPhoto is not implemented on Web.");
276    });
277  }
278  getPhoto(options) {
279    return __async(this, null, function* () {
280      return new Promise((resolve, reject) => __async(this, null, function* () {
281        if (options.webUseInput || options.source === CameraSource.Photos) {
282          this.fileInputExperience(options, resolve, reject);
283        } else if (options.source === CameraSource.Prompt) {
284          let actionSheet = document.querySelector("pwa-action-sheet");
285          if (!actionSheet) {
286            actionSheet = document.createElement("pwa-action-sheet");
287            document.body.appendChild(actionSheet);
288          }
289          actionSheet.header = options.promptLabelHeader || "Photo";
290          actionSheet.cancelable = false;
291          actionSheet.options = [{
292            title: options.promptLabelPhoto || "From Photos"
293          }, {
294            title: options.promptLabelPicture || "Take Picture"
295          }];
296          actionSheet.addEventListener("onSelection", (e) => __async(this, null, function* () {
297            const selection = e.detail;
298            if (selection === 0) {
299              this.fileInputExperience(options, resolve, reject);
300            } else {
301              this.cameraExperience(options, resolve, reject);
302            }
303          }));
304        } else {
305          this.cameraExperience(options, resolve, reject);
306        }
307      }));
308    });
309  }
310  pickImages(_options) {
311    return __async(this, null, function* () {
312      return new Promise((resolve, reject) => __async(this, null, function* () {
313        this.multipleFileInputExperience(resolve, reject);
314      }));
315    });
316  }
317  cameraExperience(options, resolve, reject) {
318    return __async(this, null, function* () {
319      yield this._setupPWACameraModal(options.direction, (photo) => this._getCameraPhoto(photo, options), () => this.fileInputExperience(options, resolve, reject), resolve, reject);
320    });
321  }
322  fileInputExperience(options, resolve, reject) {
323    let input = document.querySelector("#_capacitor-camera-input");
324    const cleanup = () => {
325      var _a;
326      (_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
327    };
328    if (!input) {
329      input = document.createElement("input");
330      input.id = "_capacitor-camera-input";
331      input.type = "file";
332      input.hidden = true;
333      document.body.appendChild(input);
334      input.addEventListener("change", (_e) => {
335        const file = input.files[0];
336        let format = "jpeg";
337        if (file.type === "image/png") {
338          format = "png";
339        } else if (file.type === "image/gif") {
340          format = "gif";
341        }
342        if (options.resultType === "dataUrl" || options.resultType === "base64") {
343          const reader = new FileReader();
344          reader.addEventListener("load", () => {
345            if (options.resultType === "dataUrl") {
346              resolve({
347                dataUrl: reader.result,
348                format
349              });
350            } else if (options.resultType === "base64") {
351              const b64 = reader.result.split(",")[1];
352              resolve({
353                base64String: b64,
354                format
355              });
356            }
357            cleanup();
358          });
359          reader.readAsDataURL(file);
360        } else {
361          resolve({
362            webPath: URL.createObjectURL(file),
363            format
364          });
365          cleanup();
366        }
367      });
368      input.addEventListener("cancel", (_e) => {
369        reject(new CapacitorException("User cancelled photos app"));
370        cleanup();
371      });
372    }
373    input.accept = "image/*";
374    input.capture = true;
375    if (options.source === CameraSource.Photos || options.source === CameraSource.Prompt) {
376      input.removeAttribute("capture");
377    } else if (options.direction === CameraDirection.Front) {
378      input.capture = "user";
379    } else if (options.direction === CameraDirection.Rear) {
380      input.capture = "environment";
381    }
382    input.click();
383  }
384  multipleFileInputExperience(resolve, reject) {
385    let input = document.querySelector("#_capacitor-camera-input-multiple");
386    const cleanup = () => {
387      var _a;
388      (_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
389    };
390    if (!input) {
391      input = document.createElement("input");
392      input.id = "_capacitor-camera-input-multiple";
393      input.type = "file";
394      input.hidden = true;
395      input.multiple = true;
396      document.body.appendChild(input);
397      input.addEventListener("change", (_e) => {
398        const photos = [];
399        for (let i = 0; i < input.files.length; i++) {
400          const file = input.files[i];
401          let format = "jpeg";
402          if (file.type === "image/png") {
403            format = "png";
404          } else if (file.type === "image/gif") {
405            format = "gif";
406          }
407          photos.push({
408            webPath: URL.createObjectURL(file),
409            format
410          });
411        }
412        resolve({
413          photos
414        });
415        cleanup();
416      });
417      input.addEventListener("cancel", (_e) => {
418        reject(new CapacitorException("User cancelled photos app"));
419        cleanup();
420      });
421    }
422    input.accept = "image/*";
423    input.click();
424  }
425  _getCameraPhoto(photo, options) {
426    return new Promise((resolve, reject) => {
427      const reader = new FileReader();
428      const format = this._getFileFormat(photo);
429      if (options.resultType === "uri") {
430        resolve({
431          webPath: URL.createObjectURL(photo),
432          format,
433          saved: false
434        });
435      } else {
436        reader.readAsDataURL(photo);
437        reader.onloadend = () => {
438          const r = reader.result;
439          if (options.resultType === "dataUrl") {
440            resolve({
441              dataUrl: r,
442              format,
443              saved: false
444            });
445          } else {
446            resolve({
447              base64String: r.split(",")[1],
448              format,
449              saved: false
450            });
451          }
452        };
453        reader.onerror = (e) => {
454          reject(e);
455        };
456      }
457    });
458  }
459  takePhotoCameraExperience(options, resolve, reject) {
460    return __async(this, null, function* () {
461      yield this._setupPWACameraModal(options.cameraDirection, (photo) => {
462        var _a;
463        return this._buildPhotoMediaResult(photo, (_a = options.includeMetadata) !== null && _a !== void 0 ? _a : false);
464      }, () => this.takePhotoCameraInputExperience(options, resolve, reject), resolve, reject);
465    });
466  }
467  takePhotoCameraInputExperience(options, resolve, reject) {
468    const input = this._createFileInput("_capacitor-camera-input-takephoto");
469    const cleanup = () => {
470      var _a;
471      (_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
472    };
473    input.onchange = (_e) => __async(this, null, function* () {
474      var _a;
475      if (!this._validateFileInput(input, reject, cleanup)) {
476        return;
477      }
478      const file = input.files[0];
479      resolve(yield this._buildPhotoMediaResult(file, (_a = options.includeMetadata) !== null && _a !== void 0 ? _a : false));
480      cleanup();
481    });
482    input.oncancel = () => {
483      reject(new CapacitorException("User cancelled photos app"));
484      cleanup();
485    };
486    input.accept = "image/*";
487    if (options.cameraDirection === CameraDirection.Front) {
488      input.capture = "user";
489    } else {
490      input.capture = "environment";
491    }
492    input.click();
493  }
494  galleryInputExperience(options, resolve, reject) {
495    var _a, _b;
496    const input = this._createFileInput("_capacitor-camera-input-gallery");
497    input.multiple = (_a = options.allowMultipleSelection) !== null && _a !== void 0 ? _a : false;
498    const cleanup = () => {
499      var _a2;
500      (_a2 = input.parentNode) === null || _a2 === void 0 ? void 0 : _a2.removeChild(input);
501    };
502    input.onchange = (_e) => __async(this, null, function* () {
503      var _a2;
504      if (!this._validateFileInput(input, reject, cleanup)) {
505        return;
506      }
507      const results = [];
508      for (let i = 0; i < input.files.length; i++) {
509        const file = input.files[i];
510        if (file.type.startsWith("image/")) {
511          results.push(yield this._buildPhotoMediaResult(file, (_a2 = options.includeMetadata) !== null && _a2 !== void 0 ? _a2 : false));
512        } else if (file.type.startsWith("video/")) {
513          const format = this._getFileFormat(file);
514          let thumbnail;
515          let resolution;
516          let duration;
517          try {
518            const videoInfo = yield this._getVideoMetadata(file);
519            thumbnail = videoInfo.thumbnail;
520            if (options.includeMetadata) {
521              resolution = videoInfo.resolution;
522              duration = videoInfo.duration;
523            }
524          } catch (e) {
525            console.warn("Failed to get video metadata:", e);
526          }
527          const result = {
528            type: MediaType.Video,
529            thumbnail,
530            webPath: URL.createObjectURL(file),
531            saved: false
532          };
533          if (options.includeMetadata) {
534            result.metadata = {
535              format,
536              resolution,
537              size: file.size,
538              creationDate: new Date(file.lastModified).toISOString(),
539              duration
540            };
541          }
542          results.push(result);
543        }
544      }
545      resolve({
546        results
547      });
548      cleanup();
549    });
550    input.oncancel = () => {
551      reject(new CapacitorException("User cancelled photos app"));
552      cleanup();
553    };
554    const mediaType = (_b = options.mediaType) !== null && _b !== void 0 ? _b : MediaTypeSelection.Photo;
555    if (mediaType === MediaTypeSelection.Photo) {
556      input.accept = "image/*";
557    } else if (mediaType === MediaTypeSelection.Video) {
558      input.accept = "video/*";
559    } else {
560      input.accept = "image/*,video/*";
561    }
562    input.click();
563  }
564  _getFileFormat(file) {
565    if (file.type === "image/png") {
566      return "png";
567    } else if (file.type === "image/gif") {
568      return "gif";
569    } else if (file.type.startsWith("video/")) {
570      return file.type.split("/")[1];
571    } else if (file.type.startsWith("image/")) {
572      return "jpeg";
573    }
574    return file.type.split("/")[1] || "jpeg";
575  }
576  _buildPhotoMediaResult(file, includeMetadata) {
577    return __async(this, null, function* () {
578      const format = this._getFileFormat(file);
579      const thumbnail = yield this._getBase64FromFile(file);
580      const result = {
581        type: MediaType.Photo,
582        thumbnail,
583        webPath: URL.createObjectURL(file),
584        saved: false
585      };
586      if (includeMetadata) {
587        const resolution = yield this._getImageResolution(file);
588        result.metadata = {
589          format,
590          resolution,
591          size: file.size,
592          creationDate: "lastModified" in file ? new Date(file.lastModified).toISOString() : (/* @__PURE__ */ new Date()).toISOString()
593        };
594      }
595      return result;
596    });
597  }
598  _validateFileInput(input, reject, cleanup) {
599    if (!input.files || input.files.length === 0) {
600      const message = input.multiple ? "No files selected" : "No file selected";
601      reject(new CapacitorException(message));
602      cleanup();
603      return false;
604    }
605    return true;
606  }
607  _setupPWACameraModal(cameraDirection, onPhotoCallback, fallbackCallback, resolve, reject) {
608    return __async(this, null, function* () {
609      if (customElements.get("pwa-camera-modal")) {
610        const cameraModal = document.createElement("pwa-camera-modal");
611        cameraModal.facingMode = cameraDirection === CameraDirection.Front ? "user" : "environment";
612        document.body.appendChild(cameraModal);
613        try {
614          yield cameraModal.componentOnReady();
615          cameraModal.addEventListener("onPhoto", (e) => __async(this, null, function* () {
616            const photo = e.detail;
617            if (photo === null) {
618              reject(new CapacitorException("User cancelled photos app"));
619            } else if (photo instanceof Error) {
620              reject(photo);
621            } else {
622              resolve(yield onPhotoCallback(photo));
623            }
624            cameraModal.dismiss();
625            document.body.removeChild(cameraModal);
626          }));
627          cameraModal.present();
628        } catch (e) {
629          fallbackCallback();
630        }
631      } else {
632        console.error(`Unable to load PWA Element 'pwa-camera-modal'. See the docs: https://capacitorjs.com/docs/web/pwa-elements.`);
633        fallbackCallback();
634      }
635    });
636  }
637  _createFileInput(id) {
638    let input = document.querySelector(`#${id}`);
639    if (!input) {
640      input = document.createElement("input");
641      input.id = id;
642      input.type = "file";
643      input.hidden = true;
644      document.body.appendChild(input);
645    }
646    return input;
647  }
648  _getImageResolution(image) {
649    return __async(this, null, function* () {
650      try {
651        const bitmap = yield createImageBitmap(image);
652        const resolution = `${bitmap.width}x${bitmap.height}`;
653        bitmap.close();
654        return resolution;
655      } catch (e) {
656        console.warn("Failed to get image resolution:", e);
657        return void 0;
658      }
659    });
660  }
661  _getBase64FromFile(file) {
662    return new Promise((resolve, reject) => {
663      const reader = new FileReader();
664      reader.onloadend = () => {
665        const dataUrl = reader.result;
666        const base64 = dataUrl.split(",")[1];
667        resolve(base64);
668      };
669      reader.onerror = (e) => {
670        reject(e);
671      };
672      reader.readAsDataURL(file);
673    });
674  }
675  _getVideoMetadata(videoFile) {
676    return new Promise((resolve) => {
677      const video = document.createElement("video");
678      video.preload = "metadata";
679      video.muted = true;
680      video.onloadedmetadata = () => {
681        const seekTime = Math.min(1, video.duration * 0.1);
682        video.currentTime = seekTime;
683      };
684      video.onseeked = () => {
685        const result = {
686          resolution: `${video.videoWidth}x${video.videoHeight}`,
687          duration: video.duration
688        };
689        try {
690          const canvas = document.createElement("canvas");
691          canvas.width = video.videoWidth;
692          canvas.height = video.videoHeight;
693          const ctx = canvas.getContext("2d");
694          if (ctx) {
695            ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
696            result.thumbnail = canvas.toDataURL("image/jpeg", 0.8).split(",")[1];
697          }
698        } catch (e) {
699          console.warn("Failed to generate video thumbnail:", e);
700        }
701        URL.revokeObjectURL(video.src);
702        resolve(result);
703      };
704      video.onerror = () => {
705        URL.revokeObjectURL(video.src);
706        resolve({});
707      };
708      video.src = URL.createObjectURL(videoFile);
709    });
710  }
711  checkPermissions() {
712    return __async(this, null, function* () {
713      if (typeof navigator === "undefined" || !navigator.permissions) {
714        throw this.unavailable("Permissions API not available in this browser");
715      }
716      try {
717        const permission = yield window.navigator.permissions.query({
718          name: "camera"
719        });
720        return {
721          camera: permission.state,
722          photos: "granted"
723        };
724      } catch (_a) {
725        throw this.unavailable("Camera permissions are not available in this browser");
726      }
727    });
728  }
729  requestPermissions() {
730    return __async(this, null, function* () {
731      throw this.unimplemented("Not implemented on web.");
732    });
733  }
734  pickLimitedLibraryPhotos() {
735    return __async(this, null, function* () {
736      throw this.unavailable("Not implemented on web.");
737    });
738  }
739  getLimitedLibraryPhotos() {
740    return __async(this, null, function* () {
741      throw this.unavailable("Not implemented on web.");
742    });
743  }
744};
745var Camera = new CameraWeb();
746
vendor: 127 bytes, lines 747-751
747// node_modules/@capacitor/camera/dist/esm/index.js
748var Camera2 = registerPlugin("Camera", {
749  web: () => new CameraWeb()
750});
751
752// src/app/shared/components/photo-capture/photo-capture.component.ts
753function PhotoCaptureComponent_Conditional_2_Template(rf, ctx) {
754  if (rf & 1) {
755    const _r1 = \u0275\u0275getCurrentView();
756    \u0275\u0275element(0, "img", 8);
757    \u0275\u0275elementStart(1, "button", 9);
758    \u0275\u0275listener("click", function PhotoCaptureComponent_Conditional_2_Template_button_click_1_listener() {
759      \u0275\u0275restoreView(_r1);
760      const ctx_r1 = \u0275\u0275nextContext();
761      return \u0275\u0275resetView(ctx_r1.removePhoto());
762    });
763    \u0275\u0275element(2, "ng-icon", 10);
764    \u0275\u0275elementEnd();
765  }
766  if (rf & 2) {
767    const ctx_r1 = \u0275\u0275nextContext();
768    \u0275\u0275property("src", ctx_r1.currentPhoto, \u0275\u0275sanitizeUrl);
769  }
770}
771function PhotoCaptureComponent_Conditional_3_Template(rf, ctx) {
772  if (rf & 1) {
773    const _r3 = \u0275\u0275getCurrentView();
774    \u0275\u0275elementStart(0, "div", 11);
775    \u0275\u0275listener("click", function PhotoCaptureComponent_Conditional_3_Template_div_click_0_listener() {
776      \u0275\u0275restoreView(_r3);
777      const ctx_r1 = \u0275\u0275nextContext();
778      return \u0275\u0275resetView(ctx_r1.captureFromCamera());
779    });
780    \u0275\u0275element(1, "ng-icon", 12);
781    \u0275\u0275elementStart(2, "span");
782    \u0275\u0275text(3);
783    \u0275\u0275pipe(4, "translate");
784    \u0275\u0275elementEnd()();
785  }
786  if (rf & 2) {
787    \u0275\u0275advance(3);
788    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(4, 1, "problemReport.photo.placeholder"));
789  }
790}
791function PhotoCaptureComponent_Conditional_4_Template(rf, ctx) {
792  if (rf & 1) {
793    \u0275\u0275elementStart(0, "div", 3);
794    \u0275\u0275element(1, "ion-spinner", 13);
795    \u0275\u0275elementEnd();
796  }
797}
798var _PhotoCaptureComponent = class _PhotoCaptureComponent {
799  constructor() {
800    this.currentPhoto = null;
801    this.photoCapture = new EventEmitter();
802    this.photoRemove = new EventEmitter();
803    this.isCapturing = false;
804  }
805  captureFromCamera() {
806    return __async(this, null, function* () {
807      yield this.capturePhoto(CameraSource.Camera);
808    });
809  }
810  captureFromGallery() {
811    return __async(this, null, function* () {
812      yield this.capturePhoto(CameraSource.Photos);
813    });
814  }
815  capturePhoto(source) {
816    return __async(this, null, function* () {
817      if (this.isCapturing)
818        return;
819      this.isCapturing = true;
820      try {
821        const image = yield Camera2.getPhoto({
822          quality: 80,
823          allowEditing: false,
824          resultType: CameraResultType.Base64,
825          source,
826          width: 1920,
827          height: 1080,
828          correctOrientation: true
829        });
830        if (image.base64String) {
831          const mimeType = image.format === "png" ? "image/png" : "image/jpeg";
832          const dataUrl = `data:${mimeType};base64,${image.base64String}`;
833          this.photoCapture.emit(dataUrl);
834        }
835      } catch (error) {
836        if ((error == null ? void 0 : error.message) !== "User cancelled photos app") {
837          console.error("Photo capture error:", error);
838        }
839      } finally {
840        this.isCapturing = false;
841      }
842    });
843  }
844  removePhoto() {
845    this.photoRemove.emit();
846  }
847};
848_PhotoCaptureComponent.\u0275fac = function PhotoCaptureComponent_Factory(__ngFactoryType__) {
849  return new (__ngFactoryType__ || _PhotoCaptureComponent)();
850};
851_PhotoCaptureComponent.\u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _PhotoCaptureComponent, selectors: [["app-photo-capture"]], inputs: { currentPhoto: "currentPhoto" }, outputs: { photoCapture: "photoCapture", photoRemove: "photoRemove" }, features: [\u0275\u0275ProvidersFeature([], [provideIcons({ lucideCamera, lucideImage, lucideX })])], decls: 14, vars: 12, consts: [[1, "photo-capture-container"], [1, "photo-preview"], [1, "placeholder"], [1, "capturing-overlay"], [1, "capture-buttons"], ["fill", "outline", "size", "small", 3, "click", "disabled"], ["name", "lucideCamera", "slot", "start"], ["name", "lucideImage", "slot", "start"], ["alt", "Problem photo", 3, "src"], ["type", "button", 1, "remove-button", 3, "click"], ["name", "lucideX", "size", "16"], [1, "placeholder", 3, "click"], ["name", "lucideCamera", "size", "48"], ["name", "crescent"]], template: function PhotoCaptureComponent_Template(rf, ctx) {
852  if (rf & 1) {
853    \u0275\u0275elementStart(0, "div", 0)(1, "div", 1);
854    \u0275\u0275template(2, PhotoCaptureComponent_Conditional_2_Template, 3, 1)(3, PhotoCaptureComponent_Conditional_3_Template, 5, 3, "div", 2)(4, PhotoCaptureComponent_Conditional_4_Template, 2, 0, "div", 3);
855    \u0275\u0275elementEnd();
856    \u0275\u0275elementStart(5, "div", 4)(6, "ion-button", 5);
857    \u0275\u0275listener("click", function PhotoCaptureComponent_Template_ion_button_click_6_listener() {
858      return ctx.captureFromCamera();
859    });
860    \u0275\u0275element(7, "ng-icon", 6);
861    \u0275\u0275text(8);
862    \u0275\u0275pipe(9, "translate");
863    \u0275\u0275elementEnd();
864    \u0275\u0275elementStart(10, "ion-button", 5);
865    \u0275\u0275listener("click", function PhotoCaptureComponent_Template_ion_button_click_10_listener() {
866      return ctx.captureFromGallery();
867    });
868    \u0275\u0275element(11, "ng-icon", 7);
869    \u0275\u0275text(12);
870    \u0275\u0275pipe(13, "translate");
871    \u0275\u0275elementEnd()()();
872  }
873  if (rf & 2) {
874    \u0275\u0275advance();
875    \u0275\u0275classProp("has-photo", ctx.currentPhoto);
876    \u0275\u0275advance();
877    \u0275\u0275conditional(ctx.currentPhoto ? 2 : 3);
878    \u0275\u0275advance(2);
879    \u0275\u0275conditional(ctx.isCapturing ? 4 : -1);
880    \u0275\u0275advance(2);
881    \u0275\u0275property("disabled", ctx.isCapturing);
882    \u0275\u0275advance(2);
883    \u0275\u0275textInterpolate1(" ", \u0275\u0275pipeBind1(9, 8, "problemReport.photo.camera"), " ");
884    \u0275\u0275advance(2);
885    \u0275\u0275property("disabled", ctx.isCapturing);
886    \u0275\u0275advance(2);
887    \u0275\u0275textInterpolate1(" ", \u0275\u0275pipeBind1(13, 10, "problemReport.photo.gallery"), " ");
888  }
889}, dependencies: [
890  IonButton,
891  IonSpinner,
892  TranslateModule,
893  TranslatePipe,
894  NgIcon
895], styles: ["\n\n.photo-capture-container[_ngcontent-%COMP%] {\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n}\n.photo-preview[_ngcontent-%COMP%] {\n  position: relative;\n  width: 100%;\n  aspect-ratio: 16/9;\n  background: var(--ion-color-light);\n  border-radius: 12px;\n  overflow: hidden;\n  border: 2px dashed var(--ion-color-medium);\n}\n.photo-preview.has-photo[_ngcontent-%COMP%] {\n  border-style: solid;\n  border-color: var(--ion-color-primary);\n}\n.photo-preview[_ngcontent-%COMP%]   img[_ngcontent-%COMP%] {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n}\n.photo-preview[_ngcontent-%COMP%]   .placeholder[_ngcontent-%COMP%] {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  height: 100%;\n  color: var(--ion-color-medium);\n  gap: 8px;\n}\n.photo-preview[_ngcontent-%COMP%]   .placeholder[_ngcontent-%COMP%]   span[_ngcontent-%COMP%] {\n  font-size: 14px;\n}\n.photo-preview[_ngcontent-%COMP%]   .remove-button[_ngcontent-%COMP%] {\n  position: absolute;\n  top: 8px;\n  right: 8px;\n  width: 32px;\n  height: 32px;\n  border-radius: 50%;\n  background: rgba(0, 0, 0, 0.6);\n  border: none;\n  color: white;\n  display: flex;\n  align-items: center;\n  justify-content: center;
895\n  cursor: pointer;\n}\n.photo-preview[_ngcontent-%COMP%]   .remove-button[_ngcontent-%COMP%]:hover {\n  background: rgba(0, 0, 0, 0.8);\n}\n.photo-preview[_ngcontent-%COMP%]   .capturing-overlay[_ngcontent-%COMP%] {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  background: rgba(0, 0, 0, 0.5);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.photo-preview[_ngcontent-%COMP%]   .capturing-overlay[_ngcontent-%COMP%]   ion-spinner[_ngcontent-%COMP%] {\n  color: white;\n  --color: white;\n}\n.capture-buttons[_ngcontent-%COMP%] {\n  display: flex;\n  gap: 12px;\n  justify-content: center;\n}\n.capture-buttons[_ngcontent-%COMP%]   ion-button[_ngcontent-%COMP%] {\n  flex: 1;\n  max-width: 150px;\n}\n.capture-buttons[_ngcontent-%COMP%]   ion-button[_ngcontent-%COMP%]   ng-icon[_ngcontent-%COMP%] {\n  margin-right: 4px;\n}"] });
896var PhotoCaptureComponent = _PhotoCaptureComponent;
897(() => {
898  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(PhotoCaptureComponent, [{
899    type: Component,
900    args: [{ selector: "app-photo-capture", standalone: true, imports: [
901      IonButton,
902      IonSpinner,
903      TranslateModule,
904      NgIcon
905    ], viewProviders: [provideIcons({ lucideCamera, lucideImage, lucideX })], template: `<div class="photo-capture-container">
906  <div class="photo-preview" [class.has-photo]="currentPhoto">
907    @if (currentPhoto) {
908      <img [src]="currentPhoto" alt="Problem photo" />
909      <button class="remove-button" (click)="removePhoto()" type="button">
910        <ng-icon name="lucideX" size="16" />
911      </button>
912    } @else {
913      <div class="placeholder" (click)="captureFromCamera()">
914        <ng-icon name="lucideCamera" size="48" />
915        <span>{{ 'problemReport.photo.placeholder' | translate }}</span>
916      </div>
917    }
918
919    @if (isCapturing) {
920      <div class="capturing-overlay">
921        <ion-spinner name="crescent" />
922      </div>
923    }
924  </div>
925
926  <div class="capture-buttons">
927    <ion-button
928      fill="outline"
929      size="small"
930      [disabled]="isCapturing"
931      (click)="captureFromCamera()">
932      <ng-icon name="lucideCamera" slot="start" />
933      {{ 'problemReport.photo.camera' | translate }}
934    </ion-button>
935
936    <ion-button
937      fill="outline"
938      size="small"
939      [disabled]="isCapturing"
940      (click)="captureFromGallery()">
941      <ng-icon name="lucideImage" slot="start" />
942      {{ 'problemReport.photo.gallery' | translate }}
943    </ion-button>
944  </div>
945</div>
946`, styles: ["/* src/app/shared/components/photo-capture/photo-capture.component.scss */\n.photo-capture-container {\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n}\n.photo-preview {\n  position: relative;\n  width: 100%;\n  aspect-ratio: 16/9;\n  background: var(--ion-color-light);\n  border-radius: 12px;\n  overflow: hidden;\n  border: 2px dashed var(--ion-color-medium);\n}\n.photo-preview.has-photo {\n  border-style: solid;\n  border-color: var(--ion-color-primary);\n}\n.photo-preview img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n}\n.photo-preview .placeholder {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  height: 100%;\n  color: var(--ion-color-medium);\n  gap: 8px;\n}\n.photo-preview .placeholder span {\n  font-size: 14px;\n}\n.photo-preview .remove-button {\n  position: absolute;\n  top: 8px;\n  right: 8px;\n  width: 32px;\n  height: 32px;\n  border-radius: 50%;\n  background: rgba(0, 0, 0, 0.6);\n  border: none;\n  color: white;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  cursor: pointer;\n}\n.photo-preview .remove-button:hover {\n  background: rgba(0, 0, 0, 0.8);\n}\n.photo-preview .capturing-overlay {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  background: rgba(0, 0, 0, 0.5);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.photo-preview .capturing-overlay ion-spinner {\n  color: white;\n  --color: white;\n}\n.capture-buttons {\n  display: flex;\n  gap: 12px;\n  justify-content: center;\n}\n.capture-buttons ion-button {\n  flex: 1;\n  max-width: 150px;\n}
946\n.capture-buttons ion-button ng-icon {\n  margin-right: 4px;\n}\n"] }]
947  }], null, { currentPhoto: [{
948    type: Input
949  }], photoCapture: [{
950    type: Output
951  }], photoRemove: [{
952    type: Output
953  }] });
954})();
955(() => {
956  (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(PhotoCaptureComponent, { className: "PhotoCaptureComponent", filePath: "src/app/shared/components/photo-capture/photo-capture.component.ts", lineNumber: 19 });
957})();
958
959// src/app/services/problem-report/problem-report-queue.service.ts
960var QUEUE_STORAGE_KEY = "problem_report_queue";
961var MAX_QUEUE_SIZE = 10;
962var MAX_RETRY_COUNT = 3;
963var _ProblemReportQueueService = class _ProblemReportQueueService {
964  constructor(storageService, apiService, stateService) {
965    this.storageService = storageService;
966    this.apiService = apiService;
967    this.stateService = stateService;
968    this.destroy$ = new Subject();
969    this.queue$ = new BehaviorSubject([]);
970    this.isOnline$ = new BehaviorSubject(true);
971    this.isSyncing$ = new BehaviorSubject(false);
972    this.init();
973  }
974  ngOnDestroy() {
975    this.destroy$.next();
976    this.destroy$.complete();
977    this.removeNetworkListener();
978  }
979  /**
980   * Initialize the queue service.
981   */
982  init() {
983    return __async(this, null, function* () {
984      yield this.loadQueue();
985      const status = yield Network.getStatus();
986      this.isOnline$.next(status.connected);
987      this.networkListenerHandle = yield Network.addListener("networkStatusChange", (status2) => {
988        const wasOffline = !this.isOnline$.value;
989        this.isOnline$.next(status2.connected);
990        if (status2.connected && wasOffline) {
991          console.log("[ProblemReportQueue] Network restored, starting sync...");
992          this.processQueue();
993        }
994      });
995      if (this.isOnline$.value && this.queue$.value.length > 0) {
996        this.processQueue();
997      }
998    });
999  }
1000  /**
1001   * Remove network listener on cleanup.
1002   */
1003  removeNetworkListener() {
1004    return __async(this, null, function* () {
1005      if (this.networkListenerHandle) {
1006        yield this.networkListenerHandle.remove();
1007      }
1008    });
1009  }
1010  /**
1011   * Load queue from local storage.
1012   */
1013  loadQueue() {
1014    return __async(this, null, function* () {
1015      try {
1016        const stored = yield this.storageService.get(QUEUE_STORAGE_KEY);
1017        if (stored) {
1018          const queue = JSON.parse(stored);
1019          this.queue$.next(queue);
1020          this.updateStateService();
1021        }
1022      } catch (error) {
1023        console.error("[ProblemReportQueue] Failed to load queue:", error);
1024        this.queue$.next([]);
1025      }
1026    });
1027  }
1028  /**
1029   * Save queue to local storage.
1030   */
1031  saveQueue() {
1032    return __async(this, null, function* () {
1033      try {
1034        yield this.storageService.set(QUEUE_STORAGE_KEY, JSON.stringify(this.queue$.value));
1035        this.updateStateService();
1036      } catch (error) {
1037        console.error("[ProblemReportQueue] Failed to save queue:", error);
1038      }
1039    });
1040  }
1041  /**
1042   * Update the state service with queue info.
1043   */
1044  updateStateService() {
1045    this.stateService.updateQueueStatus(this.queue$.value.length);
1046  }
1047  /**
1048   * Get the current queue as observable.
1049   */
1050  get queue() {
1051    return this.queue$.asObservable();
1052  }
1053  /**
1054   * Get the current queue count.
1055   */
1056  get queueCount() {
1057    return this.queue$.value.length;
1058  }
1059  /**
1060   * Check if currently online.
1061   */
1062  get isOnline() {
1063    return this.isOnline$.value;
1064  }
1065  /**
1066   * Check if currently syncing.
1067   */
1068  get isSyncing() {
1069    return this.isSyncing$.value;
1070  }
1071  /**
1072   * Check if queue is full.
1073   */
1074  get isQueueFull() {
1075    return this.queue$.value.length >= MAX_QUEUE_SIZE;
1076  }
1077  /**
1078   * Add a report to the offline queue.
1079   * Returns true if added successfully, false if queue is full.
1080   */
1081  addToQueue(title, description, category, severity, campusId, photoBase64, locationNodeId, buildingId, floorId, locationX, locationY, gpsLatitude, gpsLongitude) {
1082    return __async(this, null, function* () {
1083      if (this.isQueueFull) {
1084        console.warn("[ProblemReportQueue] Queue is full, cannot add report");
1085        return false;
1086      }
1087      const offlineReport = {
1088        localId: this.generateLocalId(),
1089        photoBase64,
1090        title,
1091        description,
1092        category,
1093        severity,
1094        locationNodeId,
1095        campusId,
1096        buildingId,
1097        floorId,
1098        locationX,
1099        locationY,
1100        gpsLatitude,
1101        gpsLongitude,
1102        createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1103        retryCount: 0
1104      };
1105      const currentQueue = this.queue$.value;
1106      this.queue$.next([...currentQueue, offlineReport]);
1107      yield this.saveQueue();
1108      console.log("[ProblemReportQueue] Report added to queue:", offlineReport.localId);
1109      return true;
1110    });
1111  }
1112  /**
1113   * Remove a report from the queue.
1114   */
1115  removeFromQueue(localId) {
1116    return __async(this, null, function* () {
1117      const currentQueue = this.queue$.value;
1118      const filtered = currentQueue.filter((r) => r.localId !== localId);
1119      this.queue$.next(filtered);
1120      yield this.saveQueue();
1121    });
1122  }
1123  /**
1124   * Process the queue - attempt to submit all pending reports.
1125   */
1126  processQueue() {
1127    return __async(this, null, function* () {
1128      if (this.isSyncing$.value) {
1129        console.log("[ProblemReportQueue] Already syncing, skipping");
1130        return;
1131      }
1132      if (!this.isOnline$.value) {
1133        console.log("[ProblemReportQueue] Offline, skipping sync");
1134        return;
1135      }
1136      const queue = this.queue$.value;
1137      if (queue.length === 0) {
1138        console.log("[ProblemReportQueue] Queue empty, nothing to sync");
1139        return;
1140      }
1141      this.isSyncing$.next(true);
1142      console.log(`[ProblemReportQueue] Processing ${queue.length} queued reports...`);
1143      from(queue).pipe(concatMap((report) => this.submitQueuedReport(report).pipe(
1144        delay(500)
1145        // Small delay between submissions
1146      )), takeUntil(this.destroy$)).subscribe({
1147        complete: () => {
1148          this.isSyncing$.next(false);
1149          console.log("[ProblemReportQueue] Queue processing complete");
1150        },
1151        error: (error) => {
1152          this.isSyncing$.next(false);
1153          console.error("[ProblemReportQueue] Queue processing error:", error);
1154        }
1155      });
1156    });
1157  }
1158  /**
1159   * Submit a single queued report.
1160   */
1161  submitQueuedReport(report) {
1162    if (report.photoBase64) {
1163      return this.apiService.submitProblemReportWithPhoto(report.title, report.description || void 0, report.category, report.severity, report.campusId, report.photoBase64, report.locationNodeId, report.buildingId, report.floorId, report.locationX, report.locationY, report.gpsLatitude, report.gpsLongitude).pipe(tap(() => __async(this, null, function* () {
1164        console.log(`[ProblemReportQueue] Report ${report.localId} submitted successfully`);
1165        yield this.removeFromQueue(report.localId);
1166      })), catchError((error) => __async(this, null, function* () {
1167        console.error(`[ProblemReportQueue] Failed to submit report ${report.localId}:`, error);
1168        report.retryCount++;
1169        if (report.retryCount >= MAX_RETRY_COUNT) {
1170          console.warn(`[ProblemReportQueue] Report ${report.localId} exceeded max retries, removing`);
1171          yield this.removeFromQueue(report.localId);
1172        } else {
1173          yield this.updateReportInQueue(report);
1174        }
1175        return of(null);
1176      })));
1177    } else {
1178      return this.apiService.submitProblemReport(report.title, report.description || void 0, report.category, report.severity, report.campusId, report.locationNodeId, report.buildingId, report.floorId, report.locationX, report.locationY, report.gpsLatitude, report.gpsLongitude).pipe(tap(() => __async(this, null, function* () {
1179        console.log(`[ProblemReportQueue] Report ${report.localId} submitted successfully`);
1180        yield this.removeFromQueue(report.localId);
1181      })), catchError((error) => __async(this, null, function* () {
1182        console.error(`[ProblemReportQueue] Failed to submit report ${report.localId}:`, error);
1183        report.retryCount++;
1184        if (report.retryCount >= MAX_RETRY_COUNT) {
1185          console.warn(`[ProblemReportQueue] Report ${report.localId} exceeded max retries, removing`);
1186          yield this.removeFromQueue(report.localId);
1187        } else {
1188          yield this.updateReportInQueue(report);
1189        }
1190        return of(null);
1191      })));
1192    }
1193  }
1194  /**
1195   * Update a report in the queue (for retry count updates).
1196   */
1197  updateReportInQueue(report) {
1198    return __async(this, null, function* () {
1199      const currentQueue = this.queue$.value;
1200      const index = currentQueue.findIndex((r) => r.localId === report.localId);
1201      if (index >= 0) {
1202        currentQueue[index] = report;
1203        this.queue$.next([...currentQueue]);
1204        yield this.saveQueue();
1205      }
1206    });
1207  }
1208  /**
1209   * Generate a unique local ID for offline reports.
1210   */
1211  generateLocalId() {
1212    return `local_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
1213  }
1214  /**
1215   * Clear the entire queue (use with caution).
1216   */
1217  clearQueue() {
1218    return __async(this, null, function* () {
1219      this.queue$.next([]);
1220      yield this.saveQueue();
1221      console.log("[ProblemReportQueue] Queue cleared");
1222    });
1223  }
1224  /**
1225   * Force a sync attempt (manual trigger).
1226   */
1227  forceSync() {
1228    return __async(this, null, function* () {
1229      const status = yield Network.getStatus();
1230      this.isOnline$.next(status.connected);
1231      if (status.connected) {
1232        yield this.processQueue();
1233      }
1234    });
1235  }
1236};
1237_ProblemReportQueueService.\u0275fac = function ProblemReportQueueService_Factory(__ngFactoryType__) {
1238  return new (__ngFactoryType__ || _ProblemReportQueueService)(\u0275\u0275inject(StorageService), \u0275\u0275inject(ApiService), \u0275\u0275inject(ProblemReportStateService));
1239};
1240_ProblemReportQueueService.\u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _ProblemReportQueueService, factory: _ProblemReportQueueService.\u0275fac, providedIn: "root" });
1241var ProblemReportQueueService = _ProblemReportQueueService;
1242(() => {
1243  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ProblemReportQueueService, [{
1244    type: Injectable,
1245    args: [{
1246      providedIn: "root"
1247    }]
1248  }], () => [{ type: StorageService }, { type: ApiService }, { type: ProblemReportStateService }], null);
1249})();
1250
1251// src/app/pages/problem-report/problem-report.page.ts
1252var _c0 = (a0) => ({ count: a0 });
1253var _forTrack0 = ($index, $item) => $item.value;
1254function ProblemReportPage_Conditional_9_Template(rf, ctx) {
1255  if (rf & 1) {
1256    \u0275\u0275elementStart(0, "div", 3);
1257    \u0275\u0275element(1, "ng-icon", 23);
1258    \u0275\u0275elementStart(2, "span");
1259    \u0275\u0275text(3);
1260    \u0275\u0275pipe(4, "translate");
1261    \u0275\u0275elementEnd()();
1262  }
1263  if (rf & 2) {
1264    \u0275\u0275advance(3);
1265    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(4, 1, "problemReport.offline.message"));
1266  }
1267}
1268function ProblemReportPage_Conditional_10_Template(rf, ctx) {
1269  if (rf & 1) {
1270    \u0275\u0275elementStart(0, "div", 4);
1271    \u0275\u0275element(1, "ng-icon", 24);
1272    \u0275\u0275elementStart(2, "span");
1273    \u0275\u0275text(3);
1274    \u0275\u0275pipe(4, "translate");
1275    \u0275\u0275elementEnd()();
1276  }
1277  if (rf & 2) {
1278    const ctx_r0 = \u0275\u0275nextContext();
1279    \u0275\u0275advance(3);
1280    \u0275\u0275textInterpolate(\u0275\u0275pipeBind2(4, 1, "problemReport.queue.pending", \u0275\u0275pureFunction1(4, _c0, ctx_r0.queuedCount)));
1281  }
1282}
1283function ProblemReportPage_Conditional_25_Template(rf, ctx) {
1284  if (rf & 1) {
1285    \u0275\u0275elementStart(0, "span", 11);
1286    \u0275\u0275text(1);
1287    \u0275\u0275pipe(2, "translate");
1288    \u0275\u0275elementEnd();
1289  }
1290  if (rf & 2) {
1291    \u0275\u0275advance();
1292    \u0275\u0275textInterpolate1("(", \u0275\u0275pipeBind1(2, 1, "problemReport.title.minChars"), ")");
1293  }
1294}
1295function ProblemReportPage_For_33_Template(rf, ctx) {
1296  if (rf & 1) {
1297    \u0275\u0275elementStart(0, "ion-select-option", 13);
1298    \u0275\u0275text(1);
1299    \u0275\u0275pipe(2, "translate");
1300    \u0275\u0275elementEnd();
1301  }
1302  if (rf & 2) {
1303    const cat_r2 = ctx.$implicit;
1304    \u0275\u0275property("value", cat_r2.value);
1305    \u0275\u0275advance();
1306    \u0275\u0275textInterpolate1(" ", \u0275\u0275pipeBind1(2, 2, cat_r2.labelKey), " ");
1307  }
1308}
1309function ProblemReportPage_For_41_Template(rf, ctx) {
1310  if (rf & 1) {
1311    \u0275\u0275elementStart(0, "ion-select-option", 13);
1312    \u0275\u0275text(1);
1313    \u0275\u0275pipe(2, "translate");
1314    \u0275\u0275elementEnd();
1315  }
1316  if (rf & 2) {
1317    const sev_r3 = ctx.$implicit;
1318    \u0275\u0275property("value", sev_r3.value);
1319    \u0275\u0275advance();
1320    \u0275\u0275textInterpolate1(" ", \u0275\u0275pipeBind1(2, 2, sev_r3.labelKey), " ");
1321  }
1322}
1323function ProblemReportPage_Conditional_57_Template(rf, ctx) {
1324  if (rf & 1) {
1325    \u0275\u0275elementStart(0, "div", 19);
1326    \u0275\u0275element(1, "ng-icon", 25);
1327    \u0275\u0275elementStart(2, "span", 26);
1328    \u0275\u0275text(3);
1329    \u0275\u0275elementEnd()();
1330  }
1331  if (rf & 2) {
1332    const ctx_r0 = \u0275\u0275nextContext();
1333    \u0275\u0275advance(3);
1334    \u0275\u0275textInterpolate(ctx_r0.locationDescription);
1335  }
1336}
1337function ProblemReportPage_Conditional_58_Template(rf, ctx) {
1338  if (rf & 1) {
1339    \u0275\u0275elementStart(0, "div", 19);
1340    \u0275\u0275element(1, "ng-icon", 27);
1341    \u0275\u0275elementStart(2, "span", 26);
1342    \u0275\u0275text(3);
1343    \u0275\u0275elementEnd()();
1344  }
1345  if (rf & 2) {
1346    const ctx_r0 = \u0275\u0275nextContext();
1347    \u0275\u0275advance(3);
1348    \u0275\u0275textInterpolate(ctx_r0.campusName);
1349  }
1350}
1351function ProblemReportPage_Conditional_59_Template(rf, ctx) {
1352  if (rf & 1) {
1353    \u0275\u0275elementStart(0, "div", 20);
1354    \u0275\u0275element(1, "ng-icon", 28);
1355    \u0275\u0275elementStart(2, "span", 26);
1356    \u0275\u0275text(3);
1357    \u0275\u0275pipe(4, "translate");
1358    \u0275\u0275elementEnd()();
1359  }
1360  if (rf & 2) {
1361    \u0275\u0275advance(3);
1362    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(4, 1, "problemReport.location.unknown"));
1363  }
1364}
1365function ProblemReportPage_Conditional_62_Template(rf, ctx) {
1366  if (rf & 1) {
1367    \u0275\u0275element(0, "ion-spinner", 29);
1368    \u0275\u0275elementStart(1, "span");
1369    \u0275\u0275text(2);
1370    \u0275\u0275pipe(3, "translate");
1371    \u0275\u0275elementEnd();
1372  }
1373  if (rf & 2) {
1374    \u0275\u0275advance(2);
1375    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(3, 1, "problemReport.button.submitting"));
1376  }
1377}
1378function ProblemReportPage_Conditional_63_Template(rf, ctx) {
1379  if (rf & 1) {
1380    \u0275\u0275element(0, "ng-icon", 30);
1381    \u0275\u0275elementStart(1, "span");
1382    \u0275\u0275text(2);
1383    \u0275\u0275pipe(3, "translate");
1384    \u0275\u0275elementEnd();
1385  }
1386  if (rf & 2) {
1387    \u0275\u0275advance(2);
1388    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(3, 1, "problemReport.button.queue"));
1389  }
1390}
1391function ProblemReportPage_Conditional_64_Template(rf, ctx) {
1392  if (rf & 1) {
1393    \u0275\u0275element(0, "ng-icon", 31);
1394    \u0275\u0275elementStart(1, "span");
1395    \u0275\u0275text(2);
1396    \u0275\u0275pipe(3, "translate");
1397    \u0275\u0275elementEnd();
1398  }
1399  if (rf & 2) {
1400    \u0275\u0275advance(2);
1401    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(3, 1, "problemReport.button.submit"));
1402  }
1403}
1404var _ProblemReportPage = class _ProblemReportPage {
1405  constructor(router, apiService, stateService, queueService, locationService, gpsService, toastService, databaseService, translateService) {
1406    this.router = router;
1407    this.apiService = apiService;
1408    this.stateService = stateService;
1409    this.queueService = queueService;
1410    this.locationService = locationService;
1411    this.gpsService = gpsService;
1412    this.toastService = toastService;
1413    this.databaseService = databaseService;
1414    this.translateService = translateService;
1415    this.destroy$ = new Subject();
1416    this.photoBase64 = null;
1417    this.title = "";
1418    this.description = "";
1419    this.category = "other";
1420    this.severity = "medium";
1421    this.isSubmitting = false;
1422    this.isOnline = true;
1423    this.queuedCount = 0;
1424    this.locationDescription = "";
1425    this.campusId = null;
1426    this.campusName = null;
1427    this.buildingId = null;
1428    this.floorId = null;
1429    this.locationX = null;
1430    this.locationY = null;
1431    this.gpsLatitude = null;
1432    this.gpsLongitude = null;
1433    this.categories = PROBLEM_REPORT_CATEGORIES;
1434    this.severities = PROBLEM_SEVERITIES;
1435  }
1436  ngOnInit() {
1437    this.stateService.state$.pipe(takeUntil(this.destroy$)).subscribe((state) => {
1438      this.isSubmitting = state.isSubmitting;
1439      this.queuedCount = state.queuedReportCount;
1440    });
1441    this.isOnline = this.queueService.isOnline;
1442    this.locationService.beaconLocation$.pipe(takeUntil(this.destroy$)).subscribe((location) => {
1443      if (location) {
1444        this.updateLocationFromBeacon(location);
1445      }
1446    });
1447    this.gpsService.position$.pipe(takeUntil(this.destroy$)).subscribe((gpsPosition) => {
1448      if (gpsPosition) {
1449        this.gpsLatitude = gpsPosition.latitude;
1450        this.gpsLongitude = gpsPosition.longitude;
1451      }
1452    });
1453    this.updateLocationInfo();
1454  }
1455  ngOnDestroy() {
1456    this.destroy$.next();
1457    this.destroy$.complete();
1458  }
1459  updateLocationFromBeacon(location) {
1460    var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1461    const parts = [];
1462    if ((_b = (_a = location.floor) == null ? void 0 : _a.entity) == null ? void 0 : _b.name) {
1463      parts.push(location.floor.entity.name);
1464    }
1465    if ((_d = (_c = location.building) == null ? void 0 : _c.entity) == null ? void 0 : _d.name) {
1466      parts.push(location.building.entity.name);
1467    }
1468    this.locationDescription = parts.join(", ") || "";
1469    if ((_e = location.campus) == null ? void 0 : _e.entity) {
1470      this.campusId = (_f = location.campus.entity.id) != null ? _f : null;
1471      this.campusName = (_g = location.campus.entity.name) != null ? _g : null;
1472    }
1473    if ((_i = (_h = location.building) == null ? void 0 : _h.entity) == null ? void 0 : _i.id) {
1474      this.buildingId = location.building.entity.id;
1475    }
1476    if ((_k = (_j = location.floor) == null ? void 0 : _j.entity) == null ? void 0 : _k.id) {
1477      this.floorId = location.floor.entity.id;
1478    }
1479    this.locationX = location.point.x != null ? Math.round(location.point.x) : null;
1480    this.locationY = location.point.y != null ? Math.round(location.point.y) : null;
1481  }
1482  updateLocationInfo() {
1483    var _a, _b;
1484    const location = this.locationService.currentBeaconLocation;
1485    if (location) {
1486      this.updateLocationFromBeacon(location);
1487    }
1488    const gpsPosition = this.gpsService.currentPosition;
1489    if (gpsPosition) {
1490      this.gpsLatitude = gpsPosition.latitude;
1491      this.gpsLongitude = gpsPosition.longitude;
1492    }
1493    if (!this.campusId) {
1494      const campuses = Array.from(this.databaseService.Models.values()).filter((m) => m.type === "CAMPUS");
1495      if (campuses.length > 0) {
1496        this.campusId = (_a = campuses[0].entity.id) != null ? _a : null;
1497        this.campusName = (_b = campuses[0].entity.name) != null ? _b : null;
1498      }
1499    }
1500  }
1501  onPhotoCapture(photoBase64) {
1502    this.photoBase64 = photoBase64;
1503  }
1504  onPhotoRemove() {
1505    this.photoBase64 = null;
1506  }
1507  get isFormValid() {
1508    return this.title.trim().length >= 5 && !!this.campusId;
1509  }
1510  get titleLength() {
1511    return this.title.length;
1512  }
1513  get descriptionLength() {
1514    return this.description.length;
1515  }
1516  submit() {
1517    return __async(this, null, function* () {
1518      var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
1519      if (!this.isFormValid || this.isSubmitting)
1520        return;
1521      if (!this.campusId) {
1522        this.toastService.showToast({
1523          message: this.translateService.instant("problemReport.error.noCampus"),
1524          type: "error"
1525        });
1526        return;
1527      }
1528      this.stateService.setSubmitting(true);
1529      try {
1530        if (this.queueService.isOnline) {
1531          if (this.photoBase64) {
1532            yield firstValueFrom(this.apiService.submitProblemReportWithPhoto(this.title.trim(), this.description.trim() || void 0, this.category, this.severity, this.campusId, this.photoBase64, void 0, (_a = this.buildingId) != null ? _a : void 0, (_b = this.floorId) != null ? _b : void 0, (_c = this.locationX) != null ? _c : void 0, (_d = this.locationY) != null ? _d : void 0, (_e = this.gpsLatitude) != null ? _e : void 0, (_f = this.gpsLongitude) != null ? _f : void 0));
1533          } else {
1534            yield firstValueFrom(this.apiService.submitProblemReport(this.title.trim(), this.description.trim() || void 0, this.category, this.severity, this.campusId, void 0, (_g = this.buildingId) != null ? _g : void 0, (_h = this.floorId) != null ? _h : void 0, (_i = this.locationX) != null ? _i : void 0, (_j = this.locationY) != null ? _j : void 0, (_k = this.gpsLatitude) != null ? _k : void 0, (_l = this.gpsLongitude) != null ? _l : void 0));
1535          }
1536          this.handleSuccess("problemReport.toast.submitSuccess");
1537        } else {
1538          yield this.queueReport();
1539        }
1540      } catch (error) {
1541        console.error("Submit error:", error);
1542        const httpError = error;
1543        if (httpError.status && httpError.status >= 400 && httpError.status < 500) {
1544          this.handleError(error, "problemReport.toast.validationError");
1545        } else if (httpError.status && httpError.status >= 500) {
1546          this.handleError(error, "problemReport.toast.serverError");
1547        } else {
1548          yield this.queueReportAsFallback();
1549        }
1550      } finally {
1551        this.stateService.setSubmitting(false);
1552      }
1553    });
1554  }
1555  queueReport() {
1556    return __async(this, null, function* () {
1557      var _a, _b, _c, _d, _e, _f, _g;
1558      if (!this.campusId)
1559        return;
1560      const success = yield this.queueService.addToQueue(this.title.trim(), this.description.trim(), this.category, this.severity, this.campusId, (_a = this.photoBase64) != null ? _a : void 0, void 0, (_b = this.buildingId) != null ? _b : void 0, (_c = this.floorId) != null ? _c : void 0, (_d = this.locationX) != null ? _d : void 0, (_e = this.locationY) != null ? _e : void 0, (_f = this.gpsLatitude) != null ? _f : void 0, (_g = this.gpsLongitude) != null ? _g : void 0);
1561      if (success) {
1562        this.handleSuccess("problemReport.toast.queueSuccess");
1563      } else {
1564        this.toastService.showToast({
1565          message: this.translateService.instant("problemReport.toast.queueFull"),
1566          type: "error"
1567        });
1568      }
1569    });
1570  }
1571  queueReportAsFallback() {
1572    return __async(this, null, function* () {
1573      var _a, _b, _c, _d, _e, _f, _g;
1574      if (!this.campusId) {
1575        this.handleError(new Error("No campus"), "problemReport.error.noCampus");
1576        return;
1577      }
1578      const success = yield this.queueService.addToQueue(this.title.trim(), this.description.trim(), this.category, this.severity, this.campusId, (_a = this.photoBase64) != null ? _a : void 0, void 0, (_b = this.buildingId) != null ? _b : void 0, (_c = this.floorId) != null ? _c : void 0, (_d = this.locationX) != null ? _d : void 0, (_e = this.locationY) != null ? _e : void 0, (_f = this.gpsLatitude) != null ? _f : void 0, (_g = this.gpsLongitude) != null ? _g : void 0);
1579      if (success) {
1580        this.handleSuccess("problemReport.toast.queuedAfterFail");
1581      } else {
1582        this.handleError(new Error("Queue failed"), "problemReport.toast.submitError");
1583      }
1584    });
1585  }
1586  handleSuccess(toastKey) {
1587    this.stateService.setSubmitSuccess();
1588    this.stateService.refreshTrigger.emit();
1589    this.toastService.showToast({
1590      message: this.translateService.instant(toastKey),
1591      type: "success"
1592    });
1593    this.resetForm();
1594    this.router.navigate(["/navigation"]);
1595  }
1596  handleError(error, toastKey) {
1597    const errorMessage = error instanceof Error ? error.message : "Submission failed";
1598    console.error("Submit error:", errorMessage);
1599    this.stateService.setSubmitError(errorMessage);
1600    this.toastService.showToast({
1601      message: this.translateService.instant(toastKey),
1602      type: "error"
1603    });
1604  }
1605  resetForm() {
1606    this.photoBase64 = null;
1607    this.title = "";
1608    this.description = "";
1609    this.category = "other";
1610    this.severity = "medium";
1611    this.buildingId = null;
1612    this.floorId = null;
1613    this.locationX = null;
1614    this.locationY = null;
1615    this.gpsLatitude = null;
1616    this.gpsLongitude = null;
1617  }
1618  getCategoryIcon(category) {
1619    var _a;
1620    const option = this.categories.find((c) => c.value === category);
1621    return (_a = option == null ? void 0 : option.icon) != null ? _a : "lucideCircleHelp";
1622  }
1623  getSeverityColor(severity) {
1624    var _a;
1625    const option = this.severities.find((s) => s.value === severity);
1626    return (_a = option == null ? void 0 : option.color) != null ? _a : "medium";
1627  }
1628};
1629_ProblemReportPage.\u0275fac = function ProblemReportPage_Factory(__ngFactoryType__) {
1630  return new (__ngFactoryType__ || _ProblemReportPage)(\u0275\u0275directiveInject(Router), \u0275\u0275directiveInject(ApiService), \u0275\u0275directiveInject(ProblemReportStateService), \u0275\u0275directiveInject(ProblemReportQueueService), \u0275\u0275directiveInject(LocationService), \u0275\u0275directiveInject(GPSService), \u0275\u0275directiveInject(ToastService), \u0275\u0275directiveInject(DatabaseService), \u0275\u0275directiveInject(TranslateService));
1631};
1632_ProblemReportPage.\u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _ProblemReportPage, selectors: [["app-problem-report"]], features: [\u0275\u0275ProvidersFeature([], [provideIcons({
1633  lucideMapPin,
1634  lucideSend,
1635  lucideWifi,
1636  lucideWifiOff,
1637  lucideZap,
1638  lucideDroplets,
1639  lucideBuilding,
1640  lucideAccessibility,
1641  lucideTriangleAlert,
1642  lucideSparkles,
1643  lucideCircleHelp
1644})])], decls: 65, vars: 60, consts: [["slot", "start"], ["defaultHref", "/navigation"], [1, "content-container"], [1, "offline-banner"], [1, "queued-banner"], [1, "report-form", 3, "ngSubmit"], [1, "form-section"], [1, "form-label"], [3, "photoCapture", "photoRemove", "currentPhoto"], ["name", "title", 1, "title-textarea", 3, "ngModelChange", "ngModel", "placeholder", "maxlength", "rows", "disabled", "autoGrow"], [1, "char-count"], [1, "min-hint"], ["name", "category", "interface", "action-sheet", 1, "category-select", 3, "ngModelChange", "ngModel", "placeholder", "disabled"], [3, "value"], ["name", "severity", "interface", "action-sheet", 1, "severity-select", 3, "ngModelChange", "ngModel", "placeholder", "disabled"], ["name", "description", 1, "description-textarea", 3, "ngModelChange", "ngModel", "placeholder", "maxlength", "rows", "disabled", "autoGrow"], [1, "form-section", "location-section"], [1, "location-card"], [1, "location-info"], [1, "location-item"], [1, "location-item", "unknown"], [1, "submit-section"], ["type", "submit", "expand", "block", 1, "submit-button", 3, "disabled"], ["name", "lucideWifiOff", "size", "1em"], ["name", "lucideWifi", "size", "1em"], ["name", "lucideMapPin", "size", "1em"], [1, "location-value"], ["name", "lucideBuilding", "size", "1em"], ["name", "lucideCircleHelp", "size", "1em"], ["name", "crescent", "slot", "start"], ["name", "lucideWifiOff", "slot", "start"], ["name", "lucideSend", "slot", "start"]], template: function ProblemReportPage_Template(rf, ctx) {
1645  if (rf & 1) {
1646    \u0275\u0275elementStart(0, "ion-header")(1, "ion-toolbar")(2, "ion-buttons", 0);
1647    \u0275\u0275element(3, "ion-back-button", 1);
1648    \u0275\u0275elementEnd();
1649    \u0275\u0275elementStart(4, "ion-title");
1650    \u0275\u0275text(5);
1651    \u0275\u0275pipe(6, "translate");
1652    \u0275\u0275elementEnd()()();
1653    \u0275\u0275elementStart(7, "ion-content")(8, "div", 2);
1654    \u0275\u0275template(9, ProblemReportPage_Conditional_9_Template, 5, 3, "div", 3)(10, ProblemReportPage_Conditional_10_Template, 5, 6, "div", 4);
1655    \u0275\u0275elementStart(11, "form", 5);
1656    \u0275\u0275listener("ngSubmit", function ProblemReportPage_Template_form_ngSubmit_11_listener() {
1657      return ctx.submit();
1658    });
1659    \u0275\u0275elementStart(12, "div", 6)(13, "label", 7);
1660    \u0275\u0275text(14);
1661    \u0275\u0275pipe(15, "translate");
1662    \u0275\u0275elementEnd();
1663    \u0275\u0275elementStart(16, "app-photo-capture", 8);
1664    \u0275\u0275listener("photoCapture", function ProblemReportPage_Template_app_photo_capture_photoCapture_16_listener($event) {
1665      return ctx.onPhotoCapture($event);
1666    })("photoRemove", function ProblemReportPage_Template_app_photo_capture_photoRemove_16_listener() {
1667      return ctx.onPhotoRemove();
1668    });
1669    \u0275\u0275elementEnd()();
1670    \u0275\u0275elementStart(17, "div", 6)(18, "label", 7);
1671    \u0275\u0275text(19);
1672    \u0275\u0275pipe(20, "translate");
1673    \u0275\u0275elementEnd();
1674    \u0275\u0275elementStart(21, "ion-textarea", 9);
1675    \u0275\u0275pipe(22, "translate");
1676    \u0275\u0275twoWayListener("ngModelChange", function ProblemReportPage_Template_ion_textarea_ngModelChange_21_listener($event) {
1677      \u0275\u0275twoWayBindingSet(ctx.title, $event) || (ctx.title = $event);
1678      return $event;
1679    });
1680    \u0275\u0275elementEnd();
1681    \u0275\u0275elementStart(23, "div", 10);
1682    \u0275\u0275text(24);
1683    \u0275\u0275template(25, ProblemReportPage_Conditional_25_Template, 3, 3, "span", 11);
1684    \u0275\u0275elementEnd()();
1685    \u0275\u0275elementStart(26, "div", 6)(27, "label", 7);
1686    \u0275\u0275text(28);
1687    \u0275\u0275pipe(29, "translate");
1688    \u0275\u0275elementEnd();
1689    \u0275\u0275elementStart(30, "ion-select", 12);
1690    \u0275\u0275pipe(31, "translate");
1691    \u0275\u0275twoWayListener("ngModelChange", function ProblemReportPage_Template_ion_select_ngModelChange_30_listener($event) {
1692      \u0275\u0275twoWayBindingSet(ctx.category, $event) || (ctx.category = $event);
1693      return $event;
1694    });
1695    \u0275\u0275repeaterCreate(32, ProblemReportPage_For_33_Template, 3, 4, "ion-select-option", 13, _forTrack0);
1696    \u0275\u0275elementEnd()();
1697    \u0275\u0275elementStart(34, "div", 6)(35, "label", 7);
1698    \u0275\u0275text(36);
1699    \u0275\u0275pipe(37, "translate");
1700    \u0275\u0275elementEnd();
1701    \u0275\u0275elementStart(38, "ion-select", 14);
1702    \u0275\u0275pipe(39, "translate");
1703    \u0275\u0275twoWayListener("ngModelChange", function ProblemReportPage_Template_ion_select_ngModelChange_38_listener($event) {
1704      \u0275\u0275twoWayBindingSet(ctx.severity, $event) || (ctx.severity = $event);
1705      return $event;
1706    });
1707    \u0275\u0275repeaterCreate(40, ProblemReportPage_For_41_Template, 3, 4, "ion-select-option", 13, _forTrack0);
1708    \u0275\u0275elementEnd()();
1709    \u0275\u0275elementStart(42, "div", 6)(43, "label", 7);
1710    \u0275\u0275text(44);
1711    \u0275\u0275pipe(45, "translate");
1712    \u0275\u0275elementEnd();
1713    \u0275\u0275elementStart(46, "ion-textarea", 15);
1714    \u0275\u0275pipe(47, "translate");
1715    \u0275\u0275twoWayListener("ngModelChange", function ProblemReportPage_Template_ion_textarea_ngModelChange_46_listener($event) {
1716      \u0275\u0275twoWayBindingSet(ctx.description, $event) || (ctx.description = $event);
1717      return $event;
1718    });
1719    \u0275\u0275elementEnd();
1720    \u0275\u0275elementStart(48, "div", 10);
1721    \u0275\u0275text(49);
1722    \u0275\u0275elementEnd()();
1723    \u0275\u0275elementStart(50, "div", 16)(51, "label", 7);
1724    \u0275\u0275text(52);
1725    \u0275\u0275pipe(53, "translate");
1726    \u0275\u0275elementEnd();
1727    \u0275\u0275elementStart(54, "ion-card", 17)(55, "ion-card-content")(56, "div", 18);
1728    \u0275\u0275template(57, ProblemReportPage_Conditional_57_Template, 4, 1, "div", 19)(58, ProblemReportPage_Conditional_58_Template, 4, 1, "div", 19)(59, ProblemReportPage_Conditional_59_Template, 5, 3, "div", 20);
1729    \u0275\u0275elementEnd()()()();
1730    \u0275\u0275elementStart(60, "div", 21)(61, "ion-button", 22);
1731    \u0275\u0275template(62, ProblemReportPage_Conditional_62_Template, 4, 3)(63, ProblemReportPage_Conditional_63_Template, 4, 3)(64, ProblemReportPage_Conditional_64_Template, 4, 3);
1732    \u0275\u0275elementEnd()()()()();
1733  }
1734  if (rf & 2) {
1735    \u0275\u0275advance(5);
1736    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(6, 38, "problemReport.header"));
1737    \u0275\u0275advance(4);
1738    \u0275\u0275conditional(!ctx.isOnline ? 9 : -1);
1739    \u0275\u0275advance();
1740    \u0275\u0275conditional(ctx.queuedCount > 0 ? 10 : -1);
1741    \u0275\u0275advance(4);
1742    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(15, 40, "problemReport.photo.label"));
1743    \u0275\u0275advance(2);
1744    \u0275\u0275property("currentPhoto", ctx.photoBase64);
1745    \u0275\u0275advance(3);
1746    \u0275\u0275textInterpolate1("", \u0275\u0275pipeBind1(20, 42, "problemReport.title.label"), " *");
1747    \u0275\u0275advance(2);
1748    \u0275\u0275twoWayProperty("ngModel", ctx.title);
1749    \u0275\u0275property("placeholder", \u0275\u0275pipeBind1(22, 44, "problemReport.title.placeholder"))("maxlength", 200)("rows", 2)("disabled", ctx.isSubmitting)("autoGrow", true);
1750    \u0275\u0275advance(2);
1751    \u0275\u0275classProp("warning", ctx.titleLength < 5);
1752    \u0275\u0275advance();
1753    \u0275\u0275textInterpolate1(" ", ctx.titleLength, "/200 ");
1754    \u0275\u0275advance();
1755    \u0275\u0275conditional(ctx.titleLength > 0 && ctx.titleLength < 5 ? 25 : -1);
1756    \u0275\u0275advance(3);
1757    \u0275\u0275textInterpolate1("", \u0275\u0275pipeBind1(29, 46, "problemReport.category.label"), " *");
1758    \u0275\u0275advance(2);
1759    \u0275\u0275twoWayProperty("ngModel", ctx.category);
1760    \u0275\u0275property("placeholder", \u0275\u0275pipeBind1(31, 48, "problemReport.category.placeholder"))("disabled", ctx.isSubmitting);
1761    \u0275\u0275advance(2);
1762    \u0275\u0275repeater(ctx.categories);
1763    \u0275\u0275advance(4);
1764    \u0275\u0275textInterpolate1("", \u0275\u0275pipeBind1(37, 50, "problemReport.severity.label"), " *");
1765    \u0275\u0275advance(2);
1766    \u0275\u0275twoWayProperty("ngModel", ctx.severity);
1767    \u0275\u0275property("placeholder", \u0275\u0275pipeBind1(39, 52, "problemReport.severity.placeholder"))("disabled", ctx.isSubmitting);
1768    \u0275\u0275advance(2);
1769    \u0275\u0275repeater(ctx.severities);
1770    \u0275\u0275advance(4);
1771    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(45, 54, "problemReport.description.label"));
1772    \u0275\u0275advance(2);
1773    \u0275\u0275twoWayProperty("ngModel", ctx.description);
1774    \u0275\u0275property("placeholder", \u0275\u0275pipeBind1(47, 56, "problemReport.description.placeholder"))("maxlength", 2e3)("rows", 4)("disabled", ctx.isSubmitting)("autoGrow", true);
1775    \u0275\u0275advance(3);
1776    \u0275\u0275textInterpolate1(" ", ctx.descriptionLength, "/2000 ");
1777    \u0275\u0275advance(3);
1778    \u0275\u0275textInterpolate(\u0275\u0275pipeBind1(53, 58, "problemReport.location.label"));
1779    \u0275\u0275advance(5);
1780    \u0275\u0275conditional(ctx.locationDescription ? 57 : -1);
1781    \u0275\u0275advance();
1782    \u0275\u0275conditional(ctx.campusName ? 58 : -1);
1783    \u0275\u0275advance();
1784    \u0275\u0275conditional(!ctx.locationDescription && !ctx.campusName ? 59 : -1);
1785    \u0275\u0275advance(2);
1786    \u0275\u0275property("disabled", !ctx.isFormValid || ctx.isSubmitting);
1787    \u0275\u0275advance();
1788    \u0275\u0275conditional(ctx.isSubmitting ? 62 : !ctx.isOnline ? 63 : 64);
1789  }
1790}, dependencies: [
1791  FormsModule,
1792  \u0275NgNoValidate,
1793  NgControlStatus,
1794  NgControlStatusGroup,
1795  MaxLengthValidator,
1796  NgModel,
1797  NgForm,
1798  TranslateModule,
1799  TranslatePipe,
1800  NgIcon,
1801  PhotoCaptureComponent,
1802  IonHeader,
1803  IonToolbar,
1804  IonButtons,
1805  IonBackButton,
1806  IonTitle,
1807  IonContent,
1808  IonButton,
1809  IonSelect,
1810  IonSelectOption,
1811  IonTextarea,
1812  IonSpinner,
1813  IonCard,
1814  IonCardContent
1815], styles: ["\n\nion-content[_ngcontent-%COMP%] {\n  --padding-start: 16px;\n  --padding-end: 16px;\n  --padding-top: 16px;\n  --padding-bottom: 16px;\n}\nion-content[_ngcontent-%COMP%]::part(scroll) {\n  overflow-x: hidden;\n}\n.content-container[_ngcontent-%COMP%] {\n  max-width: 600px;\n  margin: 0 auto;\n  padding: 1rem;\n}\n.report-form[_ngcontent-%COMP%] {\n  background: var(--ion-item-background);\n  border-radius: 12px;\n  padding: 1.5rem;\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n.report-form[_ngcontent-%COMP%]   .form-section[_ngcontent-%COMP%]:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n.offline-banner[_ngcontent-%COMP%], \n.queued-banner[_ngcontent-%COMP%] {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 12px 16px;\n  border-radius: 8px;\n  margin-bottom: 16px;\n  font-size: 14px;\n  font-weight: 500;\n}\n.offline-banner[_ngcontent-%COMP%]   ng-icon[_ngcontent-%COMP%], \n.queued-banner[_ngcontent-%COMP%]   ng-icon[_ngcontent-%COMP%] {\n  flex-shrink: 0;\n}\n.offline-banner[_ngcontent-%COMP%] {\n  background: var(--ion-color-warning);\n  color: var(--ion-color-warning-contrast);\n}\n.queued-banner[_ngcontent-%COMP%] {\n  background: var(--ion-color-info);\n  color: var(--ion-color-info-contrast);\n}\n.report-form[_ngcontent-%COMP%] {\n  display: flex;\n  flex-direction: column;\n  gap: 20px;\n}\n.form-section[_ngcontent-%COMP%] {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n.form-section[_ngcontent-%COMP%]   .form-label[_ngcontent-%COMP%] {\n  font-size: 14px;\n  font-weight: 600;\n  color: var(--ion-color-medium);\n}\n.form-section[_ngcontent-%COMP%]   ion-textarea[_ngcontent-%COMP%] {\n  --padding-top: 12px;\n  --padding-bottom: 12px;\n  --padding-start: 12px;\n  --padding-end: 12px;\n  --border-radius: 8px;\n  --background: var(--ion-color-light);\n  --border-color: var(--ion-color-step-150);\n  border: 1px solid var(--ion-color-step-200);\n}\n.form-section[_ngcontent-%COMP%]   ion-textarea.title-textarea[_ngcontent-%COMP%] {\n  min-height: 60px;\n}\n.form-section[_ngcontent-%COMP%]   ion-textarea.description-textarea[_ngcontent-%COMP%] {\n  min-height: 100px;\n}\n.form-section[_ngcontent-%COMP%]   ion-select[_ngcontent-%COMP%] {\n  --padding-start: 12px;\n  --padding-end: 12px;\n  --min-height: 44px;\n  --border-radius: 8px;\n  --background: var(--ion-color-light);\n  --border-color: var(--ion-color-step-200);\n  border: 1px solid var(--ion-color-step-200);\n}\n.form-section[_ngcontent-%COMP%]   ion-select.category-select[_ngcontent-%COMP%], \n.form-section[_ngcontent-%COMP%]   ion-select.severity-select[_ngcontent-%COMP%] {\n  min-height: 44px;\n}\n.form-section[_ngcontent-%COMP%]   .char-count[_ngcontent-%COMP%] {\n  font-size: 12px;\n  color: var(--ion-color-medium);\n  text-align: right;\n}\n.form-section[_ngcontent-%COMP%]   .char-count.warning[_ngcontent-%COMP%] {\n  color: var(--ion-color-warning);\n}\n.form-section[_ngcontent-%COMP%]   .char-count[_ngcontent-%COMP%]   .min-hint[_ngcontent-%COMP%] {\n  color: var(--ion-color-danger);\n}\n.location-section[_ngcontent-%COMP%]   .location-card[_ngcontent-%COMP%] {\n  --background: var(--ion-color-light);\n  --border-radius: 8px;\n  border: 1px solid var(--ion-color-step-200);\n}\n.location-section[_ngcontent-%COMP%]   .location-card[_ngcontent-%COMP%]   ion-card-content[_ngcontent-%COMP%] {\n  padding: 12px !important;\n}\n.location-section[_ngcontent-%COMP%]   .location-card[_ngcontent-%COMP%]   .location-info[_ngcontent-%COMP%] {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n.location-section[_ngcontent-%COMP%]   .location-card[_ngcontent-%COMP%]   .location-info[_ngcontent-%COMP%]   .location-item[_ngcontent-%COMP%] {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 14px;\n}\n.location-section[_ngcontent-%COMP%]   .location-card[_ngcontent-%COMP%]   .location-info[_ngcontent-%COMP%]   .location-item[_ngcontent-%COMP%]   ng-icon[_ngcontent-%COMP%] {\n  flex-shrink: 0;\n  color: var(--ion-color-medium);\n}\n.location-section[_ngcontent-%COMP%]   .location-card[_ngcontent-%COMP%]   .location-info[_ngcontent-%COMP%]   .location-item[_ngcontent-%COMP%]   .location-value[_ngcontent-%COMP%] {\n  color: var(--ion-color-dark);\n  font-weight: 500;\n}\n.location-section[_ngcontent-%COMP%]   .location-card[_ngcontent-%COMP%]   .location-info[_ngcontent-%COMP%]   .location-item.gps-location[_ngcontent-%COMP%]   .location-value[_ngcontent-%COMP%] {\n  font-family: monospace;\n  font-size: 13px;\n}\n.location-section[_ngcontent-%COMP%]   .location-card[_ngcontent-%COMP%]   .location-info[_ngcontent-%COMP%]   .location-item.unknown[_ngcontent-%COMP%]   .location-value[_ngcontent-%COMP%] {\n  color: var(--ion-color-medium);\n  font-style: italic;\n}\n.submit-section[_ngcontent-%COMP%] {\n  margin-top: 8px;\n  margin-bottom: 16px;\n}\n.submit-section[_ngcontent-%COMP%]   .submit-button[_ngcontent-%COMP%] {\n  --border-radius: 8px;\n  --height: 48px;\n  font-weight: 600;\n  text-transform: none;\n}\n.submit-section[_ngcontent-%COMP%]   .submit-button[_ngcontent-%COMP%]   ion-spinner[_ngcontent-%COMP%] {\n  color: currentColor;\n}"] });
1816var ProblemReportPage = _ProblemReportPage;
1817(() => {
1818  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ProblemReportPage, [{
1819    type: Component,
1820    args: [{ selector: "app-problem-report", imports: [
1821      FormsModule,
1822      TranslateModule,
1823      NgIcon,
1824      PhotoCaptureComponent,
1825      IonHeader,
1826      IonToolbar,
1827      IonButtons,
1828      IonBackButton,
1829      IonTitle,
1830      IonContent,
1831      IonButton,
1832      IonSelect,
1833      IonSelectOption,
1834      IonTextarea,
1835      IonSpinner,
1836      IonCard,
1837      IonCardContent
1838    ], viewProviders: [provideIcons({
1839      lucideMapPin,
1840      lucideSend,
1841      lucideWifi,
1842      lucideWifiOff,
1843      lucideZap,
1844      lucideDroplets,
1845      lucideBuilding,
1846      lucideAccessibility,
1847      lucideTriangleAlert,
1848      lucideSparkles,
1849      lucideCircleHelp
1850    })], template: `<ion-header>
1851  <ion-toolbar>
1852    <ion-buttons slot="start">
1853      <ion-back-button defaultHref="/navigation" />
1854    </ion-buttons>
1855    <ion-title>{{ 'problemReport.header' | translate }}</ion-title>
1856  </ion-toolbar>
1857</ion-header>
1858
1859<ion-content>
1860  <div class="content-container">
1861    <!-- Offline Banner -->
1862    @if (!isOnline) {
1863      <div class="offline-banner">
1864        <ng-icon name="lucideWifiOff" size="1em" />
1865        <span>{{ 'problemReport.offline.message' | translate }}</span>
1866      </div>
1867    }
1868
1869    <!-- Queued Reports Badge -->
1870    @if (queuedCount > 0) {
1871      <div class="queued-banner">
1872        <ng-icon name="lucideWifi" size="1em" />
1873        <span>{{ 'problemReport.queue.pending' | translate: { count: queuedCount } }}</span>
1874      </div>
1875    }
1876
1877    <form class="report-form" (ngSubmit)="submit()">
1878    <!-- Photo Capture -->
1879    <div class="form-section">
1880      <label class="form-label">{{ 'problemReport.photo.label' | translate }}</label>
1881      <app-photo-capture
1882        [currentPhoto]="photoBase64"
1883        (photoCapture)="onPhotoCapture($event)"
1884        (photoRemove)="onPhotoRemove()" />
1885    </div>
1886
1887    <!-- Title -->
1888    <div class="form-section">
1889      <label class="form-label">{{ 'problemReport.title.label' | translate }} *</label>
1890      <ion-textarea
1891        [(ngModel)]="title"
1892        name="title"
1893        [placeholder]="'problemReport.title.placeholder' | translate"
1894        [maxlength]="200"
1895        [rows]="2"
1896        [disabled]="isSubmitting"
1897        [autoGrow]="true"
1898        class="title-textarea">
1899      </ion-textarea>
1900      <div class="char-count" [class.warning]="titleLength < 5">
1901        {{ titleLength }}/200
1902        @if (titleLength > 0 && titleLength < 5) {
1903          <span class="min-hint">({{ 'problemReport.title.minChars' | translate }})</span>
1904        }
1905      </div>
1906    </div>
1907
1908    <!-- Category -->
1909    <div class="form-section">
1910      <label class="form-label">{{ 'problemReport.category.label' | translate }} *</label>
1911      <ion-select
1912        [(ngModel)]="category"
1913        name="category"
1914        interface="action-sheet"
1915        [placeholder]="'problemReport.category.placeholder' | translate"
1916        [disabled]="isSubmitting"
1917        class="category-select">
1918        @for (cat of categories; track cat.value) {
1919          <ion-select-option [value]="cat.value">
1920            {{ cat.labelKey | translate }}
1921          </ion-select-option>
1922        }
1923      </ion-select>
1924    </div>
1925
1926    <!-- Severity -->
1927    <div class="form-section">
1928      <label class="form-label">{{ 'problemReport.severity.label' | translate }} *</label>
1929      <ion-select
1930        [(ngModel)]="severity"
1931        name="severity"
1932        interface="action-sheet"
1933        [placeholder]="'problemReport.severity.placeholder' | translate"
1934        [disabled]="isSubmitting"
1935        class="severity-select">
1936        @for (sev of severities; track sev.value) {
1937          <ion-select-option [value]="sev.value">
1938            {{ sev.labelKey | translate }}
1939          </ion-select-option>
1940        }
1941      </ion-select>
1942    </div>
1943
1944    <!-- Description -->
1945    <div class="form-section">
1946      <label class="form-label">{{ 'problemReport.description.label' | translate }}</label>
1947      <ion-textarea
1948        [(ngModel)]="description"
1949        name="description"
1950        [placeholder]="'problemReport.description.placeholder' | translate"
1951        [maxlength]="2000"
1952        [rows]="4"
1953        [disabled]="isSubmitting"
1954        [autoGrow]="true"
1955        class="description-textarea">
1956      </ion-textarea>
1957      <div class="char-count">
1958        {{ descriptionLength }}/2000
1959      </div>
1960    </div>
1961
1962    <!-- Location Context -->
1963    <div class="form-section location-section">
1964      <label class="form-label">{{ 'problemReport.location.label' | translate }}</label>
1965      <ion-card class="location-card">
1966        <ion-card-content>
1967          <div class="location-info">
1968            @if (locationDescription) {
1969              <div class="location-item">
1970                <ng-icon name="lucideMapPin" size="1em" />
1971                <span class="location-value">{{ locationDescription }}</span>
1972              </div>
1973            }
1974            @if (campusName) {
1975              <div class="location-item">
1976                <ng-icon name="lucideBuilding" size="1em" />
1977                <span class="location-value">{{ campusName }}</span>
1978              </div>
1979            }
1980            @if (!locationDescription && !campusName) {
1981              <div class="location-item unknown">
1982                <ng-icon name="lucideCircleHelp" size="1em" />
1983                <span class="location-value">{{ 'problemReport.location.unknown' | translate }}</span>
1984              </div>
1985            }
1986          </div>
1987        </ion-card-content>
1988      </ion-card>
1989    </div>
1990
1991    <!-- Submit Button -->
1992    <div class="submit-section">
1993      <ion-button
1994        type="submit"
1995        expand="block"
1996        [disabled]="!isFormValid || isSubmitting"
1997        class="submit-button">
1998        @if (isSubmitting) {
1999          <ion-spinner name="crescent" slot="start" />
2000          <span>{{ 'problemReport.button.submitting' | translate }}</span>
2001        } @else if (!isOnline) {
2002          <ng-icon name="lucideWifiOff" slot="start" />
2003          <span>{{ 'problemReport.button.queue' | translate }}</span>
2004        } @else {
2005          <ng-icon name="lucideSend" slot="start" />
2006          <span>{{ 'problemReport.button.submit' | translate }}</span>
2007        }
2008      </ion-button>
2009    </div>
2010    </form>
2011  </div>
2012</ion-content>
2013`, styles: ["/* src/app/pages/problem-report/problem-report.page.scss */\nion-content {\n  --padding-start: 16px;\n  --padding-end: 16px;\n  --padding-top: 16px;\n  --padding-bottom: 16px;\n}\nion-content::part(scroll) {\n  overflow-x: hidden;\n}\n.content-container {\n  max-width: 600px;\n  margin: 0 auto;\n  padding: 1rem;\n}\n.report-form {\n  background: var(--ion-item-background);\n  border-radius: 12px;\n  padding: 1.5rem;\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n.report-form .form-section:not(:last-child) {\n  margin-bottom: 1.5rem;\n}\n.offline-banner,\n.queued-banner {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 12px 16px;\n  border-radius: 8px;\n  margin-bottom: 16px;\n  font-size: 14px;\n  font-weight: 500;\n}\n.offline-banner ng-icon,\n.queued-banner ng-icon {\n  flex-shrink: 0;\n}\n.offline-banner {\n  background: var(--ion-color-warning);\n  color: var(--ion-color-warning-contrast);\n}\n.queued-banner {\n  background: var(--ion-color-info);\n  color: var(--ion-color-info-contrast);\n}\n.report-form {\n  display: flex;\n  flex-direction: column;\n  gap: 20px;\n}\n.form-section {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n.form-section .form-label {\n  font-size: 14px;\n  font-weight: 600;\n  color: var(--ion-color-medium);\n}\n.form-section ion-textarea {\n  --padding-top: 12px;\n  --padding-bottom: 12px;\n  --padding-start: 12px;\n  --padding-end: 12px;\n  --border-radius: 8px;\n  --background: var(--ion-color-light);\n  --border-color: var(--ion-color-step-150);\n  border: 1px solid var(--ion-color-step-200);\n}\n.form-section ion-textarea.title-textarea {\n  min-height: 60px;\n}\n.form-section ion-textarea.description-textarea {\n  min-height: 100px;\n}\n.form-section ion-select {\n  --padding-start: 12px;\n  --padding-end: 12px;\n  --min-height: 44px;\n  --border-radius: 8px;\n  --background: var(--ion-color-light);\n  --border-color: var(--ion-color-step-200);\n  border: 1px solid var(--ion-color-step-200);\n}\n.form-section ion-select.category-select,\n.form-section ion-select.severity-select {\n  min-height: 44px;\n}\n.form-section .char-count {\n  font-size: 12px;\n  color: var(--ion-color-medium);\n  text-align: right;\n}\n.form-section .char-count.warning {\n  color: var(--ion-color-warning);\n}\n.form-section .char-count .min-hint {\n  color: var(--ion-color-danger);\n}\n.location-section .location-card {\n  --background: var(--ion-color-light);\n  --border-radius: 8px;\n  border: 1px solid var(--ion-color-step-200);\n}\n.location-section .location-card ion-card-content {\n  padding: 12px !important;\n}\n.location-section .location-card .location-info {\n  display: flex;\n  flex-direction: column;\n  gap: 8px;\n}\n.location-section .location-card .location-info .location-item {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  font-size: 14px;\n}\n.location-section .location-card .location-info .location-item ng-icon {\n  flex-shrink: 0;\n  color: var(--ion-color-medium);\n}\n.location-section .location-card .location-info .location-item .location-value {\n  color: var(--ion-color-dark);\n  font-weight: 500;\n}\n.location-section .location-card .location-info .location-item.gps-location .location-value {\n  font-family: monospace;\n  font-size: 13px;\n}\n.location-section .location-card .location-info .location-item.unknown .location-value {\n  color: var(--ion-color-medium);\n  font-style: italic;\n}\n.submit-section {\n  margin-top: 8px;\n  margin-bottom: 16px;\n}\n.submit-section .submit-button {\n  --border-radius: 8px;\n  --height: 48px;\n  font-weight: 600;\n  text-transform: none;\n}\n.submit-section .submit-button ion-spinner {\n  color: currentColor;\n}\n"] }]
2014  }], () => [{ type: Router }, { type: ApiService }, { type: ProblemReportStateService }, { type: ProblemReportQueueService }, { type: LocationService }, { type: GPSService }, { type: ToastService }, { type: DatabaseService }, { type: TranslateService }], null);
2015})();
2016(() => {
2017  (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ProblemReportPage, { className: "ProblemReportPage", filePath: "src/app/pages/problem-report/problem-report.page.ts", lineNumber: 54 });
2018})();
2019export {
2020  ProblemReportPage
2021};

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.