feat(auth): restore authenticated user profile
This commit is contained in:
@@ -1,10 +1,18 @@
|
||||
/// Identité simulée utilisée tant que l'authentification Azure AD n'est pas branchée.
|
||||
import "services/auth_service.dart";
|
||||
|
||||
/// Identité de secours utilisée lorsque aucun profil de session n'est chargé.
|
||||
class DemoIdentity {
|
||||
DemoIdentity._();
|
||||
|
||||
static const userName = "Lucas Martin";
|
||||
static const userInitials = "LM";
|
||||
static const userSubtitle = "Étudiant · B2 Informatique · Ensitech Cergy";
|
||||
static const campusTag = "Saint-Christophe · Cergy";
|
||||
static String get userName =>
|
||||
AuthService.currentProfile?.fullName ?? "Lucas Martin";
|
||||
static String get userInitials =>
|
||||
AuthService.currentProfile?.initials ?? "LM";
|
||||
static String get userSubtitle =>
|
||||
AuthService.currentProfile?.subtitle ??
|
||||
"Étudiant · B2 Informatique · Ensitech Cergy";
|
||||
static String get campusTag =>
|
||||
AuthService.currentProfile?.campusTag ?? "Saint-Christophe · Cergy";
|
||||
|
||||
static const loginCampusTag = "Ensitech · Cergy";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
class AuthenticatedProfile {
|
||||
const AuthenticatedProfile({
|
||||
required this.id,
|
||||
required this.nom,
|
||||
required this.prenom,
|
||||
required this.email,
|
||||
required this.classe,
|
||||
required this.roleCode,
|
||||
required this.roleLabel,
|
||||
required this.campusId,
|
||||
required this.campusNom,
|
||||
required this.campusVille,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String nom;
|
||||
final String prenom;
|
||||
final String email;
|
||||
final String? classe;
|
||||
final String roleCode;
|
||||
final String roleLabel;
|
||||
final int campusId;
|
||||
final String campusNom;
|
||||
final String campusVille;
|
||||
|
||||
factory AuthenticatedProfile.fromApi(dynamic reponse) {
|
||||
if (reponse is! Map || reponse["data"] is! Map) {
|
||||
throw const FormatException("Profil utilisateur invalide.");
|
||||
}
|
||||
|
||||
final data = reponse["data"] as Map;
|
||||
final role = data["role"];
|
||||
final campus = data["campus"];
|
||||
if (role is! Map || campus is! Map) {
|
||||
throw const FormatException("Role ou campus utilisateur invalide.");
|
||||
}
|
||||
|
||||
final id = data["id"];
|
||||
final campusId = campus["id"];
|
||||
if (id is! int || campusId is! int) {
|
||||
throw const FormatException("Identifiant utilisateur invalide.");
|
||||
}
|
||||
|
||||
final classe = data["classe"];
|
||||
if (classe != null && classe is! String) {
|
||||
throw const FormatException("Classe utilisateur invalide.");
|
||||
}
|
||||
|
||||
return AuthenticatedProfile(
|
||||
id: id,
|
||||
nom: _requiredString(data, "nom"),
|
||||
prenom: _requiredString(data, "prenom"),
|
||||
email: _requiredString(data, "email"),
|
||||
classe: classe as String?,
|
||||
roleCode: _requiredString(role, "code"),
|
||||
roleLabel: _requiredString(role, "libelle"),
|
||||
campusId: campusId,
|
||||
campusNom: _requiredString(campus, "nom"),
|
||||
campusVille: _requiredString(campus, "ville"),
|
||||
);
|
||||
}
|
||||
|
||||
String get fullName => "$prenom $nom";
|
||||
|
||||
String get initials {
|
||||
final first = prenom.trim().isEmpty ? "" : prenom.trim()[0];
|
||||
final last = nom.trim().isEmpty ? "" : nom.trim()[0];
|
||||
return "$first$last".toUpperCase();
|
||||
}
|
||||
|
||||
String get campusTag => "$campusNom · $campusVille";
|
||||
|
||||
String get subtitle {
|
||||
final details = <String>[roleLabel];
|
||||
final studentClass = classe?.trim();
|
||||
if (studentClass != null && studentClass.isNotEmpty) {
|
||||
details.add(studentClass);
|
||||
}
|
||||
details.add(campusTag);
|
||||
return details.join(" · ");
|
||||
}
|
||||
|
||||
static String _requiredString(Map source, String key) {
|
||||
final value = source[key];
|
||||
if (value is! String || value.trim().isEmpty) {
|
||||
throw FormatException("Champ utilisateur invalide : $key.");
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,8 @@ class ConfirmationScreen extends StatelessWidget {
|
||||
final dateEmprunt = emprunt.dateEmprunt;
|
||||
final date =
|
||||
"${_deuxChiffres(dateEmprunt.day)}/${_deuxChiffres(dateEmprunt.month)}/${dateEmprunt.year}";
|
||||
final heure = "${_deuxChiffres(dateEmprunt.hour)}:${_deuxChiffres(dateEmprunt.minute)}";
|
||||
final heure =
|
||||
"${_deuxChiffres(dateEmprunt.hour)}:${_deuxChiffres(dateEmprunt.minute)}";
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: EnsupColors.soft,
|
||||
@@ -48,7 +49,7 @@ class ConfirmationScreen extends StatelessWidget {
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: Column(
|
||||
children: [
|
||||
const EnsupTopBar(
|
||||
EnsupTopBar(
|
||||
brandText: "EME · Confirmation",
|
||||
campusTag: DemoIdentity.campusTag,
|
||||
),
|
||||
@@ -63,7 +64,10 @@ class ConfirmationScreen extends StatelessWidget {
|
||||
),
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 32),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 28,
|
||||
vertical: 32,
|
||||
),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 560),
|
||||
child: Column(
|
||||
@@ -75,10 +79,15 @@ class ConfirmationScreen extends StatelessWidget {
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: FilledButton.icon(
|
||||
onPressed: () => Navigator.of(context).popUntil(
|
||||
(route) => route.settings.name == "home",
|
||||
onPressed: () =>
|
||||
Navigator.of(context).popUntil(
|
||||
(route) =>
|
||||
route.settings.name == "home",
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.home_outlined,
|
||||
size: 18,
|
||||
),
|
||||
icon: const Icon(Icons.home_outlined, size: 18),
|
||||
label: Text(
|
||||
"Retour à l'accueil",
|
||||
style: GoogleFonts.darkerGrotesque(
|
||||
@@ -88,9 +97,13 @@ class ConfirmationScreen extends StatelessWidget {
|
||||
),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: EnsupColors.cyan,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderRadius: BorderRadius.circular(
|
||||
10,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -118,7 +131,9 @@ class ConfirmationScreen extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(36),
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(colors: [Color(0xFFF0FDF4), Color(0xFFECFDF5)]),
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xFFF0FDF4), Color(0xFFECFDF5)],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: const Color(0xFFBBF7D0), width: 2),
|
||||
),
|
||||
@@ -129,7 +144,9 @@ class ConfirmationScreen extends StatelessWidget {
|
||||
height: 72,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: const LinearGradient(colors: [Color(0xFF16A34A), Color(0xFF22C55E)]),
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xFF16A34A), Color(0xFF22C55E)],
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0xFF16A34A).withValues(alpha: 0.3),
|
||||
@@ -153,7 +170,10 @@ class ConfirmationScreen extends StatelessWidget {
|
||||
Text(
|
||||
"Votre emprunt a bien été enregistré. Vous pouvez récupérer le matériel.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.titilliumWeb(fontSize: 14, color: const Color(0xFF166534)),
|
||||
style: GoogleFonts.titilliumWeb(
|
||||
fontSize: 14,
|
||||
color: const Color(0xFF166534),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -191,7 +211,8 @@ class ConfirmationScreen extends StatelessWidget {
|
||||
_rrow("Date d'emprunt", date),
|
||||
_rrow("Heure", heure),
|
||||
_rrow("Campus", item.campus),
|
||||
if (note != null) _rrow("Note", note, valueColor: const Color(0xFFD97706)),
|
||||
if (note != null)
|
||||
_rrow("Note", note, valueColor: const Color(0xFFD97706)),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -203,7 +224,13 @@ class ConfirmationScreen extends StatelessWidget {
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(cle, style: GoogleFonts.titilliumWeb(fontSize: 13, color: EnsupColors.muted)),
|
||||
Text(
|
||||
cle,
|
||||
style: GoogleFonts.titilliumWeb(
|
||||
fontSize: 13,
|
||||
color: EnsupColors.muted,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
|
||||
@@ -71,7 +71,7 @@ class HomeScreen extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const ProfileCard(
|
||||
ProfileCard(
|
||||
name: DemoIdentity.userName,
|
||||
subtitle: DemoIdentity.userSubtitle,
|
||||
initials: DemoIdentity.userInitials,
|
||||
|
||||
@@ -21,6 +21,14 @@ class LoginScreen extends StatefulWidget {
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
bool _connexionEnCours = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (AuthService.mode == AuthenticationMode.azure) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _restaurerSession());
|
||||
}
|
||||
}
|
||||
|
||||
void _ouvrirEtudiant() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
@@ -39,7 +47,44 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
void _identifierParQr() {
|
||||
void _ouvrirPourRole(String roleCode) {
|
||||
switch (roleCode) {
|
||||
case "ETUDIANT":
|
||||
_ouvrirEtudiant();
|
||||
case "RESPONSABLE":
|
||||
_ouvrirResponsable();
|
||||
default:
|
||||
throw StateError("Role utilisateur non autorise.");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _restaurerSession() async {
|
||||
setState(() => _connexionEnCours = true);
|
||||
try {
|
||||
if (!await AuthService.hasMicrosoftAccount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final reponse = await ApiClient.get("/auth/me");
|
||||
final profile = AuthService.setProfileFromApi(reponse);
|
||||
if (mounted) {
|
||||
_ouvrirPourRole(profile.roleCode);
|
||||
}
|
||||
} catch (_) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text("La session Microsoft doit etre renouvelee."),
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _connexionEnCours = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _identifierParQr() async {
|
||||
if (_connexionEnCours) return;
|
||||
if (AuthService.mode == AuthenticationMode.azure) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -52,8 +97,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
return;
|
||||
}
|
||||
|
||||
AuthService.selectDemoStudent();
|
||||
_ouvrirEtudiant();
|
||||
await _connecterMicrosoft(responsableDemo: false);
|
||||
}
|
||||
|
||||
Future<void> _connecterMicrosoft({required bool responsableDemo}) async {
|
||||
@@ -64,27 +108,17 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
if (AuthService.mode == AuthenticationMode.demo) {
|
||||
if (responsableDemo) {
|
||||
AuthService.selectDemoResponsable();
|
||||
_ouvrirResponsable();
|
||||
} else {
|
||||
AuthService.selectDemoStudent();
|
||||
_ouvrirEtudiant();
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
await AuthService.signInWithMicrosoft();
|
||||
}
|
||||
|
||||
await AuthService.signInWithMicrosoft();
|
||||
final reponse = await ApiClient.get("/auth/me");
|
||||
final roleCode = _roleCode(reponse);
|
||||
final profile = AuthService.setProfileFromApi(reponse);
|
||||
if (!mounted) return;
|
||||
|
||||
switch (roleCode) {
|
||||
case "ETUDIANT":
|
||||
_ouvrirEtudiant();
|
||||
case "RESPONSABLE":
|
||||
_ouvrirResponsable();
|
||||
default:
|
||||
throw StateError("Role utilisateur non autorise.");
|
||||
}
|
||||
_ouvrirPourRole(profile.roleCode);
|
||||
} catch (error) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
@@ -97,25 +131,6 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
String _roleCode(dynamic reponse) {
|
||||
if (reponse is! Map || reponse["data"] is! Map) {
|
||||
throw StateError("Profil utilisateur invalide.");
|
||||
}
|
||||
|
||||
final data = reponse["data"] as Map;
|
||||
if (data["role"] is! Map) {
|
||||
throw StateError("Role utilisateur absent.");
|
||||
}
|
||||
|
||||
final role = data["role"] as Map;
|
||||
final code = role["code"];
|
||||
if (code is! String) {
|
||||
throw StateError("Role utilisateur invalide.");
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import "package:flutter/material.dart";
|
||||
import "package:google_fonts/google_fonts.dart";
|
||||
|
||||
import "../demo_identity.dart";
|
||||
import "../services/auth_service.dart";
|
||||
import "../services/responsable_service.dart";
|
||||
import "../theme/ensup_colors.dart";
|
||||
@@ -463,6 +462,12 @@ class _ResponsableTopBar extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final profile = AuthService.currentProfile;
|
||||
final campusTag = profile?.campusTag ?? "Ensitech · Cergy";
|
||||
final initials = profile?.initials ?? "KB";
|
||||
final userName = profile?.fullName ?? "Karim Benali";
|
||||
final roleLabel = profile?.roleLabel ?? "Responsable matériel";
|
||||
|
||||
return Container(
|
||||
height: 68,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
@@ -533,7 +538,7 @@ class _ResponsableTopBar extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
DemoIdentity.loginCampusTag,
|
||||
campusTag,
|
||||
style: GoogleFonts.titilliumWeb(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -565,7 +570,7 @@ class _ResponsableTopBar extends StatelessWidget {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Text(
|
||||
"KB",
|
||||
initials,
|
||||
style: GoogleFonts.darkerGrotesque(
|
||||
color: Colors.white,
|
||||
fontSize: 13,
|
||||
@@ -580,7 +585,7 @@ class _ResponsableTopBar extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Karim Benali",
|
||||
userName,
|
||||
style: GoogleFonts.titilliumWeb(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -588,7 +593,7 @@ class _ResponsableTopBar extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"Responsable matériel",
|
||||
roleLabel,
|
||||
style: GoogleFonts.titilliumWeb(
|
||||
fontSize: 11,
|
||||
color: EnsupColors.muted,
|
||||
|
||||
@@ -10,7 +10,11 @@ import "../widgets/brand_corners.dart";
|
||||
/// Résultat de la restitution (RG17-RG20) : conforme (succès) ou non conforme
|
||||
/// (anomalie détectée + notification au responsable).
|
||||
class ResultatRetourScreen extends StatelessWidget {
|
||||
const ResultatRetourScreen({super.key, required this.emprunt, required this.anomalies});
|
||||
const ResultatRetourScreen({
|
||||
super.key,
|
||||
required this.emprunt,
|
||||
required this.anomalies,
|
||||
});
|
||||
|
||||
final EmpruntItem emprunt;
|
||||
final List<AnomalieRetour> anomalies;
|
||||
@@ -48,7 +52,7 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: Column(
|
||||
children: [
|
||||
const EnsupTopBar(
|
||||
EnsupTopBar(
|
||||
brandText: "EME · Résultat du contrôle",
|
||||
campusTag: DemoIdentity.campusTag,
|
||||
),
|
||||
@@ -63,7 +67,10 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
),
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 32),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 28,
|
||||
vertical: 32,
|
||||
),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 560),
|
||||
child: Column(
|
||||
@@ -73,10 +80,15 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: FilledButton.icon(
|
||||
onPressed: () => Navigator.of(context).popUntil(
|
||||
(route) => route.settings.name == "home",
|
||||
onPressed: () =>
|
||||
Navigator.of(context).popUntil(
|
||||
(route) =>
|
||||
route.settings.name == "home",
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.home_outlined,
|
||||
size: 18,
|
||||
),
|
||||
icon: const Icon(Icons.home_outlined, size: 18),
|
||||
label: Text(
|
||||
"Retour à l'accueil",
|
||||
style: GoogleFonts.darkerGrotesque(
|
||||
@@ -86,9 +98,13 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: EnsupColors.cyan,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderRadius: BorderRadius.circular(
|
||||
10,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -116,7 +132,9 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(36),
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(colors: [Color(0xFFF0FDF4), Color(0xFFECFDF5)]),
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xFFF0FDF4), Color(0xFFECFDF5)],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: const Color(0xFFBBF7D0), width: 2),
|
||||
),
|
||||
@@ -127,7 +145,9 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
height: 72,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(colors: [Color(0xFF16A34A), Color(0xFF22C55E)]),
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xFF16A34A), Color(0xFF22C55E)],
|
||||
),
|
||||
),
|
||||
child: const Icon(Icons.check, color: Colors.white, size: 36),
|
||||
),
|
||||
@@ -144,7 +164,10 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
Text(
|
||||
"L'emprunt a été clôturé et le matériel est de nouveau disponible.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.titilliumWeb(fontSize: 14, color: const Color(0xFF166534)),
|
||||
style: GoogleFonts.titilliumWeb(
|
||||
fontSize: 14,
|
||||
color: const Color(0xFF166534),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -164,12 +187,19 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: EnsupColors.red.withValues(alpha: 0.06),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: EnsupColors.red.withValues(alpha: 0.2), width: 1.5),
|
||||
border: Border.all(
|
||||
color: EnsupColors.red.withValues(alpha: 0.2),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Icon(Icons.warning_amber_rounded, color: EnsupColors.red, size: 24),
|
||||
const Icon(
|
||||
Icons.warning_amber_rounded,
|
||||
color: EnsupColors.red,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@@ -227,7 +257,10 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
anomalie.etatRetour,
|
||||
valeurCouleur: EnsupColors.red,
|
||||
),
|
||||
_drow("Matériel", "${emprunt.materiel.nom} · ${emprunt.materiel.reference}"),
|
||||
_drow(
|
||||
"Matériel",
|
||||
"${emprunt.materiel.nom} · ${emprunt.materiel.reference}",
|
||||
),
|
||||
_drow("Emprunteur", DemoIdentity.userName),
|
||||
_drow("Date détection", date),
|
||||
],
|
||||
@@ -237,7 +270,11 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
Text(
|
||||
"Le responsable matériel traitera cette anomalie. Vous pouvez retourner à l'accueil.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.titilliumWeb(fontSize: 13, color: EnsupColors.muted, height: 1.6),
|
||||
style: GoogleFonts.titilliumWeb(
|
||||
fontSize: 13,
|
||||
color: EnsupColors.muted,
|
||||
height: 1.6,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -249,7 +286,13 @@ class ResultatRetourScreen extends StatelessWidget {
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(cle, style: GoogleFonts.titilliumWeb(fontSize: 13, color: EnsupColors.muted)),
|
||||
Text(
|
||||
cle,
|
||||
style: GoogleFonts.titilliumWeb(
|
||||
fontSize: 13,
|
||||
color: EnsupColors.muted,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "../models/authenticated_profile.dart";
|
||||
import "msal_bridge.dart";
|
||||
|
||||
enum AuthenticationMode { demo, azure }
|
||||
@@ -19,8 +20,11 @@ class AuthService {
|
||||
static const String demoResponsableEmail = "karim.benali@ensup.eu";
|
||||
|
||||
static String? _demoEmail;
|
||||
static AuthenticatedProfile? _currentProfile;
|
||||
static bool _azureInitialized = false;
|
||||
|
||||
static AuthenticatedProfile? get currentProfile => _currentProfile;
|
||||
|
||||
static AuthenticationMode get mode {
|
||||
return switch (_configuredMode) {
|
||||
"demo" => AuthenticationMode.demo,
|
||||
@@ -41,6 +45,13 @@ class AuthService {
|
||||
|
||||
static void clearSession() {
|
||||
_demoEmail = null;
|
||||
_currentProfile = null;
|
||||
}
|
||||
|
||||
static AuthenticatedProfile setProfileFromApi(dynamic reponse) {
|
||||
final profile = AuthenticatedProfile.fromApi(reponse);
|
||||
_currentProfile = profile;
|
||||
return profile;
|
||||
}
|
||||
|
||||
static Future<void> signInWithMicrosoft() async {
|
||||
@@ -57,6 +68,7 @@ class AuthService {
|
||||
if (mode == AuthenticationMode.azure) {
|
||||
await _initializeAzure();
|
||||
await MsalBridge.signOut();
|
||||
_currentProfile = null;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user