From f8ca77eeb949b1fe1abae20c8f0380eb8d4cdc89 Mon Sep 17 00:00:00 2001 From: Wowieee4 Date: Thu, 23 Apr 2026 11:38:29 +0700 Subject: [PATCH] updates --- .../com/walkguide/config/SecurityConfig.java | 29 +- .../controller/GuardianController.java | 43 ++ .../walkguide/controller/UserController.java | 24 + .../com/walkguide/security/JwtAuthFilter.java | 57 ++ .../java/com/walkguide/security/JwtUtil.java | 58 +- .../walkguide/service/MockDataService.java | 43 ++ .../walkguide_app/assets/images/hero.avif | Bin 0 -> 13056 bytes .../walkguide_app/assets/images/walk.jpg | Bin 0 -> 75488 bytes .../auth/presentation/login_screen.dart | 556 ++++++++++++------ .../guardian_dashboard_screen.dart | 534 ++++++++++++----- .../presentation/user_dashboard_screen.dart | 351 +++++++---- walkguide-mobile/walkguide_app/pubspec.yaml | 2 + 12 files changed, 1240 insertions(+), 457 deletions(-) create mode 100644 walkguide-backend/demo/src/main/java/com/walkguide/controller/GuardianController.java create mode 100644 walkguide-backend/demo/src/main/java/com/walkguide/controller/UserController.java create mode 100644 walkguide-backend/demo/src/main/java/com/walkguide/security/JwtAuthFilter.java create mode 100644 walkguide-backend/demo/src/main/java/com/walkguide/service/MockDataService.java create mode 100644 walkguide-mobile/walkguide_app/assets/images/hero.avif create mode 100644 walkguide-mobile/walkguide_app/assets/images/walk.jpg diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/config/SecurityConfig.java b/walkguide-backend/demo/src/main/java/com/walkguide/config/SecurityConfig.java index c081be3..1ece305 100644 --- a/walkguide-backend/demo/src/main/java/com/walkguide/config/SecurityConfig.java +++ b/walkguide-backend/demo/src/main/java/com/walkguide/config/SecurityConfig.java @@ -9,6 +9,8 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import com.walkguide.security.JwtAuthFilter; + @Configuration @EnableWebSecurity public class SecurityConfig { @@ -20,27 +22,28 @@ public class SecurityConfig { } // Aturan jalan masuk ke API kita: + // Jangan lupa inject filter-nya di atas (tambahin parameter JwtAuthFilter jwtAuthFilter) @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain filterChain(HttpSecurity http, JwtAuthFilter jwtAuthFilter) throws Exception { http .cors(cors -> cors.configurationSource(request -> { - var corsConfiguration = new org.springframework.web.cors.CorsConfiguration(); - // Izinkan semua origin localhost dan semua port (biar Chrome gak ngeblok) - corsConfiguration.setAllowedOriginPatterns(java.util.List.of( - "http://localhost:*", - "http://127.0.0.1:*" - )); - corsConfiguration.setAllowedMethods(java.util.List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); - corsConfiguration.setAllowedHeaders(java.util.List.of("*")); - corsConfiguration.setAllowCredentials(true); - return corsConfiguration; + var corsConfig = new org.springframework.web.cors.CorsConfiguration(); + corsConfig.setAllowedOriginPatterns(java.util.List.of("http://localhost:*", "http://127.0.0.1:*")); + corsConfig.setAllowedMethods(java.util.List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); + corsConfig.setAllowedHeaders(java.util.List.of("*")); + corsConfig.setAllowCredentials(true); + return corsConfig; })) .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth - .requestMatchers("/api/auth/**").permitAll() + .requestMatchers("/api/auth/**", "/swagger-ui/**", "/v3/api-docs/**").permitAll() // Login & Swagger bebas + .requestMatchers("/api/guardian/**").hasRole("GUARDIAN") // Khusus Guardian + .requestMatchers("/api/user/**").hasRole("USER") // Khusus Tunanetra .anyRequest().authenticated() - ); + ) + // TARUH SATPAM DI SINI + .addFilterBefore(jwtAuthFilter, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class); return http.build(); } diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/controller/GuardianController.java b/walkguide-backend/demo/src/main/java/com/walkguide/controller/GuardianController.java new file mode 100644 index 0000000..56bcbd5 --- /dev/null +++ b/walkguide-backend/demo/src/main/java/com/walkguide/controller/GuardianController.java @@ -0,0 +1,43 @@ +package com.walkguide.controller; + +import java.util.Map; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.walkguide.dto.ApiResponse; +import com.walkguide.service.MockDataService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/api/guardian") +@RequiredArgsConstructor +public class GuardianController { + + private final MockDataService mockDataService; + + // 4. Ambil Status User + @GetMapping("/user-status") + public ResponseEntity>> getUserStatus() { + return ResponseEntity.ok(new ApiResponse<>(true, mockDataService.getUserStatus(), "Data status user berhasil diambil")); + } + + // 5. Setting Hardware Shortcut + @PutMapping("/settings/shortcuts") + public ResponseEntity>> updateShortcuts(@RequestBody Map request) { + Map updated = mockDataService.updateShortcuts(request); + return ResponseEntity.ok(new ApiResponse<>(true, updated, "Shortcut berhasil diperbarui")); + } + + // 6. Setting Sensitivitas AI + @PutMapping("/settings/ai") + public ResponseEntity>> updateAiSettings(@RequestBody Map request) { + Map updated = mockDataService.updateAiSettings(request); + return ResponseEntity.ok(new ApiResponse<>(true, updated, "Setting AI berhasil diperbarui")); + } +} \ No newline at end of file diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/controller/UserController.java b/walkguide-backend/demo/src/main/java/com/walkguide/controller/UserController.java new file mode 100644 index 0000000..13e6653 --- /dev/null +++ b/walkguide-backend/demo/src/main/java/com/walkguide/controller/UserController.java @@ -0,0 +1,24 @@ +package com.walkguide.controller; + +import java.util.Map; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.walkguide.dto.ApiResponse; + +@RestController +@RequestMapping("/api/user") +public class UserController { + + // 7. Sinyal Darurat (Voice Command) + @PostMapping("/emergency") + public ResponseEntity> triggerEmergency(@RequestBody Map request) { + // Simulasi mengirim notif ke Guardian + String triggerType = (String) request.get("triggerType"); + return ResponseEntity.ok(new ApiResponse<>(true, "Darurat Terkirim", "Guardian telah diberi peringatan via: " + triggerType)); + } +} \ No newline at end of file diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/security/JwtAuthFilter.java b/walkguide-backend/demo/src/main/java/com/walkguide/security/JwtAuthFilter.java new file mode 100644 index 0000000..64dd69c --- /dev/null +++ b/walkguide-backend/demo/src/main/java/com/walkguide/security/JwtAuthFilter.java @@ -0,0 +1,57 @@ +package com.walkguide.security; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.Collections; + +@Component +@RequiredArgsConstructor +public class JwtAuthFilter extends OncePerRequestFilter { + + private final JwtUtil jwtUtil; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + final String authHeader = request.getHeader("Authorization"); + + // Kalau gak ada token, lewatin aja (biar dicegat sama SecurityConfig) + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + + // Potong tulisan "Bearer " + final String jwt = authHeader.substring(7); + + try { + // Ambil email & role dari token lu + String email = jwtUtil.extractUsername(jwt); + String role = jwtUtil.extractRole(jwt); // Pastiin JwtUtil lu punya fungsi extractRole! + + // Daftarin user ini ke sistem keamanan Spring + if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + email, null, Collections.singletonList(new SimpleGrantedAuthority(role)) + ); + SecurityContextHolder.getContext().setAuthentication(authToken); + } + } catch (Exception e) { + // Token kadaluarsa / rusak + System.out.println("JWT Error: " + e.getMessage()); + } + + filterChain.doFilter(request, response); + } +} \ No newline at end of file diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/security/JwtUtil.java b/walkguide-backend/demo/src/main/java/com/walkguide/security/JwtUtil.java index 6b8b81d..f7dc924 100644 --- a/walkguide-backend/demo/src/main/java/com/walkguide/security/JwtUtil.java +++ b/walkguide-backend/demo/src/main/java/com/walkguide/security/JwtUtil.java @@ -2,33 +2,67 @@ package com.walkguide.security; import java.security.Key; import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; @Component public class JwtUtil { - @Value("${jwt.secret}") - private String secret; + // Kunci rahasia buat enkripsi & dekripsi token (Minimal 256-bit) + private static final String SECRET_KEY = "404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970"; - @Value("${jwt.expiration}") - private Long expiration; - - private Key getSigningKey() { - return Keys.hmacShaKeyFor(secret.getBytes()); + // Fungsi tambahan buat ngebongkar Email (Username) + public String extractUsername(String token) { + return extractClaim(token, Claims::getSubject); } + // Fungsi tambahan buat ngebongkar Role + public String extractRole(String token) { + Claims claims = extractAllClaims(token); + return claims.get("role", String.class); + } + + public T extractClaim(String token, Function claimsResolver) { + final Claims claims = extractAllClaims(token); + return claimsResolver.apply(claims); + } + + private Claims extractAllClaims(String token) { + return Jwts.parserBuilder() + .setSigningKey(getSignInKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + private Key getSignInKey() { + byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY); + return Keys.hmacShaKeyFor(keyBytes); + } + + // Fungsi lama lu buat bikin token public String generateToken(String email, String role) { + Map claims = new HashMap<>(); + claims.put("role", role); + return createToken(claims, email); + } + + private String createToken(Map claims, String subject) { return Jwts.builder() - .setSubject(email) - .claim("role", role) + .setClaims(claims) + .setSubject(subject) .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + expiration)) - .signWith(getSigningKey()) + .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // Aktif 10 jam + .signWith(getSignInKey(), SignatureAlgorithm.HS256) .compact(); } } \ No newline at end of file diff --git a/walkguide-backend/demo/src/main/java/com/walkguide/service/MockDataService.java b/walkguide-backend/demo/src/main/java/com/walkguide/service/MockDataService.java new file mode 100644 index 0000000..81c2bc5 --- /dev/null +++ b/walkguide-backend/demo/src/main/java/com/walkguide/service/MockDataService.java @@ -0,0 +1,43 @@ +package com.walkguide.service; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.stereotype.Service; + +@Service +public class MockDataService { + + // Tambahin 'final' di sini sesuai saran VS Code + private final Map userStatus = new HashMap<>(); + private final Map hardwareShortcuts = new HashMap<>(); + private final Map aiSettings = new HashMap<>(); + + public MockDataService() { + // Data default awal saat Spring Boot nyala + userStatus.put("status", "Sedang Berjalan"); + userStatus.put("location", "Jl. Kenangan, SBY"); + userStatus.put("batteryLevel", 85); + userStatus.put("lastSeen", "2026-04-23T08:40:00Z"); + + hardwareShortcuts.put("volumeUpAction", "accept_call"); + hardwareShortcuts.put("volumeDownAction", "emergency_ping"); + + aiSettings.put("alertDistanceMeters", 2.5); + aiSettings.put("hapticFeedback", true); + } + + public Map getUserStatus() { + return userStatus; + } + + public Map updateShortcuts(Map newShortcuts) { + hardwareShortcuts.putAll(newShortcuts); + return hardwareShortcuts; + } + + public Map updateAiSettings(Map newSettings) { + aiSettings.putAll(newSettings); + return aiSettings; + } +} \ No newline at end of file diff --git a/walkguide-mobile/walkguide_app/assets/images/hero.avif b/walkguide-mobile/walkguide_app/assets/images/hero.avif new file mode 100644 index 0000000000000000000000000000000000000000..e03acbf55fb59716fd28f8a03d67ccd82f1fe9e2 GIT binary patch literal 13056 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 literal 0 HcmV?d00001 diff --git a/walkguide-mobile/walkguide_app/assets/images/walk.jpg b/walkguide-mobile/walkguide_app/assets/images/walk.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ec52a2d741343d13ec3d5fdd5ceb51877ca788f9 GIT binary patch literal 75488 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 literal 0 HcmV?d00001 diff --git a/walkguide-mobile/walkguide_app/lib/features/auth/presentation/login_screen.dart b/walkguide-mobile/walkguide_app/lib/features/auth/presentation/login_screen.dart index c622721..8dde71e 100644 --- a/walkguide-mobile/walkguide_app/lib/features/auth/presentation/login_screen.dart +++ b/walkguide-mobile/walkguide_app/lib/features/auth/presentation/login_screen.dart @@ -8,222 +8,420 @@ import '../../home/presentation/user_dashboard_screen.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); - @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { - final TextEditingController _emailController = TextEditingController(); - final TextEditingController _passwordController = TextEditingController(); - final ApiService _apiService = ApiService(); - final SecureStorage _secureStorage = SecureStorage(); - + final _emailCtrl = TextEditingController(); + final _passCtrl = TextEditingController(); + final _apiService = ApiService(); + final _secureStorage = SecureStorage(); bool _isLoading = false; - bool _isPasswordVisible = false; + bool _showPass = false; + int _selectedTab = 0; // 0 = Guardian, 1 = User + + final _hints = [ + ['guardian@walkguide.com', 'guardian123'], + ['user@walkguide.com', 'user123'], + ]; + + @override + void initState() { + super.initState(); + _emailCtrl.text = _hints[0][0]; + _passCtrl.text = _hints[0][1]; + } + + void _switchTab(int idx) { + setState(() { + _selectedTab = idx; + _emailCtrl.text = _hints[idx][0]; + _passCtrl.text = _hints[idx][1]; + }); + } Future _handleLogin() async { setState(() => _isLoading = true); - try { - final response = await _apiService.post('/auth/login', { - 'email': _emailController.text.trim(), - 'password': _passwordController.text.trim(), + final res = await _apiService.post('/auth/login', { + 'email': _emailCtrl.text.trim(), + 'password': _passCtrl.text.trim(), }); - - if (response.statusCode == 200) { - // Ekstraksi data dari ApiResponse wrapper (success, data, message) - final responseBody = response.data; - - if (responseBody['success'] == true) { - final userData = responseBody['data']; - final String token = userData['token']; - final String role = userData['role']; - - // Simpan token ke storage aman - await _secureStorage.saveToken(token); - - // Routing berdasarkan Role - if (mounted) { - if (role == 'ROLE_GUARDIAN') { - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (_) => const GuardianDashboardScreen()), - ); - } else { - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (_) => const UserDashboardScreen()), - ); - } - } - } else { - throw Exception(responseBody['message'] ?? 'Login gagal'); - } + if (res.statusCode == 200 && res.data['success'] == true) { + final data = res.data['data']; + await _secureStorage.saveToken(data['token']); + if (!mounted) return; + Navigator.pushReplacement(context, MaterialPageRoute( + builder: (_) => data['role'] == 'ROLE_GUARDIAN' + ? const GuardianDashboardScreen() + : const UserDashboardScreen(), + )); } } on DioException catch (e) { - if (mounted) { - // Ambil pesan error dari GlobalExceptionHandler di Spring Boot - String errorMsg = 'Gagal Terhubung ke Server'; - if (e.response?.data != null && e.response?.data is Map) { - errorMsg = e.response?.data['message'] ?? errorMsg; - } - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(errorMsg), - backgroundColor: Colors.redAccent, - behavior: SnackBarBehavior.floating, - ), - ); - } - } catch (e) { - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(e.toString())), - ); - } + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(e.response?.data['message'] ?? 'Gagal terhubung'), + backgroundColor: Colors.redAccent, + behavior: SnackBarBehavior.floating, + )); } finally { - setState(() => _isLoading = false); + if (mounted) setState(() => _isLoading = false); } } @override Widget build(BuildContext context) { + final isWide = MediaQuery.of(context).size.width > 700; return Scaffold( - backgroundColor: const Color(0xFFF8FAFC), + backgroundColor: const Color(0xFFF1F5F9), body: Center( - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 32.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Logo/Icon Header - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: const Color(0xFF2563EB).withValues(alpha: 0.1), - shape: BoxShape.circle, - ), - child: const Icon( - Icons.navigation_rounded, - size: 48, - color: Color(0xFF2563EB) - ), - ), - const SizedBox(height: 24), - Text( - 'Walk Guide', - style: GoogleFonts.outfit( - fontSize: 32, - fontWeight: FontWeight.bold, - color: const Color(0xFF0F172A) - ), - ), - const SizedBox(height: 8), - Text( - 'Masuk untuk mulai navigasi', - style: GoogleFonts.inter( - fontSize: 15, - color: const Color(0xFF64748B) - ), - ), - const SizedBox(height: 40), - - // Input Fields - _buildTextField( - _emailController, - Icons.alternate_email, - 'Email', - false - ), - const SizedBox(height: 20), - _buildTextField( - _passwordController, - Icons.lock_outline, - 'Password', - true - ), - - const SizedBox(height: 48), - - // Button Masuk - SizedBox( - width: double.infinity, - height: 56, - child: ElevatedButton( - onPressed: _isLoading ? null : _handleLogin, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2563EB), - foregroundColor: Colors.white, - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16) - ), - ), - child: _isLoading - ? const SizedBox( - height: 24, - width: 24, - child: CircularProgressIndicator( - color: Colors.white, - strokeWidth: 2 - ), - ) - : Text( - 'Masuk', - style: GoogleFonts.inter( - fontSize: 16, - fontWeight: FontWeight.w600 - ) - ), - ), - ), + child: Container( + constraints: const BoxConstraints(maxWidth: 900, maxHeight: 600), + margin: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.08), + blurRadius: 40, + offset: const Offset(0, 16), + ) ], ), + child: ClipRRect( + borderRadius: BorderRadius.circular(20), + child: Row( + children: [ + if (isWide) Expanded(child: _buildHeroPanel()), + _buildFormPanel(), + ], + ), + ), ), ), ); } - Widget _buildTextField( - TextEditingController controller, - IconData icon, - String hint, - bool isPassword - ) { + Widget _buildHeroPanel() { + return Stack( + fit: StackFit.expand, + children: [ + Image.asset( + 'assets/images/walk.jpg', + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => + Container(color: const Color(0xFF0F1923)), + ), + Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Colors.transparent, Color(0xCC0A1428)], + stops: [0.4, 1.0], + ), + ), + ), + Positioned( + top: 24, + left: 24, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 7), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.12), // ✅ Fixed + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.white.withValues(alpha: 0.15), // ✅ Fixed + ), + ), + child: Row( + children: [ + Container( + width: 6, + height: 6, + decoration: const BoxDecoration( + color: Color(0xFF60EFAB), + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 6), + Text( + 'AI Navigation Active', + style: GoogleFonts.inter( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ), + Positioned( + bottom: 36, + left: 32, + right: 32, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '"WalkGuide memberi saya\nkebebasan yang luar biasa."', + style: GoogleFonts.outfit( + color: Colors.white, + fontSize: 22, + fontWeight: FontWeight.w600, + height: 1.35, + ), + ), + const SizedBox(height: 14), + Text( + 'Andi Pratama', + style: GoogleFonts.inter( + color: Colors.white, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + Text( + 'Pengguna — Surabaya, Jawa Timur', + style: GoogleFonts.inter( + color: Colors.white60, + fontSize: 12, + ), + ), + const SizedBox(height: 18), + Row( + children: [ + Container( + width: 28, + height: 3, + decoration: BoxDecoration( + color: const Color(0xFF60EFAB), + borderRadius: BorderRadius.circular(2), + ), + ), + const SizedBox(width: 6), + Container( + width: 20, + height: 3, + decoration: BoxDecoration( + color: Colors.white24, + borderRadius: BorderRadius.circular(2), + ), + ), + const SizedBox(width: 6), + Container( + width: 20, + height: 3, + decoration: BoxDecoration( + color: Colors.white24, + borderRadius: BorderRadius.circular(2), + ), + ), + ], + ), + ], + ), + ), + ], + ); + } + + Widget _buildFormPanel() { return Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.02), - blurRadius: 10, - offset: const Offset(0, 4) - ) + width: 320, + color: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 36, vertical: 40), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: const Color(0xFF1A56DB), + borderRadius: BorderRadius.circular(8), + ), + child: const Icon(Icons.navigation_rounded, + color: Colors.white, size: 16), + ), + const SizedBox(width: 8), + Text( + 'WalkGuide', + style: GoogleFonts.outfit( + fontSize: 15, + fontWeight: FontWeight.w600, + color: const Color(0xFF0F172A), + ), + ), + ], + ), + const SizedBox(height: 32), + Text( + "Let's sign in", + style: GoogleFonts.outfit( + fontSize: 24, + fontWeight: FontWeight.w600, + color: const Color(0xFF0F172A), + ), + ), + const SizedBox(height: 4), + Text( + 'Continue your journey with WalkGuide.', + style: GoogleFonts.inter( + fontSize: 13, + color: const Color(0xFF64748B), + ), + ), + const SizedBox(height: 24), + // Tab switcher + Container( + padding: const EdgeInsets.all(3), + decoration: BoxDecoration( + color: const Color(0xFFF1F5F9), + borderRadius: BorderRadius.circular(10), + ), + child: Row( + children: [ + _buildTab(0, 'Guardian'), + _buildTab(1, 'User'), + ], + ), + ), + const SizedBox(height: 22), + _buildLabel('Email address'), + const SizedBox(height: 5), + _buildInput(_emailCtrl, false), + const SizedBox(height: 14), + _buildLabel('Password'), + const SizedBox(height: 5), + _buildInput(_passCtrl, true), + const SizedBox(height: 8), + Align( + alignment: Alignment.centerRight, + child: Text( + 'Forgot password?', + style: GoogleFonts.inter( + fontSize: 12, + color: const Color(0xFF1A56DB), + ), + ), + ), + const SizedBox(height: 22), + SizedBox( + width: double.infinity, + height: 42, + child: ElevatedButton( + onPressed: _isLoading ? null : _handleLogin, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF1A56DB), + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: _isLoading + ? const SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 2, + ), + ) + : Text( + 'Continue', + style: GoogleFonts.inter( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ), + const SizedBox(height: 20), + Center( + child: Text( + 'Need help? Contact support', + style: GoogleFonts.inter( + fontSize: 12, + color: const Color(0xFF94A3B8), + ), + ), + ), ], ), + ); + } + + Widget _buildTab(int idx, String label) { + final active = _selectedTab == idx; + return Expanded( + child: GestureDetector( + onTap: () => _switchTab(idx), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + color: active ? Colors.white : Colors.transparent, + borderRadius: BorderRadius.circular(7), + ), + child: Text( + label, + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 13, + fontWeight: active ? FontWeight.w600 : FontWeight.w400, + color: active + ? const Color(0xFF0F172A) + : const Color(0xFF64748B), + ), + ), + ), + ), + ); + } + + Widget _buildLabel(String text) => Text( + text, + style: GoogleFonts.inter( + fontSize: 12, + color: const Color(0xFF64748B), + ), + ); + + Widget _buildInput(TextEditingController ctrl, bool isPass) { + return Container( + height: 40, + decoration: BoxDecoration( + border: Border.all(color: const Color(0xFFE2E8F0)), + borderRadius: BorderRadius.circular(8), + ), child: TextField( - controller: controller, - obscureText: isPassword && !_isPasswordVisible, - style: GoogleFonts.inter(fontSize: 15), + controller: ctrl, + obscureText: isPass && !_showPass, + style: GoogleFonts.inter( + fontSize: 13, + color: const Color(0xFF0F172A), + ), decoration: InputDecoration( - hintText: hint, - hintStyle: const TextStyle(color: Color(0xFF94A3B8)), - prefixIcon: Icon(icon, color: const Color(0xFF94A3B8)), - suffixIcon: isPassword + border: InputBorder.none, + contentPadding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + suffixIcon: isPass ? IconButton( icon: Icon( - _isPasswordVisible ? Icons.visibility_off : Icons.visibility, - color: const Color(0xFF94A3B8) + _showPass ? Icons.visibility_off : Icons.visibility, + size: 16, + color: const Color(0xFF94A3B8), ), - onPressed: () => setState(() => _isPasswordVisible = !_isPasswordVisible), - ) + onPressed: () => setState(() => _showPass = !_showPass), + ) : null, - border: InputBorder.none, - contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 18), ), ), ); diff --git a/walkguide-mobile/walkguide_app/lib/features/home/presentation/guardian_dashboard_screen.dart b/walkguide-mobile/walkguide_app/lib/features/home/presentation/guardian_dashboard_screen.dart index cde23f7..07d0551 100644 --- a/walkguide-mobile/walkguide_app/lib/features/home/presentation/guardian_dashboard_screen.dart +++ b/walkguide-mobile/walkguide_app/lib/features/home/presentation/guardian_dashboard_screen.dart @@ -1,19 +1,18 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:animate_do/animate_do.dart'; import '../../../core/secure_storage.dart'; import '../../auth/presentation/login_screen.dart'; class GuardianDashboardScreen extends StatelessWidget { const GuardianDashboardScreen({super.key}); - Future _handleLogout(BuildContext context) async { + Future _logout(BuildContext ctx) async { await SecureStorage().deleteToken(); - if (context.mounted) { + if (ctx.mounted) { Navigator.pushAndRemoveUntil( - context, + ctx, MaterialPageRoute(builder: (_) => const LoginScreen()), - (route) => false, // Bersihin tumpukan screen + (_) => false, ); } } @@ -22,156 +21,403 @@ class GuardianDashboardScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF8FAFC), - appBar: AppBar( - backgroundColor: Colors.transparent, - elevation: 0, - title: FadeInDown( - child: Text('Guardian Command', style: GoogleFonts.outfit(color: const Color(0xFF0F172A), fontWeight: FontWeight.bold, fontSize: 24)), - ), - actions: [ - FadeInDown( - child: IconButton( - icon: const Icon(Icons.logout_rounded, color: Color(0xFFDC2626)), - tooltip: 'Logout', - onPressed: () => _handleLogout(context), - ), + body: Column(children: [ + _buildTopBar(context), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.all(20), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + _buildPageHeader(), + const SizedBox(height: 18), + _buildKpiRow(), + const SizedBox(height: 18), + _buildMainGrid(context), + ]), ), - ], - ), - body: SingleChildScrollView( - padding: const EdgeInsets.all(24.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 1. KARTU STATUS USER - FadeInUp( - duration: const Duration(milliseconds: 600), - child: Container( - padding: const EdgeInsets.all(24), - decoration: BoxDecoration( - gradient: const LinearGradient(colors: [Color(0xFF2563EB), Color(0xFF1D4ED8)]), - borderRadius: BorderRadius.circular(24), - boxShadow: [ - BoxShadow(color: const Color(0xFF2563EB).withValues(alpha: 0.3), blurRadius: 20, offset: const Offset(0, 10)) - ], - ), - child: Row( - children: [ - Container( - padding: const EdgeInsets.all(4), - decoration: const BoxDecoration(color: Colors.white24, shape: BoxShape.circle), - child: const CircleAvatar( - radius: 30, - backgroundColor: Colors.white, - child: Icon(Icons.person, size: 36, color: Color(0xFF2563EB)), - ), - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('User Pantauan', style: GoogleFonts.inter(color: Colors.blue[100], fontSize: 14)), - Text('Sistem Aktif', style: GoogleFonts.outfit(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold)), - const SizedBox(height: 8), - Row( - children: [ - const Icon(Icons.location_on, color: Colors.white70, size: 16), - const SizedBox(width: 4), - Text('Melacak lokasi...', style: GoogleFonts.inter(color: Colors.white70, fontSize: 12)), - ], - ), - ], - ), - ), - ], - ), - ), - ), - const SizedBox(height: 32), - - // 2. QUICK ACTIONS - FadeInUp( - delay: const Duration(milliseconds: 200), - child: Text('Aksi Cepat', style: GoogleFonts.outfit(fontSize: 18, fontWeight: FontWeight.bold, color: const Color(0xFF0F172A))), - ), - const SizedBox(height: 16), - FadeInUp( - delay: const Duration(milliseconds: 300), - child: Row( - children: [ - _buildQuickAction(Icons.videocam_outlined, 'Live View', const Color(0xFF10B981)), - const SizedBox(width: 16), - _buildQuickAction(Icons.phone_in_talk, 'Hubungi', const Color(0xFFF59E0B)), - const SizedBox(width: 16), - _buildQuickAction(Icons.settings_voice, 'Voice Conf', const Color(0xFF8B5CF6)), - ], - ), - ), - const SizedBox(height: 32), - - // 3. SETTINGAN DEVICE USER - FadeInUp( - delay: const Duration(milliseconds: 400), - child: Text('Konfigurasi Perangkat', style: GoogleFonts.outfit(fontSize: 18, fontWeight: FontWeight.bold, color: const Color(0xFF0F172A))), - ), - const SizedBox(height: 16), - FadeInUp( - delay: const Duration(milliseconds: 500), - child: _buildSettingTile(Icons.gamepad_outlined, 'Hardware Shortcuts', 'Atur fungsi tombol volume hp user'), - ), - FadeInUp( - delay: const Duration(milliseconds: 600), - child: _buildSettingTile(Icons.spatial_audio_outlined, 'Sensitivitas AI', 'Atur jarak deteksi rintangan'), - ), - ], ), - ), + ]), ); } - Widget _buildQuickAction(IconData icon, String label, Color color) { - return Expanded( - child: Container( - padding: const EdgeInsets.symmetric(vertical: 20), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.grey.withValues(alpha: 0.1)), - boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.02), blurRadius: 10, offset: const Offset(0, 4))], - ), - child: Column( - children: [ - Icon(icon, size: 32, color: color), - const SizedBox(height: 8), - Text(label, style: GoogleFonts.inter(fontSize: 13, fontWeight: FontWeight.w600, color: const Color(0xFF475569))), - ], - ), - ), - ); - } - - Widget _buildSettingTile(IconData icon, String title, String subtitle) { + Widget _buildTopBar(BuildContext ctx) { + return Container( + height: 52, + decoration: const BoxDecoration( + color: Colors.white, + border: Border(bottom: BorderSide(color: Color(0xFFE2E8F0), width: 0.5)), + ), + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row(children: [ + Container( + width: 26, + height: 26, + decoration: BoxDecoration( + color: const Color(0xFF1A56DB), + borderRadius: BorderRadius.circular(7), + ), + child: const Icon(Icons.navigation_rounded, color: Colors.white, size: 14), + ), + const SizedBox(width: 8), + Text('WalkGuide', style: GoogleFonts.outfit(fontSize: 14, fontWeight: FontWeight.w600)), + const SizedBox(width: 20), + _navItem('Overview', true), + _navItem('Live Track', false), + _navItem('Settings', false), + _navItem('Alerts', false), + const Spacer(), + TextButton.icon( + onPressed: () => _logout(ctx), + icon: const Icon(Icons.logout, size: 14, color: Color(0xFF64748B)), + label: Text('Sign out', style: GoogleFonts.inter(fontSize: 12, color: const Color(0xFF64748B))), + ), + const SizedBox(width: 10), + Row(children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Guardian', style: GoogleFonts.inter(fontSize: 12, fontWeight: FontWeight.w600)), + Text('guardian@walkguide.com', + style: GoogleFonts.inter(fontSize: 10, color: const Color(0xFF94A3B8))), + ], + ), + const SizedBox(width: 8), + CircleAvatar( + radius: 14, + backgroundColor: const Color(0xFF1A56DB), + child: Text('GD', + style: GoogleFonts.inter(fontSize: 10, color: Colors.white, fontWeight: FontWeight.w600)), + ), + ]), + ]), + ); + } + + Widget _navItem(String label, bool active) { + return Container( + margin: const EdgeInsets.only(right: 2), + child: TextButton( + onPressed: () {}, + style: TextButton.styleFrom( + backgroundColor: active ? const Color(0xFFF1F5F9) : Colors.transparent, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)), + ), + child: Text( + label, + style: GoogleFonts.inter( + fontSize: 13, + fontWeight: active ? FontWeight.w600 : FontWeight.w400, + color: active ? const Color(0xFF0F172A) : const Color(0xFF64748B), + ), + ), + ), + ); + } + + Widget _buildPageHeader() { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Guardian Command', + style: GoogleFonts.outfit(fontSize: 20, fontWeight: FontWeight.w600, color: const Color(0xFF0F172A))), + Text('Thursday, April 23, 2026 — Real-time monitoring active', + style: GoogleFonts.inter(fontSize: 12, color: const Color(0xFF64748B))), + ]); + } + + Widget _buildKpiRow() { + final kpis = [ + {'label': 'User Status', 'val': '● Active', 'sub': 'Walking — Jl. Kenangan SBY', 'isGreen': true}, + {'label': 'Battery Level', 'val': '85%', 'sub': 'Good — Est. 6h left', 'isGreen': false}, + {'label': 'AI Alerts Today', 'val': '12', 'sub': '3 obstacles detected', 'isGreen': false}, + {'label': 'Alert Distance', 'val': '2.5m', 'sub': 'Haptic feedback on', 'isGreen': false}, + ]; + return Row( + children: kpis.map((k) { + final isLast = kpis.indexOf(k) == kpis.length - 1; + return Expanded( + child: Container( + margin: EdgeInsets.only(right: isLast ? 0 : 10), + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFFE2E8F0), width: 0.5), + ), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(k['label'] as String, + style: GoogleFonts.inter(fontSize: 11, color: const Color(0xFF94A3B8), letterSpacing: 0.04)), + const SizedBox(height: 6), + Text( + k['val'] as String, + style: GoogleFonts.outfit( + fontSize: 20, + fontWeight: FontWeight.w600, + color: (k['isGreen'] as bool) ? const Color(0xFF16A34A) : const Color(0xFF0F172A), + ), + ), + const SizedBox(height: 3), + Text(k['sub'] as String, + style: GoogleFonts.inter(fontSize: 11, color: const Color(0xFF1A56DB))), + ]), + ), + ); + }).toList(), + ); + } + + Widget _buildMainGrid(BuildContext ctx) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column(children: [ + _buildMapCard(), + const SizedBox(height: 14), + _buildActivityCard(), + ]), + ), + const SizedBox(width: 14), + SizedBox( + width: 240, + child: Column(children: [ + _buildUserCard(), + const SizedBox(height: 12), + _buildActionsCard(), + ]), + ), + ], + ); + } + + Widget _buildMapCard() { return Container( - margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: Colors.white, - borderRadius: BorderRadius.circular(16), - border: Border.all(color: Colors.grey.withValues(alpha: 0.1)), - boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.02), blurRadius: 8, offset: const Offset(0, 2))], + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFFE2E8F0), width: 0.5), ), - child: ListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), - leading: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration(color: const Color(0xFFF1F5F9), borderRadius: BorderRadius.circular(12)), - child: Icon(icon, color: const Color(0xFF0F172A)), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Padding( + padding: const EdgeInsets.fromLTRB(16, 14, 16, 12), + child: Row(children: [ + Text('Live location', style: GoogleFonts.inter(fontSize: 13, fontWeight: FontWeight.w500)), + const Spacer(), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: const Color(0x1A16A34A), + borderRadius: BorderRadius.circular(20), + ), + child: Row(children: [ + Container( + width: 5, + height: 5, + decoration: const BoxDecoration(color: Color(0xFF16A34A), shape: BoxShape.circle), + ), + const SizedBox(width: 4), + Text('Live', + style: GoogleFonts.inter(fontSize: 10, color: const Color(0xFF16A34A), fontWeight: FontWeight.w600)), + ]), + ), + ]), ), - title: Text(title, style: GoogleFonts.inter(fontWeight: FontWeight.w600, color: const Color(0xFF0F172A))), - subtitle: Text(subtitle, style: GoogleFonts.inter(fontSize: 13, color: const Color(0xFF64748B))), - trailing: const Icon(Icons.arrow_forward_ios, size: 16, color: Color(0xFFCBD5E1)), - onTap: () {}, + ClipRRect( + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(12), + bottomRight: Radius.circular(12), + ), + child: Container( + height: 180, + decoration: const BoxDecoration(color: Color(0xFFF1F5F9)), + child: Stack(children: [ + Center( + child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + Container( + width: 36, + height: 36, + decoration: const BoxDecoration(color: Color(0xFF1A56DB), shape: BoxShape.circle), + child: const Icon(Icons.location_on, color: Colors.white, size: 20), + ), + const SizedBox(height: 8), + Text('Map placeholder', + style: GoogleFonts.inter(fontSize: 12, color: const Color(0xFF94A3B8))), + ]), + ), + Positioned( + bottom: 10, + left: 10, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: const Color(0xFFE2E8F0), width: 0.5), + ), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Jl. Kenangan No. 14', + style: GoogleFonts.inter(fontSize: 12, fontWeight: FontWeight.w600)), + Text('Surabaya, East Java', + style: GoogleFonts.inter(fontSize: 11, color: const Color(0xFF64748B))), + ]), + ), + ), + ]), + ), + ), + ]), + ); + } + + Widget _buildActivityCard() { + final items = [ + {'icon': Icons.play_circle_outline, 'title': 'Navigation started', 'time': '08:32 — User began walking route', 'color': const Color(0xFF16A34A)}, + {'icon': Icons.warning_amber_outlined, 'title': 'Obstacle detected', 'time': '08:41 — AI alert at 1.8m distance', 'color': const Color(0xFFD97706)}, + {'icon': Icons.location_on_outlined, 'title': 'Location checkpoint', 'time': '09:05 — Arrived at Jl. Kenangan', 'color': const Color(0xFF1A56DB)}, + ]; + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFFE2E8F0), width: 0.5), ), + child: Column(children: [ + Padding( + padding: const EdgeInsets.fromLTRB(16, 14, 16, 12), + child: Row(children: [ + Text('Recent activity', style: GoogleFonts.inter(fontSize: 13, fontWeight: FontWeight.w500)), + const Spacer(), + Text('Today', style: GoogleFonts.inter(fontSize: 12, color: const Color(0xFF94A3B8))), + ]), + ), + ...items.map((item) => Column(children: [ + const Divider(height: 0.5, thickness: 0.5, color: Color(0xFFE2E8F0)), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row(children: [ + Container( + width: 30, + height: 30, + decoration: BoxDecoration( + color: (item['color'] as Color).withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Icon(item['icon'] as IconData, size: 16, color: item['color'] as Color), + ), + const SizedBox(width: 12), + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(item['title'] as String, + style: GoogleFonts.inter(fontSize: 13, fontWeight: FontWeight.w500)), + Text(item['time'] as String, + style: GoogleFonts.inter(fontSize: 12, color: const Color(0xFF64748B))), + ]), + ]), + ), + ])), + ]), + ); + } + + Widget _buildUserCard() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFFE2E8F0), width: 0.5), + ), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Row(children: [ + Container( + width: 36, height: 36, + decoration: BoxDecoration( + color: const Color(0xFF1A56DB).withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(18), + ), + child: const Icon(Icons.person, color: Color(0xFF1A56DB), size: 20), + ), + const SizedBox(width: 10), + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('User (Tunanetra)', style: GoogleFonts.inter(fontSize: 13, fontWeight: FontWeight.w600)), + Text('● Online now', style: GoogleFonts.inter(fontSize: 11, color: const Color(0xFF16A34A))), + ]), + ]), + const SizedBox(height: 14), + // Ganti GridView → Row+Column biasa, lebih predictable + Row(children: [ + Expanded(child: _statCell('Battery', '85%')), + const SizedBox(width: 8), + Expanded(child: _statCell('Speed', '3.2 km/h')), + ]), + const SizedBox(height: 8), + Row(children: [ + Expanded(child: _statCell('Distance', '1.4 km')), + const SizedBox(width: 8), + Expanded(child: _statCell('Alerts', '12')), + ]), + ]), + ); + } + + Widget _statCell(String label, String val) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + color: const Color(0xFFF8FAFC), + borderRadius: BorderRadius.circular(8), + ), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(label, style: GoogleFonts.inter(fontSize: 10, color: const Color(0xFF94A3B8))), + const SizedBox(height: 2), + Text(val, style: GoogleFonts.outfit(fontSize: 14, fontWeight: FontWeight.w600)), + ]), + ); + } + + Widget _buildActionsCard() { + final actions = [ + {'icon': Icons.videocam_outlined, 'label': 'Live view', 'sub': 'Camera feed', 'color': const Color(0xFF1A56DB)}, + {'icon': Icons.phone_outlined, 'label': 'Call user', 'sub': 'Voice call', 'color': const Color(0xFF16A34A)}, + {'icon': Icons.tune, 'label': 'AI sensitivity', 'sub': 'Obstacle distance', 'color': const Color(0xFF7C3AED)}, + {'icon': Icons.warning_rounded, 'label': 'Emergency ping', 'sub': 'Alert user now', 'color': const Color(0xFFDC2626)}, + ]; + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFFE2E8F0), width: 0.5), + ), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('QUICK ACTIONS', + style: GoogleFonts.inter( + fontSize: 11, fontWeight: FontWeight.w600, color: const Color(0xFF94A3B8), letterSpacing: 0.06)), + const SizedBox(height: 8), + ...actions.map((a) => InkWell( + borderRadius: BorderRadius.circular(8), + onTap: () {}, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 6), + child: Row(children: [ + Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: (a['color'] as Color).withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(7), + ), + child: Icon(a['icon'] as IconData, size: 15, color: a['color'] as Color), + ), + const SizedBox(width: 10), + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(a['label'] as String, + style: GoogleFonts.inter(fontSize: 13, fontWeight: FontWeight.w500)), + Text(a['sub'] as String, + style: GoogleFonts.inter(fontSize: 11, color: const Color(0xFF94A3B8))), + ]), + ]), + ), + )), + ]), ); } } \ No newline at end of file diff --git a/walkguide-mobile/walkguide_app/lib/features/home/presentation/user_dashboard_screen.dart b/walkguide-mobile/walkguide_app/lib/features/home/presentation/user_dashboard_screen.dart index f53d692..61000d0 100644 --- a/walkguide-mobile/walkguide_app/lib/features/home/presentation/user_dashboard_screen.dart +++ b/walkguide-mobile/walkguide_app/lib/features/home/presentation/user_dashboard_screen.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:camera/camera.dart'; -import 'package:animate_do/animate_do.dart'; import '../../../core/secure_storage.dart'; import '../../auth/presentation/login_screen.dart'; -import '../../../../main.dart'; // import global cameras +import '../../../../main.dart'; class UserDashboardScreen extends StatefulWidget { const UserDashboardScreen({super.key}); @@ -13,159 +12,293 @@ class UserDashboardScreen extends StatefulWidget { State createState() => _UserDashboardScreenState(); } -class _UserDashboardScreenState extends State with SingleTickerProviderStateMixin { - CameraController? _cameraController; - late AnimationController _pulseController; +class _UserDashboardScreenState extends State with TickerProviderStateMixin { + CameraController? _camCtrl; + late AnimationController _radarCtrl; + late Animation _radarAnim; @override void initState() { super.initState(); - _initCamera(); - - // Setup animasi radar berdenyut - _pulseController = AnimationController( + _radarCtrl = AnimationController( vsync: this, duration: const Duration(seconds: 2), )..repeat(reverse: true); + _radarAnim = Tween(begin: 0.9, end: 1.08).animate( + CurvedAnimation(parent: _radarCtrl, curve: Curves.easeInOut), + ); + _initCamera(); } Future _initCamera() async { if (cameras.isEmpty) return; - // Pakai kamera belakang (index 0) - _cameraController = CameraController(cameras[0], ResolutionPreset.high, enableAudio: false); - await _cameraController!.initialize(); + _camCtrl = CameraController(cameras[0], ResolutionPreset.high, enableAudio: false); + await _camCtrl!.initialize(); if (mounted) setState(() {}); } - Future _handleLogout() async { + Future _logout() async { await SecureStorage().deleteToken(); if (mounted) { Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (_) => const LoginScreen()), - (route) => false, // Hapus history back + (_) => false, ); } } @override void dispose() { - _cameraController?.dispose(); - _pulseController.dispose(); + _camCtrl?.dispose(); + _radarCtrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { + final size = MediaQuery.of(context).size; + final topPad = MediaQuery.of(context).padding.top; + final botPad = MediaQuery.of(context).padding.bottom; + return Scaffold( backgroundColor: Colors.black, - body: Stack( - children: [ - // 1. LIVE CAMERA FEED - SizedBox.expand( - child: _cameraController != null && _cameraController!.value.isInitialized - ? CameraPreview(_cameraController!) - : const Center(child: CircularProgressIndicator(color: Colors.white)), - ), + body: Stack(children: [ + // 1. Camera feed + SizedBox.expand( + child: (_camCtrl != null && _camCtrl!.value.isInitialized) + ? CameraPreview(_camCtrl!) + : Container( + decoration: const BoxDecoration(color: Color(0xFF0A1520)), + ), + ), - // 2. EFEK RADAR SCANNING (Animasi Pulse) - Positioned.fill( - child: AnimatedBuilder( - animation: _pulseController, - builder: (context, child) { - return Container( - decoration: BoxDecoration( - gradient: RadialGradient( - colors: [ - Colors.transparent, - const Color(0xFF10B981).withValues(alpha: 0.1 + (_pulseController.value * 0.15)), - ], - stops: const [0.5, 1.0], - radius: 1.5, - ), - ), - ); - }, + // 2. Grid overlay + SizedBox.expand(child: CustomPaint(painter: _GridPainter())), + + // 3. Green scan gradient + AnimatedBuilder( + animation: _radarCtrl, + builder: (_, __) => SizedBox.expand( + child: DecoratedBox( + decoration: BoxDecoration( + gradient: RadialGradient( + colors: [ + Colors.transparent, + const Color(0xFF10B981).withValues(alpha: 0.05 + _radarCtrl.value * 0.08), + ], + stops: const [0.5, 1.0], + radius: 1.4, + ), + ), ), ), + ), - // 3. OVERLAY STATUS & LOGOUT (Atas) - Positioned( - top: 60, - left: 20, - right: 20, - child: FadeInDown( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ + // 4. Scan line + AnimatedBuilder( + animation: _radarCtrl, + builder: (_, __) => Positioned( + top: size.height * (0.3 + _radarCtrl.value * 0.35), + left: 0, + right: 0, + child: Container( + height: 1.5, + decoration: BoxDecoration( + color: const Color(0xFF60EFAB).withValues(alpha: 0.25), + ), + ), + ), + ), + + // 5. Top status bar + Positioned( + top: 0, + left: 0, + right: 0, + child: Container( + padding: EdgeInsets.fromLTRB(16, topPad + 8, 16, 12), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Colors.black87, Colors.transparent], + ), + ), + child: Row(children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.54), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: const Color(0xFF60EFAB).withValues(alpha: 0.3), + ), + ), + child: Row(children: [ Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.6), - borderRadius: BorderRadius.circular(30), - border: Border.all(color: const Color(0xFF10B981).withValues(alpha: 0.3)), - ), - child: Row( - children: [ - const Icon(Icons.spatial_audio_off, color: Color(0xFF10B981), size: 20), - const SizedBox(width: 8), - Text('Memindai Area...', style: GoogleFonts.inter(color: Colors.white, fontWeight: FontWeight.bold)), - ], + width: 6, + height: 6, + decoration: const BoxDecoration( + color: Color(0xFF60EFAB), + shape: BoxShape.circle, ), ), - - // Tombol Logout - IconButton( - onPressed: _handleLogout, - icon: const Icon(Icons.power_settings_new, color: Colors.white, size: 28), - tooltip: 'Keluar', - style: IconButton.styleFrom(backgroundColor: Colors.redAccent.withValues(alpha: 0.8)), + const SizedBox(width: 7), + Text( + 'Memindai area...', + style: GoogleFonts.inter( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.w500, + ), ), - ], + ]), ), - ), + const Spacer(), + IconButton( + onPressed: _logout, + icon: const Icon(Icons.power_settings_new, color: Colors.white, size: 26), + style: IconButton.styleFrom( + backgroundColor: Colors.redAccent.withValues(alpha: 0.8), + ), + ), + ]), ), + ), - // 4. OVERLAY TOMBOL BESAR (Bawah) - Positioned( - bottom: 40, - left: 20, - right: 20, - child: FadeInUp( - delay: const Duration(milliseconds: 300), - child: Row( - children: [ - Expanded( - child: ElevatedButton( - onPressed: () {}, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2563EB).withValues(alpha: 0.9), - padding: const EdgeInsets.symmetric(vertical: 24), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), - elevation: 10, - ), - child: const Icon(Icons.mic, size: 48, color: Colors.white), - ), - ), - const SizedBox(width: 16), - Expanded( - child: ElevatedButton( - onPressed: () {}, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFDC2626).withValues(alpha: 0.9), - padding: const EdgeInsets.symmetric(vertical: 24), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), - elevation: 10, - ), - child: const Icon(Icons.phone_in_talk, size: 48, color: Colors.white), - ), - ), - ], + // 6. Center radar + Center( + child: AnimatedBuilder( + animation: _radarAnim, + builder: (_, __) => Transform.scale( + scale: _radarAnim.value, + child: SizedBox( + width: 100, + height: 100, + child: CustomPaint(painter: _RadarPainter()), ), ), ), - ], - ), + ), + + // 7. Obstacle alert card + Positioned( + bottom: 160, + left: 16, + right: 16, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.6), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: const Color(0xFFF59E0B).withValues(alpha: 0.3), + ), + ), + child: Row(children: [ + Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: const Color(0x33F59E0B), + borderRadius: BorderRadius.circular(7), + ), + child: const Icon(Icons.warning_amber_rounded, color: Color(0xFFF59E0B), size: 16), + ), + const SizedBox(width: 10), + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text('Obstacle ahead', + style: GoogleFonts.inter( + color: Colors.white, fontSize: 13, fontWeight: FontWeight.w500)), + Text('2.1m — Haptic alert sent', + style: GoogleFonts.inter(color: Colors.white60, fontSize: 11)), + ]), + ]), + ), + ), + + // 8. Bottom action buttons + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + padding: EdgeInsets.fromLTRB(16, 16, 16, botPad + 20), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [Colors.black87, Colors.transparent], + ), + ), + child: Column(children: [ + Row(children: [ + Expanded(child: _bigBtn(const Color(0xCC1A56DB), Icons.mic, () {})), + const SizedBox(width: 12), + Expanded(child: _bigBtn(const Color(0xCCDC2626), Icons.phone_in_talk, () {})), + ]), + const SizedBox(height: 8), + Text( + 'Vol+ terima panggilan • Vol- ping darurat', + style: GoogleFonts.inter(fontSize: 10, color: Colors.white30), + ), + ]), + ), + ), + ]), ); } + + Widget _bigBtn(Color color, IconData icon, VoidCallback onTap) { + return ElevatedButton( + onPressed: onTap, + style: ElevatedButton.styleFrom( + backgroundColor: color, + elevation: 0, + padding: const EdgeInsets.symmetric(vertical: 22), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + ), + child: Icon(icon, size: 40, color: Colors.white), + ); + } +} + +class _GridPainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.white.withValues(alpha: 0.04) + ..strokeWidth = 0.5; + for (double x = 0; x < size.width; x += 25) { + canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint); + } + for (double y = 0; y < size.height; y += 25) { + canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} + +class _RadarPainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final center = Offset(size.width / 2, size.height / 2); + final paint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1.2; + for (final r in [48.0, 34.0, 20.0]) { + paint.color = const Color(0xFF60EFAB).withValues(alpha: 0.15 + (50 - r) / 100); + canvas.drawCircle(center, r, paint); + } + paint + ..style = PaintingStyle.fill + ..color = const Color(0xFF60EFAB); + canvas.drawCircle(center, 5, paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } \ No newline at end of file diff --git a/walkguide-mobile/walkguide_app/pubspec.yaml b/walkguide-mobile/walkguide_app/pubspec.yaml index db834be..67bf0cc 100644 --- a/walkguide-mobile/walkguide_app/pubspec.yaml +++ b/walkguide-mobile/walkguide_app/pubspec.yaml @@ -62,6 +62,8 @@ flutter: # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true + assets: + - assets/images/ # To add assets to your application, add an assets section, like this: # assets: