diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1c7c92595..6df33e446 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,33 @@
# Changelog
+
+## [1.0.8-rc13] - unstable
+
+### Added
+
+* [PostIt] Integration d'un selecteur de lieu RDV base sur Mapsui (carte interactive dans le formulaire `Rdv`).
+* [PostIt] Ajout d'un marqueur de position et d'une action de recentrage sur la carte RDV.
+* [PostIt] Ajout d'un service de reverse geocoding pour suggerer une adresse a partir des coordonnees carte.
+* [PostIt] Cache et debounce des resolutions d'adresse RDV pour limiter les appels reseau et lisser l'UX.
+* [PostIt.Tests] Nouvelles non-regressions sur le panneau d'adresse suggeree RDV et le comportement de la carte.
+* [Yavsc.Abstract] Activation de `#nullable enable annotations` sur les fichiers legacy avec annotations nullable.
+* [Yavsc.Server] Activation de `#nullable enable annotations` sur les fichiers legacy avec annotations nullable.
+
+### Changed
+
+* [PostIt] Generalisation de la barre de statut d'action (severite explicite) sur pages principales, dialogues et formulaires billing.
+* [PostIt] Harmonisation des messages de statut utilisateur en francais.
+* [PostIt] Renforcement des gardes de navigation dans les flux de gestion des membres de cercle.
+* [PostIt] Le flux RDV conserve l'adresse saisie manuellement et propose l'adresse resolue comme suggestion explicite.
+* [PostIt] Le flux de geolocalisation RDV tolere les positions proches dans le cache de suggestion d'adresse.
+
+### Fixed
+
+* [PostIt.Desktop] Correction d'un crash au demarrage OIDC (`No authority specified`) via durcissement des valeurs par defaut de configuration d'authentification.
+* [PostIt] Correction de la persistance des settings: l'etat runtime de statut n'est plus serialize dans le JSON utilisateur.
+* [PostIt.Tests] Ajout d'un verrou de non-regression sur le premier chargement des settings.
+* [PostIt] Correction du binding de la date RDV: `DatePicker.SelectedDate` est aligne sur un proxy `DateTimeOffset?` (`EventDateSelection`).
+
## [1.0.8-rc12] - unstable
### Added
diff --git a/src/PostIt/Directory.Packages.props b/src/PostIt/Directory.Packages.props
index 4dd4b2889..2f27340e9 100644
--- a/src/PostIt/Directory.Packages.props
+++ b/src/PostIt/Directory.Packages.props
@@ -16,6 +16,8 @@
+
+
diff --git a/src/PostIt/PostIt.Tests/BillingCommandPageViewModelTests.cs b/src/PostIt/PostIt.Tests/BillingCommandPageViewModelTests.cs
index c7b5b926b..17fcc968c 100644
--- a/src/PostIt/PostIt.Tests/BillingCommandPageViewModelTests.cs
+++ b/src/PostIt/PostIt.Tests/BillingCommandPageViewModelTests.cs
@@ -115,6 +115,89 @@ public class BillingCommandPageViewModelTests
}
}
+ [Fact]
+ public void ApplyLocationFromMap_sets_coordinates_and_updates_status()
+ {
+ var api = new RecordingApi();
+ var client = new BillingApiClient(api, "https://business.example/api/v1/");
+ var vm =
+ new CommandFormSummary { Id = 12, ActionName = "Rdv", Title = "Rendez-vous" }
+ .CreateCommandPageViewModel(
+ new ActivityInfo { Code = "dev", Name = "Développement" },
+ new ActivityUserDisplayItem { PerformerId = "perf-1", UserName = "Alice" },
+ client) as RdvViewModel;
+
+ vm!.Address = string.Empty;
+ vm.ApplyLocationFromMap(48.85661234, 2.35224567);
+
+ Assert.Equal(48.856612, vm.Latitude);
+ Assert.Equal(2.352246, vm.Longitude);
+ Assert.Contains("Position sélectionnée", vm.StatusMessage, StringComparison.OrdinalIgnoreCase);
+ }
+
+ [Fact]
+ public void EventDateSelection_round_trips_with_EventDate_for_DatePicker_binding()
+ {
+ var api = new RecordingApi();
+ var client = new BillingApiClient(api, "https://business.example/api/v1/");
+ var vm =
+ new CommandFormSummary { Id = 12, ActionName = "Rdv", Title = "Rendez-vous" }
+ .CreateCommandPageViewModel(
+ new ActivityInfo { Code = "dev", Name = "Développement" },
+ new ActivityUserDisplayItem { PerformerId = "perf-1", UserName = "Alice" },
+ client) as RdvViewModel;
+
+ var selected = new DateTimeOffset(2026, 9, 7, 14, 30, 0, TimeSpan.FromHours(2));
+ vm!.EventDateSelection = selected;
+
+ Assert.Equal(selected.LocalDateTime, vm.EventDate);
+ Assert.Equal(vm.EventDate, vm.EventDateSelection!.Value.LocalDateTime);
+ }
+
+ [Fact]
+ public void ApplyResolvedAddress_populates_empty_address_directly()
+ {
+ var api = new RecordingApi();
+ var client = new BillingApiClient(api, "https://business.example/api/v1/");
+ var vm =
+ new CommandFormSummary { Id = 12, ActionName = "Rdv", Title = "Rendez-vous" }
+ .CreateCommandPageViewModel(
+ new ActivityInfo { Code = "dev", Name = "Développement" },
+ new ActivityUserDisplayItem { PerformerId = "perf-1", UserName = "Alice" },
+ client) as RdvViewModel;
+
+ vm!.Address = string.Empty;
+ vm.ApplyResolvedAddress("10 rue de Rivoli, 75001 Paris");
+
+ Assert.Equal("10 rue de Rivoli, 75001 Paris", vm.Address);
+ Assert.False(vm.HasSuggestedAddress);
+ }
+
+ [Fact]
+ public void ApplyResolvedAddress_preserves_manual_address_and_exposes_suggestion()
+ {
+ var api = new RecordingApi();
+ var client = new BillingApiClient(api, "https://business.example/api/v1/");
+ var vm =
+ new CommandFormSummary { Id = 12, ActionName = "Rdv", Title = "Rendez-vous" }
+ .CreateCommandPageViewModel(
+ new ActivityInfo { Code = "dev", Name = "Développement" },
+ new ActivityUserDisplayItem { PerformerId = "perf-1", UserName = "Alice" },
+ client) as RdvViewModel;
+
+ vm!.Address = "Saisie manuelle";
+ vm.ApplyResolvedAddress("10 rue de Rivoli, 75001 Paris");
+
+ Assert.Equal("Saisie manuelle", vm.Address);
+ Assert.True(vm.HasSuggestedAddress);
+ Assert.Equal("10 rue de Rivoli, 75001 Paris", vm.SuggestedAddress);
+
+ vm.ApplySuggestedAddressCommand.Execute(null);
+
+ Assert.Equal("10 rue de Rivoli, 75001 Paris", vm.Address);
+ Assert.False(vm.HasSuggestedAddress);
+ }
+
[Fact]
public async Task InitializeAsync_loads_prestations_for_brush_and_submit_posts_selected_prestation()
{
diff --git a/src/PostIt/PostIt.Tests/NominatimReverseGeocodingServiceTests.cs b/src/PostIt/PostIt.Tests/NominatimReverseGeocodingServiceTests.cs
new file mode 100644
index 000000000..4eceb7a2f
--- /dev/null
+++ b/src/PostIt/PostIt.Tests/NominatimReverseGeocodingServiceTests.cs
@@ -0,0 +1,72 @@
+using System.Net;
+using System.Text;
+using PostIt.Services;
+
+namespace PostIt.Tests;
+
+public class NominatimReverseGeocodingServiceTests
+{
+ [Fact]
+ public async Task TryResolveAddressAsync_formats_compact_street_address_from_nominatim_payload()
+ {
+ var handler = new StubHandler("""
+ {
+ "display_name": "6, Place de l'Hôtel-de-Ville - Esplanade de la Libération, Paris, 75004, France",
+ "address": {
+ "house_number": "6",
+ "road": "Place de l'Hôtel-de-Ville - Esplanade de la Libération",
+ "postcode": "75004",
+ "city": "Paris"
+ }
+ }
+ """);
+
+ var service = new NominatimReverseGeocodingService(new HttpClient(handler)
+ {
+ BaseAddress = new Uri("https://nominatim.openstreetmap.org/")
+ });
+
+ var result = await service.TryResolveAddressAsync(48.8566, 2.3522);
+
+ Assert.Equal("6, Place de l'Hôtel-de-Ville - Esplanade de la Libération, 75004, Paris", result);
+ Assert.NotNull(handler.LastRequest);
+ Assert.Contains("reverse?format=jsonv2", handler.LastRequest!.RequestUri!.ToString(), StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public async Task TryResolveAddressAsync_returns_null_on_unsuccessful_response()
+ {
+ var handler = new StubHandler("{}", HttpStatusCode.TooManyRequests);
+ var service = new NominatimReverseGeocodingService(new HttpClient(handler)
+ {
+ BaseAddress = new Uri("https://nominatim.openstreetmap.org/")
+ });
+
+ var result = await service.TryResolveAddressAsync(48.8566, 2.3522);
+
+ Assert.Null(result);
+ }
+
+ private sealed class StubHandler : HttpMessageHandler
+ {
+ private readonly string _payload;
+ private readonly HttpStatusCode _statusCode;
+
+ public HttpRequestMessage? LastRequest { get; private set; }
+
+ public StubHandler(string payload, HttpStatusCode statusCode = HttpStatusCode.OK)
+ {
+ _payload = payload;
+ _statusCode = statusCode;
+ }
+
+ protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ LastRequest = request;
+ return Task.FromResult(new HttpResponseMessage(_statusCode)
+ {
+ Content = new StringContent(_payload, Encoding.UTF8, "application/json")
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PostIt/PostIt.Tests/RdvPageHeadlessTests.cs b/src/PostIt/PostIt.Tests/RdvPageHeadlessTests.cs
new file mode 100644
index 000000000..fdf8689ab
--- /dev/null
+++ b/src/PostIt/PostIt.Tests/RdvPageHeadlessTests.cs
@@ -0,0 +1,79 @@
+using Avalonia.Controls;
+using Avalonia.Headless.XUnit;
+using PostIt.ViewModels;
+using PostIt.ViewModels.Commands;
+using PostIt.Views.Commands;
+using Yavsc.Abstract.Workflow;
+using Yavsc.Api.Client;
+
+namespace PostIt.Tests;
+
+public class RdvPageHeadlessTests
+{
+ [AvaloniaFact]
+ public void Suggested_address_panel_is_hidden_by_default()
+ {
+ var page = CreatePage(out _);
+
+ var panel = page.FindControl("SuggestedAddressPanel");
+ var progress = page.FindControl("SuggestedAddressProgress");
+
+ Assert.NotNull(panel);
+ Assert.NotNull(progress);
+ Assert.False(panel!.IsVisible);
+ Assert.False(progress!.IsVisible);
+ }
+
+ [AvaloniaFact]
+ public async Task Suggested_address_panel_and_spinner_follow_viewmodel_state()
+ {
+ var page = CreatePage(out var vm);
+ var panel = page.FindControl("SuggestedAddressPanel")!;
+ var progress = page.FindControl("SuggestedAddressProgress")!;
+ var applyButton = page.FindControl