Skip to content

Conversation

@lgourdin
Copy link
Contributor

@lgourdin lgourdin commented Dec 10, 2025

Summary by CodeRabbit

  • New Features
    • Introduced Itinevert: a full public-transport planner with multi-step wizard, filters, results view, map mode, pagination, progress/loading and no-results screens.
    • Added Itinevert entry in the main menu and related banner/UI components.
    • New address input with autocomplete, geolocation and profile autofill.
    • Documents can open in a new tab from lists and maps; map display now supports area polygons.
    • New input components: autocomplete, radio groups and a bus icon; updated French translations.

✏️ Tip: You can customize this high-level summary in your review settings.

gerome.perrin added 30 commits November 3, 2025 10:05
… being chosen when navitia isochron threshold reached
gerome.perrin and others added 25 commits November 28, 2025 11:44
@github-actions github-actions bot added the enhancement New feature or request label Dec 10, 2025
(cherry picked from commit c6398ffe7d74202a9f74806fb26f5afda4aba9a0)
@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Walkthrough

Adds the Itinevert feature: multi-step public-transport journey wizard, filtering and result views, map enhancements for area polygons and new tab link behavior, Photon-based address/autocomplete inputs, a new ItinevertService (Navitia integrations), route, UI and router additions, and multiple supporting components and translations.

Changes

Cohort / File(s) Summary
Itinevert feature & views
src/components/itinevert/ItinevertWizard.vue, src/components/itinevert/ItinevertFilterView.vue, src/components/itinevert/ItinevertResultView.vue, src/components/itinevert/ItinevertLoadingView.vue, src/components/itinevert/ItinevertNoResultView.vue, src/components/itinevert/ItinevertBanner.vue, src/components/itinevert/ItinevertFilterItem.vue, src/components/itinevert/ItinevertPageSelector.vue, src/views/portals/ItinevertView.vue
New Itinevert UI: wizard (form/filter/result), filter items, result view with map/list modes and pagination, loading/no-result states, banner and portal view with persistence and navigation integration.
Input controls
src/components/generics/inputs/InputAddress.vue, src/components/generics/inputs/InputAutocomplete.vue, src/components/generics/inputs/InputRadioButton.vue
New reusable inputs: address field with Photon autocomplete, geolocation and reverse geocoding; general autocomplete dropdown; radio-group with per-option disabled reasons.
Datatable & link renderers
src/components/datatable/DocumentsTable.vue, src/components/datatable/cell-renderers/DocumentLinkNewTab.vue
Added openInNewTab prop to DocumentsTable and conditional use of a new DocumentLinkNewTab renderer to open document links in a new tab when enabled.
Map components & utils
src/components/map/OlMap.vue, src/components/map/map-utils.js
OlMap: new openInNewTab prop and click handling to open documents in new tabs; added polygon-area rendering for area documents and imported/added getDocumentPolygonStyle utility for polygon styling.
Service layer
src/js/apis/itinevert-service.js
New ItinevertService: Navitia integrations (coverage, isochrones, reachable routes/waypoints), query builders, pagination helpers (with progress/cancellation), polygon helpers, coordinate projection utilities, and exported constants.
API changes
src/js/apis/photon.js, src/js/apis/transport-service.js
Photon.getPropositions: guard centerWgs84 access to avoid errors; TransportService: added getWaypointsAccessibleTC(routeId).
Constants, router, font-awesome
src/js/constants/index.js, src/js/vue-plugins/router.js, src/js/vue-plugins/font-awesome-config.js
Added categorizedFieldsDefault for filters, new /Itinevert route, and registered faFaceFrown regular icon.
Views, mixins, menu, query UI
src/views/SideMenu.vue, src/views/documents/utils/QueryItems.vue, src/views/documents/utils/url-mixin.js
SideMenu: Itinevert nav item changed to anchor for full refresh; QueryItems: new showPrintButton prop and use of categorizedFieldsDefault; url-mixin: documentType computed reads route query for Itinevert routes.
Icons
src/components/generics/icons/IconItinevert.vue
New IconItinevert component rendering a FontAwesome bus icon with fixedWidth prop.
Translations
src/translations/fr.json
Added French translations for Itinevert UI strings (labels, prompts, errors, minutes unit).

Sequence Diagram

sequenceDiagram
    actor User
    participant Portal as ItinevertView
    participant Wizard as ItinevertWizard
    participant Service as ItinevertService
    participant Navitia as Navitia API
    participant Photon as Photon API
    participant Map as OlMap/MapView

    User->>Portal: Open Itinevert
    Portal->>Wizard: mount (form view)
    User->>Wizard: submit form (origin, dest, datetime, activities)
    Wizard->>Service: formSearch / buildQuery
    Service->>Navitia: GET coverage / isochrones / polygon
    Navitia-->>Service: geometry, coverage, counts
    Service-->>Wizard: initial results
    Wizard->>Wizard: switch to filter view
    User->>Wizard: apply filters
    Wizard->>Service: enhanceQuery(filters)
    Service->>Navitia: GET reachable routes/waypoints (paginated)
    Navitia-->>Service: paginated documents
    Service-->>Wizard: aggregated results (with progress)
    Wizard->>Map: render documents & polygons (openInNewTab considered)
    User->>Map: click document
    Map->>Map: open document (new tab if openInNewTab or modifier)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

  • Complex, heterogeneous changes: new service layer, many new Vue components, map rendering changes, router and constants, and input integrations.
  • Areas needing extra attention:
    • src/js/apis/itinevert-service.js — query builders, pagination, cancellation, projection math.
    • src/components/itinevert/ItinevertWizard.vue and ItinevertResultView.vue — async flows, state transitions, promise cancellation, localStorage persistence.
    • src/components/map/OlMap.vue and src/components/map/map-utils.js — polygon styling, performance with many features, click/openInNewTab behavior.
    • src/components/generics/inputs/InputAddress.vue — Photon integration, geolocation, reverse-geocoding, error paths.
    • Integration points: router registration, url-mixin change affecting documentType resolution, and QueryItems categorization usage.

Possibly related PRs

  • feat: Smart origin lot 1 + lot 2 #4360 — Modifies OlMap.vue and map-utils.js for document styling and map interactions; strongly related to this PR's map polygon styling and click/openInNewTab behavior.

Poem

🐰 I nibbled maps and stitched the trail,

New tabs that open, routes that sail,
Addresses that whisper where to start,
Filters bloom, the journey's art,
Itinevert hops — a wheely heart! 🚌✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title refers to 'Smart/Origin parcours 3 itinévert' which aligns with the substantial Itinevert feature implementation across multiple new components, services, and utilities.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch smart-origin/revue_parcours_3

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7753eaf and 958411e.

📒 Files selected for processing (1)
  • src/components/itinevert/ItinevertWizard.vue (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/itinevert/ItinevertWizard.vue
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build (lts/*)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (15)
src/components/generics/inputs/InputAutocomplete.vue-44-44 (1)

44-44: Default formatSuggestion returns undefined.

The default () => {} returns undefined, which will display as text when rendering suggestions. Consider returning an empty string or the suggestion itself.

-    formatSuggestion: { type: Function, default: () => {} },
+    formatSuggestion: { type: Function, default: (s) => s ?? '' },
src/components/generics/inputs/InputAutocomplete.vue-2-2 (1)

2-2: Incorrect class binding syntax.

The class binding ['control, autocomplete-wrapper', { fullwidth }] treats 'control, autocomplete-wrapper' as a single class name string instead of two separate classes.

-  <div :class="['control, autocomplete-wrapper', { fullwidth }]">
+  <div :class="['control', 'autocomplete-wrapper', { fullwidth }]">
src/components/itinevert/ItinevertBanner.vue-28-28 (1)

28-28: Potentially incorrect router-link name.

{ name: '/' } is likely incorrect—'/' is typically a path, not a named route. This may result in a navigation failure.

-            <router-link :to="{ name: '/' }" class="menu-brand has-text-centered">
+            <router-link to="/" class="menu-brand has-text-centered">

Or use the correct named route if one exists (e.g., { name: 'home' }).

src/components/generics/inputs/InputAutocomplete.vue-46-55 (1)

46-55: Incorrect type initialization for valueSuggestions.

valueSuggestions is initialized as an empty string '' but should be an array [] since it's used with .filter(), .slice(), and .length array operations.

       localData: {
         value: this.defaultValue ? this.formatSuggestion(this.defaultValue) : '',
         selectedValue: '',
-        valueSuggestions: '',
+        valueSuggestions: [],
         showSuggestions: this.showSuggestions,
       },
src/views/documents/utils/url-mixin.js-22-28 (1)

22-28: Missing fallback when documentType query parameter is absent.

If the route name starts with "Itinevert" but this.$route.query.documentType is undefined, this will return undefined, potentially breaking the fields computed property which accesses constants.objectDefinitions[this.documentType].

Consider adding a fallback:

       // check if it starts with "Itinevert", so that we can retrieve the doc type
       if (this.$route.name.startsWith('Itinevert')) {
-        return this.$route.query.documentType;
+        return this.$route.query.documentType ?? 'route';
       }
src/components/itinevert/ItinevertNoResultView.vue-6-7 (1)

6-7: Fix error prop default factory (currently returns undefined) and optionally guard the error line

The error prop default is:

default: () => {},

This arrow function returns undefined, not an empty object. If you intend an object default, it should be:

    error: {
      type: Object,
-     default: () => {},
+     default: () => ({}),
    },

Also, since the template always renders the error line:

<span class="no-result-found">
  {{ errorMessages[error?.id] ?? error?.message }}
</span>

you might want to wrap it in a v-if so the red line only appears when there is an actual message:

-    <span class="no-result-found">{{ errorMessages[error?.id] ?? error?.message }}</span>
+    <span
+      class="no-result-found"
+      v-if="errorMessages[error?.id] ?? error?.message"
+    >
+      {{ errorMessages[error?.id] ?? error?.message }}
+    </span>

Also applies to: 14-18

src/components/map/map-utils.js-306-345 (1)

306-345: Move zIndex from stroke to style to make polygon highlighting effective

In OpenLayers 10.7.0, zIndex is a property of ol.style.Style, not ol.style.Stroke. Setting it on the stroke object is ignored, so highlighted polygons won't actually be drawn above others.

Move zIndex to the style object:

export const getDocumentPolygonStyle = function (title, highlight, properties) {
  // Highlighted polygon (properties provided)
  if (highlight && properties) {
    return new ol.style.Style({
      fill: new ol.style.Fill({
        color: properties.color,
      }),
      stroke: new ol.style.Stroke({
        color: properties.strokeColor,
        width: 4,
-       zIndex: 1,
      }),
      text: buildTextStyle(properties.name || title, highlight, true),
+     zIndex: 1,
    });
  }
src/components/itinevert/ItinevertFilterView.vue-133-135 (1)

133-135: Potential runtime error: filteredRoutes.total may be undefined.

The routeCount computed property directly accesses filteredRoutes.total, but the default prop value is an empty object without a total property. This could cause display issues or errors.

     routeCount() {
-      return this.filteredRoutes.total;
+      return this.filteredRoutes?.total ?? 0;
     },
src/components/itinevert/ItinevertFilterItem.vue-154-156 (1)

154-156: Incorrect initial value for numericalRangeSlider.

The initialValue() method uses field.values for the numericalRangeSlider case, but numerical range sliders use field.min and field.max (as seen in lines 20-21), not field.values.

     if (this.field.queryMode === 'valuesRangeSlider' || this.field.queryMode === 'numericalRangeSlider') {
-      return [this.field.values?.[0] ?? 0, this.field.values?.[this.field.values.length - 1] ?? 0];
+      if (this.field.queryMode === 'valuesRangeSlider') {
+        return [this.field.values?.[0] ?? 0, this.field.values?.[this.field.values.length - 1] ?? 0];
+      }
+      return [this.field.min ?? 0, this.field.max ?? 0];
     }
src/views/portals/ItinevertView.vue-73-76 (1)

73-76: localStorage key collision risk and missing restoration.

The localStorage key is just the property name (e.g., "visible"), which could conflict with other components using the same key. Additionally, the stored value is never restored on component mount.

Consider namespacing the key and restoring on mount:

+  mounted() {
+    const stored = this.$localStorage.get('itinevert_visible');
+    if (stored !== null) {
+      this.visible = stored;
+    }
+  },
+
   methods: {
     changeView(view) {
       this.wizardView = view;
     },
     toggleProperty(property) {
       this.setProperty(property, !this[property]);
     },
     setProperty(property, value) {
       this[property] = value;
-      this.$localStorage.set(`${property}`, this[property]);
+      this.$localStorage.set(`itinevert_${property}`, this[property]);
     },
   },

Committable suggestion skipped: line range outside the PR's diff.

src/components/generics/inputs/InputAddress.vue-47-50 (1)

47-50: Hardcoded French placeholder should use internationalization.

The default placeholder is hardcoded in French. This should either be internationalized or the default should be empty with the parent component providing a translated placeholder.

src/components/generics/inputs/InputAddress.vue-64-74 (1)

64-74: Incorrect type for addressPropositions initialization.

addressPropositions is initialized as an empty string but is used as an array throughout the component (e.g., slice(0, 6) on line 145, .length check on line 16). This could cause runtime errors if methods are called before the first successful search.

 data() {
   return {
     localData: {
       address: this.defaultAddress ? this.formatProposition(this.defaultAddress) : '',
       selectedAddress: '',
-      addressPropositions: '',
+      addressPropositions: [],
       showAddressPropositions: this.showAddressPropositions,
       coordinates: null,
     },
   };
 },
src/components/itinevert/ItinevertResultView.vue-362-366 (1)

362-366: Potential null reference when mapping document IDs.

If this.documents?.documents is undefined, oldIds will be undefined and the includes check on line 390 will fail.

         // get list of previous document IDs
-        const oldIds = this.documents?.documents?.map((doc) => doc.document_id);
+        const oldIds = this.documents?.documents?.map((doc) => doc.document_id) ?? [];
src/components/generics/inputs/InputAddress.vue-128-152 (1)

128-152: Missing timeout cleanup on component unmount.

searchTimeout is used but not declared in data(), and more importantly, the timeout is never cleared when the component is destroyed. This could cause errors or memory leaks if the component unmounts while a search is pending.

Add cleanup in a beforeDestroy hook:

+  beforeDestroy() {
+    if (this.searchTimeout) {
+      clearTimeout(this.searchTimeout);
+    }
+  },
   methods: {

Alternatively, declare searchTimeout in data() for consistency, though it works as an instance property too.

Committable suggestion skipped: line range outside the PR's diff.

src/components/itinevert/ItinevertResultView.vue-202-226 (1)

202-226: Hardcoded document_id: 1 may conflict with real documents.

The synthetic area document uses document_id: 1, which could match a real document ID, potentially causing highlighting or interaction issues. Consider using a unique identifier like a negative number or a UUID.

         const areaDocument = {
-          document_id: 1,
+          document_id: -1, // synthetic ID to avoid conflicts with real documents
           type: 'a',
🧹 Nitpick comments (25)
src/components/generics/inputs/InputRadioButton.vue (2)

11-14: Conditionally render reason text.

The reason paragraph renders for all options, creating empty <p> elements when no reason exists. Consider using v-if to only render when a reason is present.

Apply this diff:

       <div class="option">
         {{ option.text }}
-        <p class="optionDisabled">{{ reasons[option.value] }}</p>
+        <p v-if="reasons[option.value]" class="optionDisabled">{{ reasons[option.value] }}</p>
       </div>

87-91: Remove !important if possible.

The !important flag on line 89 suggests specificity conflicts. In scoped component CSS, this is usually avoidable. Consider removing it and testing whether the styles still apply correctly.

src/js/constants/index.js (1)

39-143: Inconsistent key casing in categorizedFieldsDefault.

The key ratings (line 55) uses lowercase while other keys (General, Terrain, Miscs) are capitalized. This inconsistency could lead to confusion or issues when iterating over categories.

     General: [
       ...
     ],
 
-    ratings: [
+    Ratings: [
       ...
     ],

If this is intentional (e.g., for i18n keys), please add a comment explaining the convention.

src/components/itinevert/ItinevertBanner.vue (1)

5-5: Multiple TODO placeholders remain in production code.

This component contains several TODO comments indicating incomplete implementation:

  • Line 5: "todo : texte descriptif"
  • Lines 12-22: Multiple placeholder router-links with "todo : des liens vers d'autres contenus"

These should be addressed before merging or tracked in a follow-up issue.

Would you like me to open an issue to track completing this banner component?

Also applies to: 12-22

src/translations/fr.json (1)

36-36: Minor plural inconsistency.

English key "Access points" (plural) is translated to French "Point d'accès" (singular). Consider using "Points d'accès" for consistency.

-    "Access points": "Point d'accès",
+    "Access points": "Points d'accès",
src/components/generics/inputs/InputAutocomplete.vue (1)

65-91: Undeclared searchTimeout property.

this.searchTimeout is used for debouncing but is never declared in data(). This works but is not idiomatic Vue—instance properties should be declared.

Add searchTimeout: null to data() or initialize it in a created() hook:

   data() {
     return {
+      searchTimeout: null,
       localData: {
         ...
       },
     };
   },
src/js/apis/itinevert-service.js (4)

200-212: Side effect in getter methods.

getJourneyReachableWaypoints and getJourneyReachableRoutes reset this.jobID = null as a side effect. This couples reading results with clearing state, which could cause issues if the result needs to be fetched again (e.g., on network error retry).

Consider separating job lifecycle management from result retrieval, or letting the caller explicitly clear the job.


336-340: Use safer Object.hasOwn() instead of hasOwnProperty.

newQuery.hasOwnProperty(key) can fail if newQuery has a property named hasOwnProperty or if it's created with Object.create(null).

-      if (newQuery.hasOwnProperty(key) && newQuery[key]) {
+      if (Object.hasOwn(newQuery, key) && newQuery[key]) {

468-476: Unreachable code block.

The check at lines 469-476 for Array.isArray(initialVal) && typeof initialVal[0] === 'number' is unreachable because arrays are already handled at line 455-461.

     // Compare for boolean
     if (typeof initialVal === 'boolean') {
       return initialVal === fieldValue;
     }
 
-    // Compare for numerical ranges
-    if (Array.isArray(initialVal) && typeof initialVal[0] === 'number') {
-      return (
-        Array.isArray(fieldValue) &&
-        fieldValue.length === 2 &&
-        initialVal[0] === fieldValue[0] &&
-        initialVal[1] === fieldValue[1]
-      );
-    }
-
     // General compare for strings
     return initialVal === fieldValue;

82-126: Consider extracting duplicated CancelablePromise logic.

getAllReachableRoutes and getAllReachableWaypoints contain nearly identical implementations with inline CancelablePromise class definitions.

Extract the common pattern into a shared helper to improve maintainability and reduce duplication.

Also applies to: 154-198

src/js/vue-plugins/router.js (1)

19-20: Route works, but consider normalizing path casing and/or lazy‑loading

The Itinevert route is correctly wired, but you may want to:

  • Use a lowercase path (e.g. /itinevert) for consistency with existing routes.
  • Optionally lazy‑load ItinevertView like other portal views to keep the initial bundle smaller.

Example for the path:

-  { path: '/Itinevert', name: 'Itinevert', component: ItinevertView },
+  { path: '/itinevert', name: 'Itinevert', component: ItinevertView },

(adjust any hardcoded URLs accordingly if you change it).

Also applies to: 56-56

src/views/SideMenu.vue (1)

62-68: Anchor‑based Itinevert link is fine; confirm icon registration and reload trade‑off

Using a plain <a> with $router.resolve matches the intent to force a full refresh, but note:

  • This will always reload the whole SPA, even when navigating from another route (performance trade‑off versus a <router-link>).
  • <icon-itinevert> must be globally registered (unlike IconYeti, which is imported locally) or the menu will show an unknown element warning.

If the only requirement is “refresh when already on Itinevert”, you could alternatively keep <router-link> and add a click handler that forces a reload when $route.name === 'Itinevert'.

src/components/generics/icons/IconItinevert.vue (1)

1-14: Icon wrapper is straightforward and consistent

The IconItinevert wrapper around <fa-icon> with a fixedWidth Boolean prop looks good and matches typical icon component patterns. Optionally add name: 'IconItinevert' for clearer component traces.

src/components/itinevert/ItinevertPageSelector.vue (1)

68-72: Consider initializing total from documents prop.

The hardcoded initial value of 30 for total could cause a brief display glitch before the watcher updates it. Consider initializing from the prop if available.

   data() {
     return {
-      total: 30,
+      total: this.documents?.total ?? 0,
     };
   },
src/components/itinevert/ItinevertWizard.vue (3)

196-202: Duplicate categoryIcon definition.

This categoryIcon object is also defined in ItinevertFilterView.vue (lines 74-80). Consider extracting to a shared constant to maintain consistency.

// In a shared constants file
export const itinevertCategoryIcon = {
  General: 'filter',
  MultiSearch: 'filter',
  Miscs: 'database',
  Terrain: ['waypoint', 'summit'],
  ratings: 'tachometer-alt',
};

310-318: noResultsFound uses hasOwnProperty inconsistently with optional chaining.

The code mixes hasOwnProperty checks with optional chaining, which is inconsistent. Also, using Object.prototype.hasOwnProperty.call would be safer.

     noResultsFound() {
       if (this.formData?.searchKind?.selected === 'route') {
-        return !this.filteredRoutes.hasOwnProperty('documents') || this.filteredRoutes?.documents?.length === 0;
+        return !this.filteredRoutes?.documents?.length;
       } else if (this.formData?.searchKind?.selected === 'waypoint') {
-        return !this.filteredWaypoints.hasOwnProperty('documents') || this.filteredWaypoints?.documents?.length === 0;
+        return !this.filteredWaypoints?.documents?.length;
       } else {
         return false;
       }
     },

128-132: Search button validation uses method call in template.

Calling isSearchEnabled() as a method in the template means it runs on every render. Consider making it a computed property for better performance and caching.

+  computed: {
+    // ... existing computed properties ...
+    isSearchEnabled() {
+      if (this.formData.searchKind.selected === 'route' && this.formData.activities.length < 1) {
+        return false;
+      }
+      if (
+        this.formData.destinationKind.selected === 'mountain range' &&
+        this.formData.mountainRange.selected === null
+      ) {
+        return false;
+      }
+      if (this.formData.startingPoint.selectedAddress === null || this.formData.startingPoint.selectedAddress === '') {
+        return false;
+      }
+      return true;
+    },
+  },

   methods: {
-    isSearchEnabled() { ... },

Then update template:

-            <button class="button is-primary" :disabled="!isSearchEnabled()" @click="formSearch">
+            <button class="button is-primary" :disabled="!isSearchEnabled" @click="formSearch">
src/components/generics/inputs/InputAddress.vue (5)

86-89: Misleading comment: no date picker in this component.

The comment references "Firefox's date picker calendar" but this component doesn't contain a date picker. This appears to be copy-pasted from another component. Consider removing this code if not needed, or updating the comment to explain its actual purpose.


96-126: Inconsistent API usage and missing internationalization.

  1. This method uses fetch() directly while searchAddressPropositions uses the photon API wrapper. Consider using photon for consistency and centralized error handling.

  2. Alert messages are hardcoded in English instead of using $gettext for internationalization:

         (error) => {
           console.error('Geolocation error:', error);
-          alert('Unable to get your current location.');
+          alert(this.$gettext('Unable to get your current location.'));
         }
       );
     } else {
-      alert('Geolocation is not supported by your browser.');
+      alert(this.$gettext('Geolocation is not supported by your browser.'));
     }

179-181: Simplify string concatenation.

The template literal ${' '} is unnecessarily verbose.

       if (props.street) {
-        formattedAddress = `${formattedAddress}${' '}${props.street}`;
+        formattedAddress = `${formattedAddress} ${props.street}`;
       }

198-242: Consider using the photon API wrapper for consistency.

Both reverseGeocodeUserLocation and useCurrentLocation make direct fetch calls to photon.komoot.io, while searchAddressPropositions uses the photon API wrapper. For maintainability and consistent error handling, consider adding a reverse geocoding method to the photon API module.


29-31: Improve button accessibility.

The geolocation button lacks type="button" (defaults to "submit" in forms) and an aria-label for screen readers.

-      <button class="geolocalisation" @click="useCurrentLocation">
+      <button type="button" class="geolocalisation" @click="useCurrentLocation" :aria-label="$gettext('Use current location')">
         <img class="geolocalisation-img" src="@/assets/img/boxes/geoloc.svg" alt="geoloc" />
       </button>
src/components/itinevert/ItinevertResultView.vue (3)

388-388: Typo in comment.

"IDsw" should be "IDs".

-          // filter only matching old IDsw
+          // filter only matching old IDs

255-257: Typo in method name: tooglePropertytoggleProperty.

The method name has a spelling error.

-    toogleProperty(property) {
+    toggleProperty(property) {
       this.setProperty(property, !this[property]);
     },

Also update the template reference on line 20:

-        <span @click="toogleProperty('listMode')" ...>
+        <span @click="toggleProperty('listMode')" ...>

231-235: Unnecessary async keyword in watch handler.

The handler doesn't use await, so async is not needed.

     '$route.query': {
-      async handler(newQuery) {
+      handler(newQuery) {
         this.filterPostNavitiaDocuments(newQuery);
       },
     },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7782a7b and 7753eaf.

⛔ Files ignored due to path filters (1)
  • src/assets/img/itinevert/itinevert-banner.png is excluded by !**/*.png
📒 Files selected for processing (27)
  • src/components/datatable/DocumentsTable.vue (10 hunks)
  • src/components/datatable/cell-renderers/DocumentLinkNewTab.vue (1 hunks)
  • src/components/generics/icons/IconItinevert.vue (1 hunks)
  • src/components/generics/inputs/InputAddress.vue (1 hunks)
  • src/components/generics/inputs/InputAutocomplete.vue (1 hunks)
  • src/components/generics/inputs/InputRadioButton.vue (1 hunks)
  • src/components/itinevert/ItinevertBanner.vue (1 hunks)
  • src/components/itinevert/ItinevertFilterItem.vue (1 hunks)
  • src/components/itinevert/ItinevertFilterView.vue (1 hunks)
  • src/components/itinevert/ItinevertLoadingView.vue (1 hunks)
  • src/components/itinevert/ItinevertNoResultView.vue (1 hunks)
  • src/components/itinevert/ItinevertPageSelector.vue (1 hunks)
  • src/components/itinevert/ItinevertResultView.vue (1 hunks)
  • src/components/itinevert/ItinevertWizard.vue (1 hunks)
  • src/components/map/OlMap.vue (4 hunks)
  • src/components/map/map-utils.js (1 hunks)
  • src/js/apis/itinevert-service.js (1 hunks)
  • src/js/apis/photon.js (1 hunks)
  • src/js/apis/transport-service.js (1 hunks)
  • src/js/constants/index.js (1 hunks)
  • src/js/vue-plugins/font-awesome-config.js (2 hunks)
  • src/js/vue-plugins/router.js (2 hunks)
  • src/translations/fr.json (21 hunks)
  • src/views/SideMenu.vue (1 hunks)
  • src/views/documents/utils/QueryItems.vue (4 hunks)
  • src/views/documents/utils/url-mixin.js (1 hunks)
  • src/views/portals/ItinevertView.vue (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-02T19:55:04.817Z
Learnt from: flob38
Repo: c2corg/c2c_ui PR: 4382
File: src/views/DfmAdLarge.vue:17-19
Timestamp: 2025-08-02T19:55:04.817Z
Learning: The DfmAdLarge component in src/views/DfmAdLarge.vue is only used on large screens (width >= 1216px), so fixed min-width values are appropriate and won't cause layout issues on smaller viewports.

Applied to files:

  • src/components/itinevert/ItinevertWizard.vue
  • src/components/itinevert/ItinevertResultView.vue
🪛 GitHub Actions: Continuous integration
src/components/itinevert/ItinevertWizard.vue

[error] 781-781: stylelint: declaration-block-no-duplicate-properties: Unexpected duplicate 'width' declaration.

🔇 Additional comments (20)
src/js/vue-plugins/font-awesome-config.js (1)

9-9: LGTM!

The new faFaceFrownRegular icon is properly imported and registered following the existing patterns.

Also applies to: 367-367

src/js/apis/transport-service.js (1)

21-29: LGTM!

The new getWaypointsAccessibleTC method follows the existing pattern used by isReachable, properly documented and consistent with the service's style.

src/components/itinevert/ItinevertBanner.vue (1)

58-69: Counterintuitive responsive logo sizing.

The logo is set to max-width: 20% on desktop but shrinks to max-width: 10% on tablet. Typically, relative sizing increases on smaller screens for better visibility. Was this intentional?

src/js/apis/itinevert-service.js (1)

28-28: Constant appears to represent runtime state.

MAX_NAVITIA_ISOCHRONES_REQUEST_REACHED is exported as a constant with value false, but semantically it seems like it should track whether a limit has been reached at runtime. If this is intentional configuration, consider renaming for clarity (e.g., NAVITIA_ISOCHRONES_REQUEST_LIMIT_ENABLED).

src/js/apis/photon.js (1)

16-24: Safer handling of optional center coordinates looks correct

Guarding centerWgs84 before indexing avoids crashes when it’s not provided, without changing behavior when it is. No further changes needed here.

src/views/documents/utils/QueryItems.vue (1)

68-80: Configurable print button and centralized categorized fields look good

  • showPrintButton cleanly controls both inline and dropdown PrintButton visibility and defaults to true, so existing behavior is preserved.
  • Reusing constants.categorizedFieldsDefault for all document types keeps the categorization centralized; this is fine as long as those defaults cover all current types.

No issues spotted with these changes.

Also applies to: 101-112, 135-143

src/components/datatable/cell-renderers/DocumentLinkNewTab.vue (1)

1-9: The document-link component is registered globally via the genericComponents plugin (src/js/vue-plugins/generic-components.js), which automatically registers all components from @/components/generics with kebab-case naming. This component works as intended and follows the established pattern used by other cell-renderers in the same directory (e.g., DocumentAuthor.vue, AreaList.vue), which similarly depend on globally-registered components without local imports or explicit name properties.

src/components/map/OlMap.vue (3)

854-884: LGTM! Area document polygon support is well-structured.

The conditional handling for area documents (type === 'a') correctly renders filled polygons from geom_detail using the new getDocumentPolygonStyle. The logic appropriately separates area documents from other types that use point/line rendering.

One minor note: area features don't have their ID set (unlike non-area features at line 874), which may affect ID-based lookups if needed in the future.


269-273: LGTM!

The openInNewTab prop is properly defined with appropriate type and default value.


1141-1141: LGTM!

The click handler correctly uses the openInNewTab prop as an alternative to Ctrl-click for opening documents in new tabs.

src/components/datatable/DocumentsTable.vue (3)

70-73: LGTM!

The openInNewTab prop is properly defined.


82-86: Column definitions won't update if openInNewTab changes at runtime.

The watcher only triggers computeColumnDefs when documentType changes. If openInNewTab can change while the component is mounted, the column definitions won't reflect the new value.

If openInNewTab is expected to change at runtime, add it to the watch:

   watch: {
     documentType: {
       handler: 'computeColumnDefs',
       immediate: true,
     },
+    openInNewTab: 'computeColumnDefs',
     highlightedDocument(newValue, oldValue) {

130-133: LGTM!

The conditional renderer pattern is applied consistently across all document types.

src/components/itinevert/ItinevertLoadingView.vue (1)

1-47: LGTM!

Well-implemented loading component with:

  • Proper accessibility via ARIA attributes on the progress bar
  • Safe division-by-zero handling in computedPercent
  • Clean separation between spinner and progress bar modes
  • Smooth CSS transitions for progress updates
src/components/itinevert/ItinevertPageSelector.vue (1)

99-101: Unused method hideOnclick.

The hideOnclick method is only called from selectLimit, so it could be inlined. However, this is a minor style preference.

src/components/itinevert/ItinevertFilterView.vue (2)

139-141: Edge case: canDisplayResult returns false when total is 0 but also when undefined.

With the current logic and the potential undefined from filteredRoutes.total, the button will be disabled correctly, but for clarity the null-safe access would be better.


74-80: Using $options for static data is a valid Vue 2 pattern.

The categoryIcon mapping as a component option is correctly accessed via $options.categoryIcon in the template. This is appropriate for static configuration that doesn't need reactivity.

src/views/portals/ItinevertView.vue (1)

49-60: LGTM!

The conditional initial state based on user login status is a reasonable UX decision - logged-in users likely don't need the banner repeatedly.

src/components/itinevert/ItinevertWizard.vue (1)

373-380: Unconditional URL parameter clearing on mount may cause issues.

Clearing URL query parameters on mount could disrupt deep linking or page refresh scenarios where users expect their search state to be preserved.

Is this intentional? If users should be able to bookmark or share Itinevert URLs with parameters, this behavior would break that use case.

src/components/itinevert/ItinevertResultView.vue (1)

93-101: LGTM: Document table and map integration.

The integration of DocumentsTable and map-view with highlighting and new-tab opening is well implemented. The open-in-new-tab prop propagation is consistent across both views.

:value="option.value"
v-model="selected"
@change="emitSelectedValue"
:disabled="disabledOptions.map((option) => option.value).includes(option.value)"
Copy link

@coderabbitai coderabbitai bot Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Optimize disabled check with a computed Set.

The disabled check creates a new array via .map() and searches it for every option on every render. With many options, this becomes expensive.

Create a computed property for disabled values:

   computed: {
+    disabledValues() {
+      return new Set(this.disabledOptions.map((option) => option.value));
+    },
     reasons() {

Then update the template:

       <input
         type="radio"
         :value="option.value"
         v-model="selected"
         @change="emitSelectedValue"
-        :disabled="disabledOptions.map((option) => option.value).includes(option.value)"
+        :disabled="disabledValues.has(option.value)"
       />
🤖 Prompt for AI Agents
In src/components/generics/inputs/InputRadioButton.vue around line 9, the
template builds a new array via disabledOptions.map(...) and calls
.includes(...) for every option on each render; instead create a computed Set of
disabled values (e.g., disabledValues = computed(() => new Set((disabledOptions
|| []).map(o => o.value)))) and then change the template to check membership
with disabledValues.has(option.value); also guard against undefined
disabledOptions when building the Set so the computed is safe.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

complètement d'accord

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nayor, thank you! The computed Set approach will improve performance, especially with larger option lists. 👍

Comment on lines +21 to +38
props: {
options: {
type: Array,
required: true,
},
disabledOptions: {
type: Array,
default: () => [],
},
defaultValue: {
type: String,
default: '',
},
displayVertically: {
type: Boolean,
default: false,
},
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing value prop breaks v-model.

The component references this.value on line 41 and watches value on lines 54-56, but doesn't declare a value prop. For Vue 2 v-model to work, the component must accept a value prop.

Apply this diff to add the missing prop:

   props: {
+    value: {
+      type: String,
+      default: '',
+    },
     options: {
       type: Array,
       required: true,
     },
🤖 Prompt for AI Agents
In src/components/generics/inputs/InputRadioButton.vue around lines 21 to 38,
the component is missing a declared value prop which breaks v-model usage; add a
value prop to props (type: [String, Number, Boolean, Object] or at least String)
with a suitable default (e.g. empty string) so the component accepts the v-model
value, matching the existing references to this.value and the value watcher.

Comment on lines +39 to +43
data() {
return {
selected: this.value || this.defaultValue,
};
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Emit initial value to parent.

When defaultValue is used, the parent component won't receive the initial selected value, causing potential state desynchronization.

Add a mounted hook to emit the initial value:

   watch: {
     value(newValue) {
       this.selected = newValue;
     },
   },
+  mounted() {
+    if (this.selected) {
+      this.$emit('input', this.selected);
+    }
+  },
   methods: {

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/components/generics/inputs/InputRadioButton.vue around lines 39-43, the
component sets selected from this.value || this.defaultValue but never notifies
the parent when defaultValue is used; add a mounted() hook that checks if
this.value is undefined (or null) and this.defaultValue is defined then emits
the initial selected value to the parent (e.g., this.$emit('input',
this.selected) or this.$emit('update:value', this.selected) consistent with the
component's existing event naming), so the parent receives the initial state and
stays synchronized.

Comment on lines +70 to +76
<div v-else-if="field.queryMode === 'date'">
<date-query-item />
</div>

<div v-else-if="field.queryMode === 'dates'">
<dates-query-item />
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing v-model bindings for date query items.

DateQueryItem and DatesQueryItem are rendered without any v-model or event bindings, so their selected values won't propagate to the parent. If these are meant to be functional, they need value binding similar to other query modes.

     <div v-else-if="field.queryMode === 'date'">
-      <date-query-item />
+      <date-query-item v-model="selected" :field="field" />
     </div>

     <div v-else-if="field.queryMode === 'dates'">
-      <dates-query-item />
+      <dates-query-item v-model="selected" :field="field" />
     </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div v-else-if="field.queryMode === 'date'">
<date-query-item />
</div>
<div v-else-if="field.queryMode === 'dates'">
<dates-query-item />
</div>
<div v-else-if="field.queryMode === 'date'">
<date-query-item v-model="selected" :field="field" />
</div>
<div v-else-if="field.queryMode === 'dates'">
<dates-query-item v-model="selected" :field="field" />
</div>
🤖 Prompt for AI Agents
In src/components/itinevert/ItinevertFilterItem.vue around lines 70 to 76, the
DateQueryItem and DatesQueryItem are rendered without model bindings so their
selected values never reach the parent; add the same v-model wiring used by
other query modes (e.g. v-model="field.value") when rendering these components
and ensure the child components use/emit the standard update:modelValue event so
the parent field.value is kept in sync.

Comment on lines +145 to +147
oninput: debounce(function () {
this.selected = this.$refs.input.value;
}, 300),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Dead code: oninput method is never used.

The debounced oninput method is defined but never bound to any element in the template. The input element on line 46 uses v-model directly instead.

Either remove this dead code or wire it to the text input if debouncing is intended:

-    oninput: debounce(function () {
-      this.selected = this.$refs.input.value;
-    }, 300),

Or, if debouncing is needed for the input field, bind it:

-      <input ref="input" :type="field.type" v-model="selected" class="input" :placeholder="$gettext(field.name)" />
+      <input ref="input" :type="field.type" :value="selected" @input="oninput" class="input" :placeholder="$gettext(field.name)" />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
oninput: debounce(function () {
this.selected = this.$refs.input.value;
}, 300),

Comment on lines +238 to +253
mounted() {
// init filtered documents
this.filteredDocumentsBeforePagination = {
documents: this.documents.documents,
total: this.documents.total,
};
this.filteredDocuments = {
documents: this.documents.documents.slice(this.offset, this.offset + this.limit),
total: this.documents.total,
};
this.promise = {
data: this.filteredDocuments,
error: null,
cancel: () => {},
};
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential null reference error in mounted hook.

If documents prop is null (its default), accessing this.documents.documents and this.documents.total on lines 241-246 will throw an error.

   mounted() {
     // init filtered documents
+    if (!this.documents) {
+      this.filteredDocuments = { documents: [], total: 0 };
+      this.filteredDocumentsBeforePagination = { documents: [], total: 0 };
+      this.promise = { data: this.filteredDocuments, error: null, cancel: () => {} };
+      return;
+    }
     this.filteredDocumentsBeforePagination = {
       documents: this.documents.documents,
       total: this.documents.total,
     };
🤖 Prompt for AI Agents
In src/components/itinevert/ItinevertResultView.vue around lines 238 to 253, the
mounted hook assumes this.documents is non-null and directly accesses
this.documents.documents and this.documents.total which will throw if the prop
is null; guard these accesses by using safe defaults (e.g. const docs =
this.documents?.documents ?? [] and const total = this.documents?.total ?? 0),
use docs.slice(this.offset, this.offset + this.limit) when building
filteredDocuments, and set the promise.data to the safe filteredDocuments result
so the component works when documents is null.

Comment on lines +523 to +527
} else {
this.queryError = JSON.parse(data);
this.filteredRoutes = {};
this.filteredWaypoints = {};
}
Copy link

@coderabbitai coderabbitai bot Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Unsafe JSON.parse on potentially non-JSON data.

If data doesn't have documents property, the code assumes it's a JSON string and calls JSON.parse(data). This could throw a SyntaxError if the response is not valid JSON.

           } else {
-            this.queryError = JSON.parse(data);
+            try {
+              this.queryError = typeof data === 'string' ? JSON.parse(data) : data;
+            } catch (e) {
+              this.queryError = { message: 'Unknown error occurred' };
+            }
             this.filteredRoutes = {};
             this.filteredWaypoints = {};
           }

The same pattern appears at lines 562 and 592 - consider applying similar defensive handling.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else {
this.queryError = JSON.parse(data);
this.filteredRoutes = {};
this.filteredWaypoints = {};
}
} else {
try {
this.queryError = typeof data === 'string' ? JSON.parse(data) : data;
} catch (e) {
this.queryError = { message: 'Unknown error occurred' };
}
this.filteredRoutes = {};
this.filteredWaypoints = {};
}
🤖 Prompt for AI Agents
In src/components/itinevert/ItinevertWizard.vue around lines 523-527 (and
similarly at ~562 and ~592), the code calls JSON.parse(data) on the assumption
that non-`documents` responses are valid JSON, which can throw a SyntaxError;
change this to defensively parse: first check if data is a string, then wrap
JSON.parse in try/catch (or use a safe JSON parse helper) and on parse failure
set this.queryError to a safe fallback (e.g., the raw string or a structured
error message) and clear filteredRoutes/filteredWaypoints as currently done;
apply the same defensive parsing pattern at the other two locations to prevent
runtime exceptions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vérifier que le json a bien la bonne forme avant de le parser et/ou capturer l'erreur de parsing

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nayor, thank you for confirming! Yes, the suggestion I provided includes both safeguards:

  1. Type checking before parsing (typeof data === 'string')
  2. Try/catch block to handle any parsing errors gracefully

As mentioned in my initial comment, this same defensive pattern should be applied at all three locations where JSON.parse is called on potentially unsafe data:

  • Line ~523 (in the isochrones flow)
  • Line ~562 (in the computeJourneyReachableRoutes error handler)
  • Line ~592 (in the filterSearch error handler)

This will prevent runtime SyntaxError exceptions if the response isn't valid JSON.

Comment on lines +35 to +40
function ItinevertService() {
this.axios = axios.create({
baseURL: config.urls.api,
});
this.jobID = null;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Singleton with mutable jobID may cause concurrency issues.

The service is exported as a singleton (new ItinevertService()), but this.jobID is mutable instance state. If multiple Itinevert searches run concurrently, they will overwrite each other's job IDs.

Consider either:

  1. Returning jobID from start methods and passing it to progress/result methods
  2. Using a Map to track multiple jobs

Copy link
Contributor

@Nayor Nayor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bon travail !

Quelques remarques de finition + les commentaires de rabbitai à corriger, mais rien de structurel pour moi 👍
Ma seule interrogation se porte sur le fait que les filtres (côté back) ne se reposent plus sur elastic pour faire la recherche (la recherche par dictionnaire c'est vraiment pas terrible, et on a une evol elastic de prévu très bientôt pour corriger ça)

},
placeholder: {
type: String,
default: 'Entrez une adresse',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Utiliser l'internationalisation (i18n)

this.localData.coordinates = coords;

try {
const response = await fetch(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

à mettre dans une fonction, dans photon.js

if (data.features && data.features.length > 0) {
const location = data.features[0];
this.selectAddress(location);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

manque un fallback si aucune adresse correspondante n'est trouvé (mettre les coordonées par exemple)

this.selectAddress(location);
}
} catch (error) {
console.error('Error during reverse geolocation:', error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C'est un peu excessif le console.error non ? étant donné que ça n'est pas un composant central de l'application
.warn c'est bien

},
(error) => {
console.error('Geolocation error:', error);
alert('Unable to get your current location.');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utiliser toast avec un type is-warning plutôt

<section>
<div class="columns">
<div class="column is-12-mobile">
<div class="subtitle">todo : texte descriptif</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO ?

<span slot="button" class="button is-size-7-mobile" :disabled="category.fields.length === 0">
<fa-icon :icon="$options.categoryIcon[category.name]" />
<span class="is-hidden-mobile">
<!-- $gettext('General') -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code mort à enlever ?

</script>

<style scoped lang="scss">
$section-padding: 1.5rem; //TODO find this variable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO à résoudre

<style scoped lang="scss">
$section-padding: 1.5rem; //TODO find this variable
$header-height: 34px;
$header-padding-bottom: 1.5rem; //TODO find this variable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO à résoudre

Comment on lines +523 to +527
} else {
this.queryError = JSON.parse(data);
this.filteredRoutes = {};
this.filteredWaypoints = {};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vérifier que le json a bien la bonne forme avant de le parser et/ou capturer l'erreur de parsing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants