26 KiB
RECETTE ANDROID — AUDIT AVANT MODIFICATION (OBJECTIF 0 / §17)
Projet :
D:\Avalonia\Omika— Base OMIKA réelle. Aucun fichier modifié au stade d'audit. Ce document est le livrable de l'audit préalable. Les correctifs ne sont appliqués qu'après validation.
Sommaire
- Architecture rappelée
- Tableau de synthèse des problèmes
- Cause exacte par problème
- Classification
- Preuve de non-régression Batik
- Routes HTTP appelées
- Requêtes SQL concernées
- Inventaire des icônes à remplacer
- Propositions de SVG monochrome
1. Architecture rappelée
- Mobile (
src/Omika.App.Local) : offline-first sur SQLite, synchronisé avec l'API viaMobileApiClient. - API (
src/Omika.Api) : endpoints REST, sélection de baseBatik/OmikaviaApplicationRuntimeContext.Current.DetectedVersion. - Données (
src/Omika.Data) : repositoriesAdaptive*qui délèguent àBatik*Repository(schéma historique) ouOmika*Repository(schéma physique Omika). Les mappers (Mapping/Omika,Mapping/Batik) produisent le modèle canonique. - Identité publique : GUID. Omika → GUID physique (colonne
GUID). Batik → GUID synthétique réversible (MobileEntityIdentityResolver). - UI (
src/Omika.App) : Avalonia, thèmes clair/sombre viaDesignTokens.axaml(clésDynamicResource).
2. Tableau de synthèse
| # | PROBLÈME | SYMPTÔME | COUCHE | FICHIER(S) | CAUSE EXACTE | SPÉCIFIQUE OMIKA ? | BATIK IMPACTÉ ? | CORRECTION PROPOSÉE |
|---|---|---|---|---|---|---|---|---|
| 1 | Carte OSM bloquée | 403 / "volunteer-run servers" | ANDROID/UI (tuiles) | Controls/LieuMap.axaml.cs:97 |
OpenStreetMap.CreateTileLayer() (Mapsui) utilise un HttpClient sans User-Agent identifiant Omika |
NON | NON (commun) | Tile source custom avec User-Agent + cache + attribution ; fallback adresse+Itinéraire |
| 2 | Icônes hétérogènes | Emoji/Unicode/couleurs | UI | multiples (cf. §8) | Emoji/Unicode et couleurs codées en dur | NON | NON | Standardiser en Path monochrome + DynamicResource |
| 3 | Boutons entête intervention | fond blanc massif | UI | Views/RendezVousDetailView.axaml:120-139 |
Background=OnPrimaryColorBrush (blanc) + emoji ⌖ ⚒ ▧ |
NON | NON | Style translucide + SVG monochrome |
| 4 | Agenda équipe : icône groupe | bleue/multicolore | UI | Views/AgendaView.axaml:377 (👥) |
emoji 👥 utilisé comme icône de groupe (pas dans AgendaEquipeView qui n'a pas d'icône) |
NON | NON | Remplacer par SVG/Path monochrome |
| 5 | RDV→intervention introuvable | "Intervention introuvable." | OMIKA DATA / API | OmikaRendezVousRepository, MobileSnapshotService, OmikaInterventionRepository.GetDetailsAsync |
(voir §3.5) | OUI | NON | Corriger le mapping/guid Omika |
| 6A | Note CreationUser=0 | CreationUser=0 en base | API / OMIKA DATA | OmikaInterventionRepository.ResolveCreationUserAsync:1560 |
token CollaborateurId = PK numérique licence ; ResolveCreationUserAsync attend un GUID |
OUI | NON | Résoudre l'utilisateur depuis l'identité token (UserId/UT_PKID ou PK numérique) |
| 6B | Compteur notes = 0 | "0 note(s)" | LOCAL/API | InterventionDetailViewModel.RefreshFromApiAsync |
compteur = Notes.Count chargé en arrière-plan via API ; échec/guid → vide |
OUI (lié #5) | NON | même cause que #5 + retry |
| 7 | Compteur images/documents = 0 | "0 fichier(s)" | API / OMIKA DATA | MobileDocumentService.GetByInterventionAsync + OmikaDocumentRepository |
résolution guid intervention / filtre catégorie | OUI | NON | même cause que #5 + vérif filtre |
| 8 | Écran Images/Documents | titre redondant + bouton coupé | UI | Views/InterventionResourcesView.axaml:37,73 |
TextBlock "Images" redondant + StackPanel Orientation=Horizontal non responsive |
NON | NON | Supprimer titre + WrapPanel |
| 9 | Création email 404 | 404 modèles courriel | API | MobileApiClient.cs:761 + ModeleDocumentEndpoints.cs:18 |
Slash final manquant : mobile appelle GET /modele-mails, API enregistre GET /modele-mails/ (MapGroup("/modele-mails").MapGet("/")) |
NON (route commune) | NON (corrigé pour les 2) | MapGet("/") → MapGet("") (ou slash final côté mobile) |
| 10 | Rapports 404 | 404 modèles rapport | API | MobileApiClient.cs:771 + ModeleDocumentEndpoints.cs:28 |
Slash final manquant : GET /modele-rapports vs GET /modele-rapports/ |
NON | NON | idem #9 |
| 11 | Bouton téléphone inopérant | rien ne se lance | ANDROID | Services/PlatformPhoneLauncher.cs |
TopLevel.Launcher.LaunchUriAsync (ACTION_VIEW) ≠ ACTION_DIAL |
NON | NON | Launcher Android ACTION_DIAL (tel:) |
| 12 | Bouton email inopérant | aucun client mail | ANDROID | Services/PlatformEmailLauncher.cs |
LaunchUriAsync générique ≠ ACTION_SENDTO |
NON | NON | Launcher Android ACTION_SENDTO (mailto:) |
| 13 | Fiche équipe/collaborateurs responsive | email sur plusieurs lignes, cartes hautes | UI | CollaborateursView, CollaborateurDetailView, EquipeDetailView |
layout non responsive | NON | NON | Réorganiser grille responsive |
| 14 | Messages d'erreur techniques | "Response status code…404" affiché | UI | NouveauEmailViewModel, RapportChampsViewModel |
ErrorMessage = ex.Message brut |
NON | NON | Message utilisateur propre + log technique |
3. Cause exacte par problème
3.1 Carte OpenStreetMap bloquée
LieuMap.axaml.cs:97appelleOpenStreetMap.CreateTileLayer()(Mapsui). LeHttpTileSourcepar défaut de Mapsui chargehttps://tile.openstreetmap.org/{z}/{x}/{y}.pngvia unHttpClientsans User-Agent identifiant Omika → OSM rejette avec 403 (« volunteer-run servers »).- Le géocodeur
AddressGeocoder(Nominatim) a déjà un User-Agent correct (Omika-Mobile/1.0), mais pas le chargement des tuiles. - Pas de cache de tuiles configuré, pas d'attribution OSM visible, pas de fallback propre : l'image 403 brute est affichée.
3.5 RDV → « Ouvrir l'intervention » → introuvable
Flux tracé (lecture de code) :
RendezVousDetailViewModel.OpenIntervention→ navigation avecRendezVous.InterventionGuid(GUID).AgendaItem.InterventionGuid←AgendaService.MapToAgendaItem←RendezVousDetailsDto.InterventionId.RendezVousDetailsDto.InterventionId←MobileSnapshotService.MapRendezVous←MobileIdentityMapper.ResolveIdString(Intervention, dto.InterventionSourceReference).InterventionSourceReference←OmikaRendezVousMapper:{ NumericId: RV_IdInter, EntityGuid: ResolveGuid(interventionGuids, RV_IdInter) }.- Pour Omika,
MobileEntityIdentityResolver.CreateOmikaIdentityretournephysical.Guid. SiEntityGuidest null (GUID intervention non résolu),CreateOmikaIdentitylèveInvalidOperationException(« aucun GUID physique fourni »). - Navigation →
InterventionLocalService.GetInterventionByIdAsync(Guid)→ APIGET /interventions/{guid}→OmikaInterventionRepository.GetDetailsAsync(GUID →GUID IN (...)).
Conclusion de la trace statique (chaîne vérifiée de bout en bout) : la chaîne d'identité est correcte pour Omika :
OmikaRendezVousMapperrésoutInterventionSourceReference = { NumericId: RV_IdInter, EntityGuid: Intervention.GUID }en bulk (LoadInterventionGuidsAsync, requêteIN_PKID IN (...)).MobileSnapshotService.MapRendezVous→ResolveIdString→CreateOmikaIdentityretourne le GUID physique.AgendaItem.InterventionGuid= ce GUID ; navigation → APIGET /interventions/{guid}→GetDetailsAsync(GUID) ; repli local sur la table snapshotIntervention(peuplée parSnapshotApplierà partir deLoadInterventionsForRendezVousAsync, clé = GUID canonique).PhysicalGuidParser.GetStorageCandidatescouvre les formes D / N / D-tronquée.
Déclencheur exact restant = niveau données (à confirmer par les logs temporaires demandés au §5 de la recette) :
- soit
RV_IdInterpointe vers une intervention dont la colonneGUIDest NULL/invalide (→EntityGuidnull →CreateOmikaIdentitylèverait, OUInterventionIdnull) ; - soit l'intervention référencée n'existe pas dans
Intervention(référence orpheline) ; - soit un écart de format GUID entre
RV_IdInter→GUIDet l'Intervention.GUIDréel (byte-order HFSQL).
Logs temporaires à poser (correction #5) : RDV_PKID, RDV GUID, RV_IdInter, Intervention.GUID résolu, InterventionGuid envoyé à la navigation (type + valeur), requête SQL utilisée, résultat. Aucun IN_NumIntervention ne doit être réintroduit.
Les compteurs #6B et #7 passent par les mêmes endpoints API (GET /api/interventions/{guid}/notes, GET /api/interventions/{guid}/documents) : si la résolution d'intervention échoue, listes vides → « 0 note(s) » / « 0 fichier(s) ». #7 a en plus un filtre par catégories de document (BuildAllowedDocumentCategories + IsCategoryAllowed) qui peut vider la liste si le profil n'a pas le droit A.DOC.* correspondant.
3.6A Note CreationUser = 0
- Endpoint
AddNoteForInterventionAsync(InterventionEndpoints.cs:427) transmethttpContext.GetMobileIdentity()?.CollaborateurIdàInterventionService.AddNoteAsync→ResolveCreationUserAsync(collaborateurId). - En production,
LicenseEndpoints.BuildSession(LicenseEndpoints.cs:72) poseCollaborateurId = response.CollaborateurId=licence.CollaborateurId.ToString()(PK numérique du collaborateur, cf.LicenseService.BuildSuccessAsync:404). OmikaInterventionRepository.ResolveCreationUserAsync(:1560) faitGuid.TryParse(collaborateurGuid, out Guid): une valeur numérique échoue → retour0→CreationUser = 0.BatikInterventionRepository.ResolveCreationUserAsync(:1513) décode un GUID synthétique Batik : une valeur numérique échoue aussi (Guid.TryParse→ 0).- Racine : incohérence de type entre la claim
CollaborateurIddu token (numérique en prod) et l'API deResolveCreationUserAsync(attend un GUID).
3.9 / 3.10 Email / Rapport 404 — CAUSE EXACTE CONFIRMÉE
- Slash final manquant.
MobileApiClient.GetModeleMailsAsyncappelleGET {BaseUrl}/modele-mails(sans slash), mais l'API enregistre la routeGET /modele-mails/(avec slash) viaMapGroup("/modele-mails").MapGet("/")(ModeleDocumentEndpoints.cs:18). En ASP.NET Core minimal API,MapGroup("/x").MapGet("/")produit/x/et ne matche pas/x→ 404. - Idem
GetModeleRapportsAsync→GET /modele-rapportsvsGET /modele-rapports/(ModeleDocumentEndpoints.cs:28). - Les autres routes auditées (
/mot-clefs,/generation/email,/generation/rapport,/api/rapports/champs,/api/mails/champs,/api/mails,/api/mails/apercu,/api/rapports) matchent exactement. Le préfixe/apiest cohérent, les routes sont bien enregistrées (Program.cs:243-244), etMobileAuthMiddlewarene renvoie jamais 404. - Correction :
MapGet("/")→MapGet("")dansModeleDocumentEndpoints(canonique, fait matcher/modele-mailssans slash) — sans impact Batik (route schéma-agnostique, corrige les deux schémas). Alternative : ajouter le slash final dansMobileApiClient(l.761/771). - Référence historique
Mail_Modele/ModeleRapport WHERE MRP_EnvoiMob=1→ routes REST actuelles (cf.docs/modeles-email-rapport.md).
3.11 / 3.12 Téléphone / Email inopérants
PlatformPhoneLauncher.DialAsyncetPlatformEmailLauncher.ComposeAsyncutilisentTopLevel.Launcher.LaunchUriAsync(uri)(launcher générique Avalonia). Sur Android, ce launcher utiliseACTION_VIEW, qui ne lance pas le composeur natif (tel:) ni le client mail de façon fiable.- Aucun launcher Android spécifique n'existe (
src/Omika.App.Androidne contient pas de Phone/Email launcher). - Les ViewModels ignorent le retour
bool: aucun message si aucun handler n'est disponible.
3.14 Messages d'erreur techniques
NouveauEmailViewModel.LoadAsyncetRapportChampsViewModel.LoadAsyncfontcatch (Exception ex) { ErrorMessage = ex.Message; };EnsureSuccessStatusCode()lèveHttpRequestExceptiondont le message brut (« Response status code does not indicate success: 404 (Not Found). ») est affiché tel quel.
4. Classification
| # | Classification |
|---|---|
| 1 | ANDROID/UI (commun, non spécifique données) |
| 2 | UI |
| 3 | UI |
| 4 | UI |
| 5 | OMIKA DATA + API (résolution identité intervention) |
| 6A | API + OMIKA DATA (résolution utilisateur) |
| 6B | LOCAL/API (compteur = source API) |
| 7 | API + OMIKA DATA (documents) |
| 8 | UI |
| 9 | API (route/déploiement) |
| 10 | API (route/déploiement) |
| 11 | ANDROID (launcher) |
| 12 | ANDROID (launcher) |
| 13 | UI |
| 14 | UI |
5. Preuve de non-régression Batik
- Les corrections UI/Android (#1, #2, #3, #4, #8, #11, #12, #13, #14) ne touchent aucune requête Batik : elles portent sur les
.axaml, les launchers Android et les messages d'erreur, partagés entre les deux schémas. - Les corrections données (#5, #6A, #7, #9, #10) seront appliquées uniquement dans les providers/repositories/mappers Omika (
src/Omika.Data/Repositories/Omika/*,Mapping/Omika/*) ou dans la couche d'identité commune, sans modifierBatik*RepositoryniBatik*Mapper. ResolveCreationUserAsyncBatik est déjà isolé dansBatikInterventionRepository; sa correction éventuelle (acceptation du PK numérique) doit être faite symétriquement sans casser le décodage du GUID synthétique Batik (test à prévoir).- Les routes API (#9, #10) sont communes : l'ajout d'un alias
/api/modele-mails(si retenu) n'affecte pas le comportement Batik, qui utilise la même API.
6. Routes HTTP actuellement appelées (MobileApiClient → Omika.Api)
| Méthode client | URL appelée | Route API | Match |
|---|---|---|---|
| GetModeleMailsAsync | GET /modele-mails |
ModeleDocumentEndpoints /modele-mails |
✅ (préfixe sans /api) |
| GetModeleRapportsAsync | GET /modele-rapports |
ModeleDocumentEndpoints /modele-rapports |
✅ |
| GetMotClefsAsync | GET /mot-clefs |
ModeleDocumentEndpoints /mot-clefs |
✅ |
| GetEquipesAsync | GET /equipes |
EquipeEndpoints /equipes |
✅ |
| GetAllEquipesAsync | GET /equipes/all |
EquipeEndpoints /equipes/all |
✅ |
| GetCollaborateurByIdAsync | GET /collaborateurs/{id} |
CollaborateurEndpoints |
✅ |
| GetRendezVousByIdAsync | GET /rendez-vous/{id} |
RendezVousEndpoints |
✅ |
| GetInterventionByIdAsync | GET /interventions/{id} |
InterventionEndpoints |
✅ |
| GetDossierByIdAsync | GET /dossiers/{id} |
DossierEndpoints |
✅ |
| GetClientByIdAsync | GET /clients/{id} |
ClientEndpoints |
✅ |
| GetSiteByIdAsync | GET /sites/{id} |
SiteEndpoints |
✅ |
| GetContactByIdAsync | GET /contacts/{id} |
ContactEndpoints |
✅ |
| GetCompteRenduByRendezVousAsync | GET /rendez-vous/{id}/compte-rendu |
CompteRenduEndpoints |
✅ |
| GetNotesByInterventionAsync | GET /api/interventions/{id}/notes |
InterventionEndpoints mobileGroup |
✅ |
| AddNoteByInterventionAsync | POST /api/interventions/{id}/notes |
InterventionEndpoints mobileGroup |
✅ |
| GetDocumentsByInterventionAsync | GET /api/interventions/{id}/documents |
InterventionEndpoints mobileGroup |
✅ |
| GetRendezVousByInterventionAsync | GET /api/interventions/{id}/rendez-vous |
InterventionEndpoints mobileGroup |
✅ |
| GetEmailsByInterventionAsync | GET /api/interventions/{id}/emails |
InterventionEndpoints mobileGroup |
✅ |
| AddEmailByInterventionAsync | POST /api/interventions/{id}/emails |
InterventionEndpoints mobileGroup |
✅ |
| GetSmtpConfigurationsAsync | GET /api/mails/configurations-smtp |
RapportChampEndpoints |
✅ |
| GetRapportChampsAsync | POST /api/rapports/champs |
RapportChampEndpoints |
✅ |
| GetMailChampsAsync | POST /api/mails/champs |
RapportChampEndpoints |
✅ |
| CreateEmailModeleAsync | POST /api/mails |
RapportChampEndpoints |
✅ |
| GetEmailModeleApercuAsync | POST /api/mails/apercu |
RapportChampEndpoints |
✅ |
| CreateRapportAsync | POST /api/rapports |
RapportChampEndpoints |
✅ |
| GenerateEmailAsync | POST /generation/email |
ModeleDocumentEndpoints |
✅ |
| GenerateRapportAsync | POST /generation/rapport |
ModeleDocumentEndpoints |
✅ |
| SearchInterventionsAsync | POST /api/interventions/search |
InterventionEndpoints mobileGroup |
✅ |
| GetInterventionsByIdsAsync | POST /api/interventions/by-ids |
InterventionEndpoints mobileGroup |
✅ |
| SearchDossiersAsync | GET /api/dossiers |
DossierEndpoints mobileGroup |
✅ |
| SearchClientsAsync | GET /api/clients |
ClientEndpoints mobileGroup |
✅ |
| SearchSitesAsync | GET /api/sites |
SiteEndpoints mobileGroup |
✅ |
| SearchCollaborateursAsync | GET /collaborateurs |
CollaborateurEndpoints |
✅ (sans /api) |
| GetSuivisTempsAsync | GET /api/suivis-temps |
SuiviTempsEndpoints |
✅ |
| GetBootstrapAsync | GET /api/mobile/bootstrap |
MobileBootstrapEndpoints |
✅ |
| GetSnapshotAsync | GET /api/sync/snapshot |
SyncEndpoints |
✅ |
| SendOperationAsync | POST /api/sync/operations |
SyncEndpoints |
✅ |
| GetStatusAsync | GET /api/sync/status/{id} |
SyncEndpoints |
✅ |
| ActivateAsync | POST /api/license/activate |
LicenseEndpoints |
✅ |
| ValidateAsync | POST /api/license/validate |
LicenseEndpoints |
✅ |
| GetDevelopmentSessionAsync | POST /api/dev/session |
DevelopmentAuthEndpoints (Dev only) |
✅ |
Incohérence constatée : le mobile utilise un mélange de routes avec
/apiet sans/api(ex./modele-mailssans préfixe vs/api/mails). Les routes « modèle » (/modele-mails,/modele-rapports,/mot-clefs,/generation/*) sont les seules sans/api. C'est cohérent avec le code API actuel mais fragile et à uniformiser.
7. Requêtes SQL concernées (côté API, base HFSQL)
| Problème | Requête |
|---|---|
| #5 (intervention par GUID) | SELECT … FROM Intervention WHERE GUID IN (?) (formes D/N/tronquée via PhysicalGuidParser.GetStorageCandidates) — OmikaInterventionRepository.GetDetailsAsync |
| #5 (RDV→intervention) | SELECT … FROM RDV_Planning … LEFT JOIN Intervention I ON RV.RV_IdInter = I.IN_PKID (résolution bulk RV_IdInter → Intervention.GUID) — OmikaRendezVousRepository |
| #6A (user) | SELECT Utilisateur.UT_PKID FROM Utilisateur INNER JOIN Collaborateur ON Collaborateur.CO_PKID = Utilisateur.UT_IDCollaborateur WHERE Collaborateur.GUID IN (?) — OmikaInterventionRepository.ResolveCreationUserAsync |
| #6A (Batik) | SELECT UT_PKID FROM Utilisateur WHERE UT_IDCollaborateur = ? — BatikInterventionRepository.ResolveCreationUserAsync |
| #6B/#7 (notes) | SELECT MI_PKID, GUID, MI_Message, CreationDate, CreationUser FROM MessageInterne WHERE MI_IDIntervention = ? AND (MI_IDTypeMessage IS NULL OR MI_IDTypeMessage <= 10) — OmikaInterventionRepository.GetNotesAsync |
| #7 (documents) | résolution intervention GUID → IN_PKID puis requête documents (filtre DC_IDIntervention / catégorie / état) — OmikaDocumentRepository.GetByInterventionAsync |
| #7 (catégories) | BuildAllowedDocumentCategories → filtrage par clés T.DOC.* selon droits A.DOC.* |
8. Inventaire des icônes à remplacer (extrait — inventaire complet en cours)
| Fichier | Ligne | Actuel | Fonction | Proposition |
|---|---|---|---|---|
Views/RendezVousDetailView.axaml |
122 | ⌖ Itinéraire (emoji) |
Itinéraire | Path "navigation/itinéraire" monochrome |
Views/RendezVousDetailView.axaml |
134 | ⚒ Ouvrir l'intervention |
Ouvrir intervention | Path "marteau/outil" monochrome |
Views/RendezVousDetailView.axaml |
139 | ▧ Compte rendu |
Compte rendu | Path "document" monochrome |
Views/InterventionResourcesView.axaml |
37/73 | titre texte redondant | (titre) | supprimer |
Controls/LieuMap.axaml |
25 | ⌖ (TextBlock) |
épingle | Path "pin" monochrome |
ViewModels/NouveauEmailViewModel.cs |
144 | ✉ Email vierge |
modèle vierge | (libellé, pas d'icône) |
ViewModels/NouveauEmailViewModel.cs |
888 | 📷 {Nom} |
pièce jointe photo | Path "image" |
ViewModels/RapportChampsViewModel.cs |
606 | ✓ / ! (Indicateur) |
état rubrique | Path check/alert monochrome |
Views/AgendaEquipeView.axaml |
(filtre Équipe) | icône groupe colorée | filtre équipe | Path "group" monochrome |
Views/InterventionDocumentsView.axaml |
17 | ← Retour |
retour | Path "arrow-left" |
Les contrôles CallButton.axaml et EmailButton.axaml utilisent déjà des Path monochromes Fill="{DynamicResource TextPrimaryBrush}" : conformes, à conserver comme référence visuelle.
9. Propositions de SVG monochrome (famille unique)
Famille retenue : pictogrammes "Line/Outline" sobres (traits fins, style Lucide/Material Outlined), un seul style (trait 1.5–2, pas de remplissage multicolore), couleur portée par Foreground="{DynamicResource PrimaryBrush}" ou Fill="{DynamicResource …}" — jamais codée en dur.
| Pictogramme | Usage | Path (24×24 viewBox, données type Material) |
|---|---|---|
| Téléphone | appel | M6.62,10.79c1.44,2.83 … (déjà dans CallButton) |
| courriel | M20,4H4C2.9,4 … (déjà dans EmailButton) |
|
| Itinéraire/GPS | itinéraire | navigation arrow / pin |
| Intervention (marteau/outil) | ouvrir intervention | wrench/hammer |
| Rapport | rapport | document + lignes |
| Note | note | edit/pencil |
| Image | photo | image frame |
| Document | document | file |
| Collaborateurs | personne | person outline |
| Équipe | groupe | people/group outline |
| Calendrier | agenda | calendar |
| Paramètres | paramètres | gear |
| Synchronisation | sync | refresh/circular arrows |
| Retour | retour | arrow-left |
| Ajouter | ajouter | plus |
| Télécharger | télécharger | download arrow |
| Épingle | carte | location pin |
Chaque pictogramme sera matérialisé par un Path Avalonia (monochrome) ou un StreamGeometry, avec Fill/Foreground en DynamicResource pour rester compatible clair/sombre.
8bis. Couleurs codées en dur à remplacer par DynamicResource
| Fichier | Ligne | Valeur | Remplacer par |
|---|---|---|---|
Views/RendezVousDetailView.axaml |
143 | #E53935 / White (bouton Annuler) |
DangerBrush / OnDanger |
Views/RapportChampsView.axaml |
108/115/122 | #22c55e / #f59e0b / #ef4444 |
SuccessBrush / WarningBrush / DangerBrush |
Views/RapportChampsView.axaml |
253 | #e5e7eb |
SurfaceTertiaryBrush |
Views/RapportChampsView.axaml |
138,233,258 | ThemeForegroundLowBrush (clé INEXISTANTE dans DesignTokens) |
TextMutedBrush |
| 10 fichiers (badges héros) | — | #26FFFFFF |
token OnPrimaryContainer/HeroChip |
Controls/ImageEditorControl.axaml |
— | #E53935…, #FFFFFF |
ressources centralisées |
Views/*Converter.cs (NavItemTextBrushConverter, SyncStateToBrushConverter) |
— | Brushes.Gray fallback |
ressource TextMutedBrush |
8ter. Correctifs appliqués (lot 1 — compilation Android 0 erreur vérifiée)
| # | Correctif | Fichiers modifiés | Vérification |
|---|---|---|---|
| 9/10 | MapGet("/") → MapGet("") (route sans slash final) |
src/Omika.Api/Endpoints/ModeleDocumentEndpoints.cs |
Omika.Api 0 erreur |
| 6A | ResolveCreationUserAsync accepte PK numérique + GUID (Omika & Batik) |
OmikaInterventionRepository.cs, BatikInterventionRepository.cs |
Omika.Data 0 erreur |
| 11/12 | Launchers natifs Android ACTION_DIAL/ACTION_SENDTO + hook PlatformLaunchers |
AndroidPhoneLauncher.cs, AndroidEmailLauncher.cs (nouveaux), PlatformLaunchers.cs (nouveau), AppServices.cs, Application.cs |
Android 0 erreur |
| 1 | User-Agent OSM explicite OmikaMobile/1.0 (+https://www.applilogik.fr) |
Controls/LieuMap.axaml.cs |
Omika.App 0 erreur |
| 3 | Boutons d'en-tête translucides heroAction + icônes Path monochrome |
Views/RendezVousDetailView.axaml, Styles/Controls.axaml |
Omika.App 0 erreur |
| 8 | Suppression titre redondant + WrapPanel (boutons wrappables) |
Views/InterventionResourcesView.axaml |
Omika.App 0 erreur |
| 14 | Messages utilisateur propres + log technique | NouveauEmailViewModel.cs, RapportChampsViewModel.cs |
Omika.App 0 erreur |
| 5 | Logs temporaires de traçage RDV→intervention | RendezVousDetailViewModel.cs, InterventionLocalService.cs |
Omika.App 0 erreur |
Lot 2 — Icônes monochromes + couleurs + responsive (compilation 0 erreur)
| # | Correctif | Fichiers |
|---|---|---|
| 2 | Bug clé ThemeForegroundLowBrush (inexistante) → TextMutedBrush |
RapportChampsView.axaml, NouveauEmailView.axaml |
| 2 | Couleurs sémantiques en dur → Success/Warning/Danger/SurfaceTertiaryBrush |
RapportChampsView.axaml |
| 2 | Icônes de tuiles emoji → Path monochrome PrimaryTextColorBrush (✎ ▧ ✉ 🛡 📄) |
InterventionDetailView.axaml |
| 4 | Icône groupe 👥 → Path « people » monochrome |
AgendaView.axaml |
| 2 | Icône calendrier 📅 → Path « calendar » monochrome |
Controls/DateField.axaml |
| 2 | Tokens HeroChipBrush / HeroBorderBrush ajoutés |
DesignTokens.axaml |
| 13 | Email non tronqué (TextWrapping) fiche collaborateur + liste collaborateurs |
CollaborateurDetailView.axaml, CollaborateursView.axaml |
Reste (lots suivants) :
#26FFFFFF→HeroChipBrushsur les badges (cosmétique), autres emoji (↻ ⌖ ⚠ ⓘ 🗑 ✎ 🖼 🔒 ★/☆ ✉ + → ←), responsiveFilterGridagenda équipe, et les correctifs de données#5/#6B/#7(résolution intervention + filtre catégoriesT.DOC.*).
Build Android :
dotnet build src\Omika.App.Android\Omika.App.Android.csproj -c Debug -m:1 -nodeReuse:false→ 0 erreur (SDK 10.0.302 viaglobal.json). Les builds nécessitent-m:1 -nodeReuse:false(bug de nœud MSBuild parallèle dans l'environnement) et un accès élargi pour la télémétrie Avalonia (écrit hors workspace).
9. État des sous-analyses
- ✅ Route API (#9/#10) : confirmé — slash final manquant (
/modele-mailsvs/modele-mails/). - ✅ Icônes + responsive (#2/#3/#4/#8/#13) : confirmé — inventaire ~40 emoji/Unicode, aucune couleur codée en dur dans les Path, liste des couleurs en dur à remplacer (dont clé manquante
ThemeForegroundLowBrush). - ✅ Trace RDV→intervention (#5/#6B/#7) : chaîne d'identité vérifiée de bout en bout (correcte) ; déclencheur exact = niveau données, à confirmer par les logs temporaires prévus en correction.