- Replace dart:js with package:js in health_check_service_web.dart\n- Implement showHealthCheckNotification in web/index.html\n- Pin js dependency to ^0.6.7 for flutter_secure_storage_web compatibility auth: harden AuthInterceptor + tests - Allow overrideAuthRepository injection for testing\n- Normalize imports to package: paths\n- Add unit test covering token attach, 401→refresh→retry, and failure path\n- Add integration test skeleton gated by env vars ui/data: map User.companyName to list column - Add companyName to domain User\n- Map UserDto.company?.name\n- Render companyName in user_list cleanup: remove legacy equipment table + unused code; minor warnings - Remove _buildFlexibleTable and unused helpers\n- Remove unused zipcode details and cache retry constant\n- Fix null-aware and non-null assertions\n- Address child-last warnings in administrator dialog docs: update AGENTS.md session context
1393 lines
49 KiB
Dart
1393 lines
49 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get_it/get_it.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:superport/screens/common/theme_shadcn.dart';
|
|
import 'package:superport/screens/common/components/shadcn_components.dart';
|
|
import 'package:superport/screens/vendor/vendor_list_screen.dart';
|
|
import 'package:superport/screens/vendor/controllers/vendor_controller.dart';
|
|
import 'package:superport/screens/model/model_list_screen.dart';
|
|
import 'package:superport/screens/zipcode/zipcode_search_screen.dart';
|
|
import 'package:superport/screens/zipcode/controllers/zipcode_controller.dart';
|
|
import 'package:superport/screens/equipment/equipment_list.dart';
|
|
import 'package:superport/screens/company/company_list.dart';
|
|
import 'package:superport/screens/user/user_list.dart';
|
|
import 'package:superport/screens/warehouse_location/warehouse_location_list.dart';
|
|
import 'package:superport/screens/inventory/inventory_history_screen.dart';
|
|
import 'package:superport/screens/maintenance/maintenance_schedule_screen.dart';
|
|
import 'package:superport/screens/maintenance/maintenance_alert_dashboard.dart';
|
|
import 'package:superport/screens/maintenance/maintenance_history_screen.dart' as maint;
|
|
import 'package:superport/screens/maintenance/controllers/maintenance_controller.dart';
|
|
import 'package:superport/screens/rent/rent_list_screen.dart';
|
|
import 'package:superport/screens/rent/controllers/rent_controller.dart';
|
|
import 'package:superport/services/auth_service.dart';
|
|
import 'package:superport/core/services/lookups_service.dart';
|
|
import 'package:superport/injection_container.dart' as di;
|
|
import 'package:superport/utils/constants.dart';
|
|
import 'package:superport/data/models/auth/auth_user.dart';
|
|
|
|
/// ERP 시스템 최적화 메인 레이아웃
|
|
/// F-Pattern 레이아웃 적용 (1920x1080 최적화)
|
|
/// 상단 헤더 + 좌측 사이드바 + 메인 콘텐츠 구조
|
|
class AppLayout extends StatefulWidget {
|
|
final String initialRoute;
|
|
|
|
const AppLayout({super.key, this.initialRoute = Routes.home});
|
|
|
|
@override
|
|
State<AppLayout> createState() => _AppLayoutState();
|
|
}
|
|
|
|
class _AppLayoutState extends State<AppLayout>
|
|
with TickerProviderStateMixin {
|
|
late String _currentRoute;
|
|
bool _sidebarCollapsed = false;
|
|
late AnimationController _sidebarAnimationController;
|
|
AuthUser? _currentUser;
|
|
late final AuthService _authService;
|
|
late final LookupsService _lookupsService;
|
|
late Animation<double> _sidebarAnimation;
|
|
int _expiringMaintenanceCount = 0; // 30일 내 만료 예정 유지보수 수
|
|
|
|
// 레이아웃 상수 (1920x1080 최적화)
|
|
static const double _sidebarExpandedWidth = 260.0;
|
|
static const double _sidebarCollapsedWidth = 72.0;
|
|
static const double _headerHeight = 64.0;
|
|
static const double _maxContentWidth = 1440.0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_currentRoute = widget.initialRoute;
|
|
_setupAnimations();
|
|
_authService = GetIt.instance<AuthService>();
|
|
_lookupsService = GetIt.instance<LookupsService>();
|
|
_loadCurrentUser();
|
|
_loadMaintenanceAlerts();
|
|
_initializeLookupData(); // Lookup 데이터 초기화
|
|
}
|
|
|
|
Future<void> _loadCurrentUser() async {
|
|
try {
|
|
// 서버에서 최신 관리자 정보 가져오기
|
|
final result = await _authService.getCurrentAdminFromServer();
|
|
result.fold(
|
|
(failure) {
|
|
print('[AppLayout] 서버에서 관리자 정보 로드 실패: ${failure.message}');
|
|
// 실패 시 로컬 스토리지에서 캐시된 정보 사용
|
|
_loadCurrentUserFromLocal();
|
|
},
|
|
(user) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_currentUser = user;
|
|
});
|
|
print('[AppLayout] 서버에서 관리자 정보 로드 성공: ${user.name} (${user.email})');
|
|
}
|
|
},
|
|
);
|
|
} catch (e) {
|
|
print('[AppLayout] 관리자 정보 로드 중 예외 발생: $e');
|
|
// 예외 발생 시 로컬 스토리지에서 캐시된 정보 사용
|
|
_loadCurrentUserFromLocal();
|
|
}
|
|
}
|
|
|
|
/// 로컬 스토리지에서 캐시된 사용자 정보 로드 (fallback)
|
|
Future<void> _loadCurrentUserFromLocal() async {
|
|
final user = await _authService.getCurrentUser();
|
|
if (mounted) {
|
|
setState(() {
|
|
_currentUser = user;
|
|
});
|
|
print('[AppLayout] 로컬에서 관리자 정보 로드: ${user?.name ?? 'Unknown'}');
|
|
}
|
|
}
|
|
|
|
Future<void> _loadMaintenanceAlerts() async {
|
|
try {
|
|
print('[DEBUG] 유지보수 알림 정보 로드 시작...');
|
|
// TODO: MaintenanceController를 통해 알림 정보 로드
|
|
// 현재는 임시로 0으로 설정
|
|
if (mounted) {
|
|
setState(() {
|
|
_expiringMaintenanceCount = 0;
|
|
print('[DEBUG] 유지보수 알림 상태 업데이트 완료: $_expiringMaintenanceCount');
|
|
});
|
|
}
|
|
} catch (e) {
|
|
print('[ERROR] 유지보수 알림 정보 로드 중 예외 발생: $e');
|
|
print('[ERROR] 스택 트레이스: ${StackTrace.current}');
|
|
}
|
|
}
|
|
|
|
/// Lookup 데이터 초기화 (앱 시작 시 한 번만 호출)
|
|
Future<void> _initializeLookupData() async {
|
|
try {
|
|
print('[DEBUG] Lookups 서비스 초기화 시작...');
|
|
|
|
if (!_lookupsService.isInitialized) {
|
|
final result = await _lookupsService.initialize();
|
|
result.fold(
|
|
(failure) {
|
|
print('[ERROR] Lookups 초기화 실패: ${failure.message}');
|
|
},
|
|
(success) {
|
|
print('[DEBUG] Lookups 서비스 초기화 성공!');
|
|
final stats = _lookupsService.getCacheStats();
|
|
print('[DEBUG] - 제조사: ${stats['manufacturers_count']}개');
|
|
print('[DEBUG] - 장비명: ${stats['equipment_names_count']}개');
|
|
print('[DEBUG] - 장비 카테고리: ${stats['equipment_categories_count']}개');
|
|
print('[DEBUG] - 장비 상태: ${stats['equipment_statuses_count']}개');
|
|
},
|
|
);
|
|
} else {
|
|
print('[DEBUG] Lookups 서비스 이미 초기화됨 (캐시 사용)');
|
|
}
|
|
} catch (e) {
|
|
print('[ERROR] Lookups 초기화 중 예외 발생: $e');
|
|
}
|
|
}
|
|
|
|
void _setupAnimations() {
|
|
_sidebarAnimationController = AnimationController(
|
|
duration: const Duration(milliseconds: 250),
|
|
vsync: this,
|
|
);
|
|
_sidebarAnimation = Tween<double>(
|
|
begin: _sidebarExpandedWidth,
|
|
end: _sidebarCollapsedWidth
|
|
).animate(
|
|
CurvedAnimation(
|
|
parent: _sidebarAnimationController,
|
|
curve: Curves.easeInOutCubic,
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_sidebarAnimationController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
/// 현재 경로에 따라 적절한 컨텐츠 섹션을 반환
|
|
Widget _getContentForRoute(String route) {
|
|
switch (route) {
|
|
case Routes.home:
|
|
return ChangeNotifierProvider(
|
|
create: (context) => di.sl<VendorController>(),
|
|
child: const VendorListScreen(),
|
|
);
|
|
case Routes.vendor:
|
|
return ChangeNotifierProvider(
|
|
create: (context) => di.sl<VendorController>(),
|
|
child: const VendorListScreen(),
|
|
);
|
|
case Routes.model:
|
|
return const ModelListScreen();
|
|
case Routes.equipment:
|
|
case Routes.equipmentInList:
|
|
case Routes.equipmentOutList:
|
|
case Routes.equipmentRentList:
|
|
return EquipmentList(currentRoute: route);
|
|
case Routes.company:
|
|
return const CompanyList();
|
|
case Routes.user:
|
|
return const UserList();
|
|
// License 시스템이 Maintenance로 대체됨
|
|
case Routes.maintenance: // 메인 진입점을 알림 대시보드로 변경
|
|
return ChangeNotifierProvider(
|
|
create: (_) => GetIt.instance<MaintenanceController>(),
|
|
child: const MaintenanceAlertDashboard(),
|
|
);
|
|
case Routes.maintenanceSchedule: // 일정관리는 별도 라우트 유지
|
|
return ChangeNotifierProvider(
|
|
create: (_) => GetIt.instance<MaintenanceController>(),
|
|
child: const MaintenanceScheduleScreen(),
|
|
);
|
|
case Routes.maintenanceAlert:
|
|
return ChangeNotifierProvider(
|
|
create: (_) => GetIt.instance<MaintenanceController>(),
|
|
child: const MaintenanceAlertDashboard(),
|
|
);
|
|
case Routes.maintenanceHistory:
|
|
return ChangeNotifierProvider(
|
|
create: (_) => GetIt.instance<MaintenanceController>(),
|
|
child: const maint.MaintenanceHistoryScreen(),
|
|
);
|
|
case Routes.warehouseLocation:
|
|
return const WarehouseLocationList();
|
|
case Routes.zipcode:
|
|
return ChangeNotifierProvider(
|
|
create: (context) => di.sl<ZipcodeController>(),
|
|
child: const ZipcodeSearchScreen(),
|
|
);
|
|
case Routes.inventory:
|
|
case Routes.inventoryHistory:
|
|
return const InventoryHistoryScreen();
|
|
case Routes.rent:
|
|
return ChangeNotifierProvider(
|
|
create: (_) => GetIt.instance<RentController>(),
|
|
child: const RentListScreen(),
|
|
);
|
|
case '/test/api':
|
|
// Navigator를 사용하여 별도 화면으로 이동
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
Navigator.pushNamed(context, '/test/api');
|
|
});
|
|
return const Center(child: CircularProgressIndicator());
|
|
default:
|
|
return ChangeNotifierProvider(
|
|
create: (context) => di.sl<VendorController>(),
|
|
child: const VendorListScreen(),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 경로 변경 메서드
|
|
void _navigateTo(String route) {
|
|
setState(() {
|
|
_currentRoute = route;
|
|
});
|
|
// 유지보수 화면으로 이동할 때 알림 정보 새로고침
|
|
if (route == Routes.maintenance || route == Routes.maintenanceAlert) {
|
|
_loadMaintenanceAlerts();
|
|
}
|
|
}
|
|
|
|
/// 사이드바 토글
|
|
void _toggleSidebar() {
|
|
setState(() {
|
|
_sidebarCollapsed = !_sidebarCollapsed;
|
|
});
|
|
|
|
if (_sidebarCollapsed) {
|
|
_sidebarAnimationController.forward();
|
|
} else {
|
|
_sidebarAnimationController.reverse();
|
|
}
|
|
}
|
|
|
|
/// 현재 페이지 제목 가져오기
|
|
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final screenWidth = MediaQuery.of(context).size.width;
|
|
final isWideScreen = screenWidth >= 1920;
|
|
|
|
return Provider<AuthService>.value(
|
|
value: _authService,
|
|
child: Scaffold(
|
|
backgroundColor: ShadcnTheme.backgroundSecondary,
|
|
body: Column(
|
|
children: [
|
|
// F-Pattern: 1차 시선 - 상단 헤더
|
|
_buildTopHeader(),
|
|
|
|
// 메인 콘텐츠 영역
|
|
Expanded(
|
|
child: Row(
|
|
children: [
|
|
// 좌측 사이드바
|
|
AnimatedBuilder(
|
|
animation: _sidebarAnimation,
|
|
builder: (context, child) {
|
|
return Container(
|
|
width: _sidebarAnimation.value,
|
|
decoration: BoxDecoration(
|
|
color: ShadcnTheme.background,
|
|
border: Border(
|
|
right: BorderSide(
|
|
color: ShadcnTheme.border,
|
|
width: 1,
|
|
),
|
|
),
|
|
),
|
|
child: _buildSidebar(),
|
|
);
|
|
},
|
|
),
|
|
|
|
// 메인 콘텐츠 (최대 너비 제한)
|
|
Expanded(
|
|
child: Center(
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: isWideScreen ? _maxContentWidth : double.infinity,
|
|
),
|
|
padding: EdgeInsets.all(
|
|
isWideScreen ? ShadcnTheme.spacing6 : ShadcnTheme.spacing4
|
|
),
|
|
child: Column(
|
|
children: [
|
|
// F-Pattern: 2차 시선 - 페이지 헤더 + 액션 (주석처리)
|
|
// _buildPageHeader(),
|
|
//
|
|
// const SizedBox(height: ShadcnTheme.spacing4),
|
|
|
|
// F-Pattern: 주요 작업 영역
|
|
Expanded(
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: ShadcnTheme.background,
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusLg),
|
|
border: Border.all(
|
|
color: ShadcnTheme.border,
|
|
width: 1,
|
|
),
|
|
boxShadow: ShadcnTheme.shadowSm,
|
|
),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusLg - 1),
|
|
child: _getContentForRoute(_currentRoute),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// F-Pattern 1차 시선: 상단 헤더 (로고, 주요 메뉴, 알림, 프로필)
|
|
Widget _buildTopHeader() {
|
|
return Container(
|
|
height: _headerHeight,
|
|
decoration: BoxDecoration(
|
|
color: ShadcnTheme.background,
|
|
border: Border(
|
|
bottom: BorderSide(
|
|
color: ShadcnTheme.border,
|
|
width: 1,
|
|
),
|
|
),
|
|
boxShadow: ShadcnTheme.shadowXs,
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: ShadcnTheme.spacing4),
|
|
child: Row(
|
|
children: [
|
|
// 왼쪽: 로고 + 사이드바 토글
|
|
Row(
|
|
children: [
|
|
// 사이드바 토글 버튼
|
|
SizedBox(
|
|
width: 40,
|
|
height: 40,
|
|
child: IconButton(
|
|
onPressed: _toggleSidebar,
|
|
icon: AnimatedRotation(
|
|
turns: _sidebarCollapsed ? 0 : 0.5,
|
|
duration: const Duration(milliseconds: 250),
|
|
child: Icon(
|
|
Icons.menu,
|
|
color: ShadcnTheme.foregroundSecondary,
|
|
size: 20,
|
|
),
|
|
),
|
|
tooltip: _sidebarCollapsed ? '사이드바 펼치기' : '사이드바 접기',
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: ShadcnTheme.spacing3),
|
|
|
|
// 앱 로고 및 제목
|
|
Row(
|
|
children: [
|
|
Container(
|
|
width: 36,
|
|
height: 36,
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
ShadcnTheme.primary,
|
|
ShadcnTheme.primaryDark,
|
|
],
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
),
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
|
),
|
|
child: Icon(
|
|
Icons.directions_boat,
|
|
size: 20,
|
|
color: ShadcnTheme.primaryForeground,
|
|
),
|
|
),
|
|
const SizedBox(width: ShadcnTheme.spacing3),
|
|
Text(
|
|
'supERPort',
|
|
style: ShadcnTheme.headingH5.copyWith(
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
|
|
const Spacer(),
|
|
|
|
// 오른쪽: 알림 + 프로필
|
|
Row(
|
|
children: [
|
|
// 검색
|
|
SizedBox(
|
|
width: 40,
|
|
height: 40,
|
|
child: IconButton(
|
|
onPressed: () {
|
|
// 전역 검색 기능
|
|
},
|
|
icon: Icon(
|
|
Icons.search,
|
|
color: ShadcnTheme.foregroundSecondary,
|
|
size: 20,
|
|
),
|
|
tooltip: '검색',
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: ShadcnTheme.spacing2),
|
|
|
|
// 알림
|
|
SizedBox(
|
|
width: 40,
|
|
height: 40,
|
|
child: Stack(
|
|
children: [
|
|
IconButton(
|
|
onPressed: () {
|
|
// 알림 기능
|
|
},
|
|
icon: Icon(
|
|
Icons.notifications_outlined,
|
|
color: ShadcnTheme.foregroundSecondary,
|
|
size: 20,
|
|
),
|
|
tooltip: '알림',
|
|
),
|
|
Positioned(
|
|
right: 10,
|
|
top: 10,
|
|
child: Container(
|
|
width: 8,
|
|
height: 8,
|
|
decoration: BoxDecoration(
|
|
color: ShadcnTheme.error,
|
|
shape: BoxShape.circle,
|
|
border: Border.all(
|
|
color: ShadcnTheme.background,
|
|
width: 1.5,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: ShadcnTheme.spacing3),
|
|
|
|
const ShadcnSeparator(
|
|
direction: Axis.vertical,
|
|
thickness: 1,
|
|
),
|
|
|
|
const SizedBox(width: ShadcnTheme.spacing3),
|
|
|
|
// 프로필
|
|
InkWell(
|
|
onTap: () => _showProfileMenu(context),
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusFull),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(ShadcnTheme.spacing1),
|
|
child: Row(
|
|
children: [
|
|
ShadcnAvatar(
|
|
initials: _currentUser?.name.substring(0, 1).toUpperCase() ?? 'U',
|
|
size: 32,
|
|
backgroundColor: ShadcnTheme.primaryLight,
|
|
textColor: ShadcnTheme.primary,
|
|
showBorder: false,
|
|
),
|
|
const SizedBox(width: ShadcnTheme.spacing2),
|
|
Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
_currentUser?.name ?? '사용자',
|
|
style: ShadcnTheme.labelMedium,
|
|
),
|
|
Text(
|
|
_getUserRoleText(_currentUser?.role),
|
|
style: ShadcnTheme.caption.copyWith(
|
|
color: ShadcnTheme.foregroundMuted,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(width: ShadcnTheme.spacing2),
|
|
Icon(
|
|
Icons.expand_more,
|
|
size: 16,
|
|
color: ShadcnTheme.foregroundMuted,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 사용자 역할 텍스트 변환
|
|
String _getUserRoleText(String? role) {
|
|
switch (role) {
|
|
case 'admin':
|
|
return '관리자';
|
|
case 'manager':
|
|
return '매니저';
|
|
case 'member':
|
|
return '일반 사용자';
|
|
default:
|
|
return '사용자';
|
|
}
|
|
}
|
|
|
|
/// 사이드바 빌드
|
|
Widget _buildSidebar() {
|
|
return SidebarMenu(
|
|
currentRoute: _currentRoute,
|
|
onRouteChanged: _navigateTo,
|
|
collapsed: _sidebarCollapsed,
|
|
expiringMaintenanceCount: _expiringMaintenanceCount,
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 프로필 메뉴 표시
|
|
void _showProfileMenu(BuildContext context) {
|
|
showModalBottomSheet(
|
|
context: context,
|
|
backgroundColor: ShadcnTheme.background,
|
|
shape: const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.vertical(
|
|
top: Radius.circular(ShadcnTheme.radiusXl),
|
|
),
|
|
),
|
|
builder: (context) => Container(
|
|
padding: const EdgeInsets.all(ShadcnTheme.spacing6),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
// 핸들바
|
|
Container(
|
|
width: 40,
|
|
height: 4,
|
|
decoration: BoxDecoration(
|
|
color: ShadcnTheme.border,
|
|
borderRadius: BorderRadius.circular(2),
|
|
),
|
|
),
|
|
const SizedBox(height: ShadcnTheme.spacing6),
|
|
|
|
// 프로필 정보
|
|
Row(
|
|
children: [
|
|
ShadcnAvatar(
|
|
initials: _currentUser?.name.substring(0, 1).toUpperCase() ?? 'U',
|
|
size: 56,
|
|
backgroundColor: ShadcnTheme.primaryLight,
|
|
textColor: ShadcnTheme.primary,
|
|
),
|
|
const SizedBox(width: ShadcnTheme.spacing4),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
_currentUser?.name ?? '사용자',
|
|
style: ShadcnTheme.headingH5,
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
_currentUser?.email ?? '',
|
|
style: ShadcnTheme.bodySmall.copyWith(
|
|
color: ShadcnTheme.foregroundMuted,
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
ShadcnBadge(
|
|
text: _getUserRoleText(_currentUser?.role),
|
|
variant: ShadcnBadgeVariant.primary,
|
|
size: ShadcnBadgeSize.small,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: ShadcnTheme.spacing6),
|
|
const ShadcnSeparator(),
|
|
const SizedBox(height: ShadcnTheme.spacing4),
|
|
|
|
// 메뉴 항목들
|
|
_buildProfileMenuItem(
|
|
icon: Icons.person_outline,
|
|
title: '프로필 설정',
|
|
onTap: () {
|
|
Navigator.pop(context);
|
|
// 프로필 설정 화면으로 이동
|
|
},
|
|
),
|
|
_buildProfileMenuItem(
|
|
icon: Icons.lock_outline,
|
|
title: '비밀번호 변경',
|
|
onTap: () {
|
|
Navigator.pop(context);
|
|
_showChangePasswordDialog(context);
|
|
},
|
|
),
|
|
_buildProfileMenuItem(
|
|
icon: Icons.settings_outlined,
|
|
title: '환경 설정',
|
|
onTap: () {
|
|
Navigator.pop(context);
|
|
// 환경 설정 화면으로 이동
|
|
},
|
|
),
|
|
|
|
const SizedBox(height: ShadcnTheme.spacing4),
|
|
const ShadcnSeparator(),
|
|
const SizedBox(height: ShadcnTheme.spacing4),
|
|
|
|
// 로그아웃 버튼
|
|
ShadcnButton(
|
|
text: '로그아웃',
|
|
onPressed: () async {
|
|
// 로딩 다이얼로그 표시
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => Center(
|
|
child: Container(
|
|
padding: const EdgeInsets.all(ShadcnTheme.spacing6),
|
|
decoration: BoxDecoration(
|
|
color: ShadcnTheme.background,
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusLg),
|
|
),
|
|
child: const CircularProgressIndicator(),
|
|
),
|
|
),
|
|
);
|
|
|
|
try {
|
|
// AuthService를 사용하여 로그아웃
|
|
final authService = GetIt.instance<AuthService>();
|
|
await authService.logout();
|
|
|
|
// 로딩 다이얼로그와 현재 모달 닫기
|
|
if (context.mounted) {
|
|
Navigator.of(context).pop(); // 로딩 다이얼로그
|
|
Navigator.of(context).pop(); // 프로필 메뉴
|
|
// 로그인 화면으로 이동
|
|
Navigator.of(context).pushNamedAndRemoveUntil(
|
|
'/login',
|
|
(route) => false,
|
|
);
|
|
}
|
|
} catch (e) {
|
|
// 에러 처리
|
|
if (context.mounted) {
|
|
Navigator.of(context).pop(); // 로딩 다이얼로그 닫기
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('로그아웃 중 오류가 발생했습니다.'),
|
|
backgroundColor: ShadcnTheme.error,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
},
|
|
variant: ShadcnButtonVariant.destructive,
|
|
fullWidth: true,
|
|
icon: Icon(Icons.logout, size: 18),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 프로필 메뉴 아이템
|
|
Widget _buildProfileMenuItem({
|
|
required IconData icon,
|
|
required String title,
|
|
required VoidCallback onTap,
|
|
}) {
|
|
return InkWell(
|
|
onTap: onTap,
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: ShadcnTheme.spacing3,
|
|
vertical: ShadcnTheme.spacing3,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
icon,
|
|
size: 20,
|
|
color: ShadcnTheme.foregroundSecondary,
|
|
),
|
|
const SizedBox(width: ShadcnTheme.spacing3),
|
|
Text(
|
|
title,
|
|
style: ShadcnTheme.bodyMedium,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 비밀번호 변경 다이얼로그
|
|
void _showChangePasswordDialog(BuildContext context) {
|
|
final oldPasswordController = TextEditingController();
|
|
final newPasswordController = TextEditingController();
|
|
final confirmPasswordController = TextEditingController();
|
|
bool isLoading = false;
|
|
bool obscureOldPassword = true;
|
|
bool obscureNewPassword = true;
|
|
bool obscureConfirmPassword = true;
|
|
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => StatefulBuilder(
|
|
builder: (context, setState) => AlertDialog(
|
|
backgroundColor: ShadcnTheme.background,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusLg),
|
|
),
|
|
title: Row(
|
|
children: [
|
|
Icon(
|
|
Icons.lock_outline,
|
|
color: ShadcnTheme.primary,
|
|
size: 24,
|
|
),
|
|
const SizedBox(width: ShadcnTheme.spacing3),
|
|
Text(
|
|
'비밀번호 변경',
|
|
style: ShadcnTheme.headingH5,
|
|
),
|
|
],
|
|
),
|
|
content: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Text(
|
|
'보안을 위해 기존 비밀번호를 입력하고 새 비밀번호를 설정해주세요.',
|
|
style: ShadcnTheme.bodySmall.copyWith(
|
|
color: ShadcnTheme.foregroundMuted,
|
|
),
|
|
),
|
|
const SizedBox(height: ShadcnTheme.spacing6),
|
|
|
|
// 기존 비밀번호
|
|
Text(
|
|
'기존 비밀번호',
|
|
style: ShadcnTheme.labelMedium,
|
|
),
|
|
const SizedBox(height: ShadcnTheme.spacing2),
|
|
TextFormField(
|
|
controller: oldPasswordController,
|
|
obscureText: obscureOldPassword,
|
|
decoration: InputDecoration(
|
|
hintText: '현재 사용중인 비밀번호를 입력하세요',
|
|
suffixIcon: IconButton(
|
|
onPressed: () {
|
|
setState(() {
|
|
obscureOldPassword = !obscureOldPassword;
|
|
});
|
|
},
|
|
icon: Icon(
|
|
obscureOldPassword ? Icons.visibility_off : Icons.visibility,
|
|
color: ShadcnTheme.foregroundMuted,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: ShadcnTheme.spacing4),
|
|
|
|
// 새 비밀번호
|
|
Text(
|
|
'새 비밀번호',
|
|
style: ShadcnTheme.labelMedium,
|
|
),
|
|
const SizedBox(height: ShadcnTheme.spacing2),
|
|
TextFormField(
|
|
controller: newPasswordController,
|
|
obscureText: obscureNewPassword,
|
|
decoration: InputDecoration(
|
|
hintText: '8자 이상의 새 비밀번호를 입력하세요',
|
|
suffixIcon: IconButton(
|
|
onPressed: () {
|
|
setState(() {
|
|
obscureNewPassword = !obscureNewPassword;
|
|
});
|
|
},
|
|
icon: Icon(
|
|
obscureNewPassword ? Icons.visibility_off : Icons.visibility,
|
|
color: ShadcnTheme.foregroundMuted,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: ShadcnTheme.spacing4),
|
|
|
|
// 새 비밀번호 확인
|
|
Text(
|
|
'새 비밀번호 확인',
|
|
style: ShadcnTheme.labelMedium,
|
|
),
|
|
const SizedBox(height: ShadcnTheme.spacing2),
|
|
TextFormField(
|
|
controller: confirmPasswordController,
|
|
obscureText: obscureConfirmPassword,
|
|
decoration: InputDecoration(
|
|
hintText: '새 비밀번호를 다시 입력하세요',
|
|
suffixIcon: IconButton(
|
|
onPressed: () {
|
|
setState(() {
|
|
obscureConfirmPassword = !obscureConfirmPassword;
|
|
});
|
|
},
|
|
icon: Icon(
|
|
obscureConfirmPassword ? Icons.visibility_off : Icons.visibility,
|
|
color: ShadcnTheme.foregroundMuted,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
actions: [
|
|
ShadcnButton(
|
|
text: '취소',
|
|
onPressed: isLoading ? null : () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
variant: ShadcnButtonVariant.secondary,
|
|
),
|
|
ShadcnButton(
|
|
text: isLoading ? '변경 중...' : '변경하기',
|
|
onPressed: isLoading ? null : () async {
|
|
// 유효성 검사
|
|
if (oldPasswordController.text.isEmpty) {
|
|
_showSnackBar(context, '기존 비밀번호를 입력해주세요.', isError: true);
|
|
return;
|
|
}
|
|
|
|
if (newPasswordController.text.length < 8) {
|
|
_showSnackBar(context, '새 비밀번호는 8자 이상이어야 합니다.', isError: true);
|
|
return;
|
|
}
|
|
|
|
if (newPasswordController.text != confirmPasswordController.text) {
|
|
_showSnackBar(context, '새 비밀번호가 일치하지 않습니다.', isError: true);
|
|
return;
|
|
}
|
|
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
// AuthService.changePassword API 호출
|
|
final result = await _authService.changePassword(
|
|
oldPassword: oldPasswordController.text,
|
|
newPassword: newPasswordController.text,
|
|
);
|
|
|
|
result.fold(
|
|
(failure) {
|
|
if (context.mounted) {
|
|
_showSnackBar(context, failure.message, isError: true);
|
|
}
|
|
},
|
|
(messageResponse) {
|
|
if (context.mounted) {
|
|
Navigator.of(context).pop();
|
|
_showSnackBar(context, messageResponse.message);
|
|
}
|
|
},
|
|
);
|
|
} catch (e) {
|
|
if (context.mounted) {
|
|
_showSnackBar(context, '비밀번호 변경 중 오류가 발생했습니다.', isError: true);
|
|
}
|
|
} finally {
|
|
if (mounted) {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
},
|
|
variant: ShadcnButtonVariant.primary,
|
|
icon: isLoading ? SizedBox(
|
|
width: 16,
|
|
height: 16,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 2,
|
|
valueColor: AlwaysStoppedAnimation<Color>(
|
|
ShadcnTheme.primaryForeground,
|
|
),
|
|
),
|
|
) : Icon(Icons.check, size: 18),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 스낵바 표시 헬퍼 메서드
|
|
void _showSnackBar(BuildContext context, String message, {bool isError = false}) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(message),
|
|
backgroundColor: isError ? ShadcnTheme.error : ShadcnTheme.success,
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 재설계된 사이드바 메뉴 (접기/펼치기 지원)
|
|
class SidebarMenu extends StatelessWidget {
|
|
final String currentRoute;
|
|
final Function(String) onRouteChanged;
|
|
final bool collapsed;
|
|
final int expiringMaintenanceCount;
|
|
|
|
const SidebarMenu({
|
|
super.key,
|
|
required this.currentRoute,
|
|
required this.onRouteChanged,
|
|
required this.collapsed,
|
|
required this.expiringMaintenanceCount,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
children: [
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
padding: EdgeInsets.all(
|
|
collapsed ? ShadcnTheme.spacing2 : ShadcnTheme.spacing3
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (!collapsed) ...[
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: ShadcnTheme.spacing3,
|
|
vertical: ShadcnTheme.spacing2,
|
|
),
|
|
child: Text(
|
|
'메인 메뉴',
|
|
style: ShadcnTheme.caption.copyWith(
|
|
color: ShadcnTheme.foregroundMuted,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: 0.5,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: ShadcnTheme.spacing1),
|
|
],
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.factory_outlined,
|
|
title: '벤더 관리',
|
|
route: Routes.vendor,
|
|
isActive: currentRoute == Routes.vendor,
|
|
badge: null,
|
|
),
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.category_outlined,
|
|
title: '모델 관리',
|
|
route: Routes.model,
|
|
isActive: currentRoute == Routes.model,
|
|
badge: null,
|
|
),
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.inventory_2_outlined,
|
|
title: '장비 관리',
|
|
route: Routes.equipment,
|
|
isActive: [
|
|
Routes.equipment,
|
|
Routes.equipmentInList,
|
|
Routes.equipmentOutList,
|
|
Routes.equipmentRentList,
|
|
].contains(currentRoute),
|
|
badge: null,
|
|
),
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.history,
|
|
title: '재고 이력',
|
|
route: Routes.inventoryHistory,
|
|
isActive: [
|
|
Routes.inventory,
|
|
Routes.inventoryHistory,
|
|
].contains(currentRoute),
|
|
badge: null,
|
|
),
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.warehouse_outlined,
|
|
title: '입고지 관리',
|
|
route: Routes.warehouseLocation,
|
|
isActive: currentRoute == Routes.warehouseLocation,
|
|
badge: null,
|
|
),
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.location_on_outlined,
|
|
title: '우편번호 검색',
|
|
route: Routes.zipcode,
|
|
isActive: currentRoute == Routes.zipcode,
|
|
badge: null,
|
|
),
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.business_outlined,
|
|
title: '회사 관리',
|
|
route: Routes.company,
|
|
isActive: currentRoute == Routes.company,
|
|
badge: null,
|
|
),
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.people_outlined,
|
|
title: '사용자 관리',
|
|
route: Routes.user,
|
|
isActive: currentRoute == Routes.user,
|
|
badge: null,
|
|
),
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.build_circle_outlined,
|
|
title: '유지보수 관리',
|
|
route: Routes.maintenance,
|
|
isActive: currentRoute == Routes.maintenance ||
|
|
currentRoute == Routes.maintenanceSchedule ||
|
|
currentRoute == Routes.maintenanceAlert ||
|
|
currentRoute == Routes.maintenanceHistory,
|
|
badge: null,
|
|
hasSubMenu: true,
|
|
subMenuItems: collapsed ? [] : [
|
|
_buildSubMenuItem(
|
|
title: '알림 대시보드',
|
|
route: Routes.maintenanceAlert,
|
|
isActive: currentRoute == Routes.maintenanceAlert,
|
|
),
|
|
_buildSubMenuItem(
|
|
title: '일정 관리',
|
|
route: Routes.maintenanceSchedule,
|
|
isActive: currentRoute == Routes.maintenanceSchedule,
|
|
),
|
|
_buildSubMenuItem(
|
|
title: '이력 조회',
|
|
route: Routes.maintenanceHistory,
|
|
isActive: currentRoute == Routes.maintenanceHistory,
|
|
),
|
|
],
|
|
),
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.calendar_month_outlined,
|
|
title: '임대 관리',
|
|
route: Routes.rent,
|
|
isActive: currentRoute == Routes.rent,
|
|
badge: null,
|
|
),
|
|
|
|
|
|
if (!collapsed) ...[
|
|
const SizedBox(height: ShadcnTheme.spacing4),
|
|
const ShadcnSeparator(),
|
|
const SizedBox(height: ShadcnTheme.spacing4),
|
|
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: ShadcnTheme.spacing3,
|
|
vertical: ShadcnTheme.spacing2,
|
|
),
|
|
child: Text(
|
|
'개발자 도구',
|
|
style: ShadcnTheme.caption.copyWith(
|
|
color: ShadcnTheme.foregroundMuted,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: 0.5,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: ShadcnTheme.spacing1),
|
|
],
|
|
|
|
_buildMenuItem(
|
|
icon: Icons.bug_report_outlined,
|
|
title: 'API 테스트',
|
|
route: '/test/api',
|
|
isActive: currentRoute == '/test/api',
|
|
badge: null,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
|
|
// 하단 버전 정보
|
|
if (!collapsed) ...[
|
|
const ShadcnSeparator(),
|
|
Container(
|
|
padding: const EdgeInsets.all(ShadcnTheme.spacing3),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'supERPort v1.0.0',
|
|
style: ShadcnTheme.caption.copyWith(
|
|
color: ShadcnTheme.foregroundMuted,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
'© 2025 Superport',
|
|
style: ShadcnTheme.caption.copyWith(
|
|
color: ShadcnTheme.foregroundSubtle,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildMenuItem({
|
|
required IconData icon,
|
|
required String title,
|
|
required String route,
|
|
required bool isActive,
|
|
String? badge,
|
|
bool hasSubMenu = false,
|
|
List<Widget> subMenuItems = const [],
|
|
}) {
|
|
return Column(
|
|
children: [
|
|
AnimatedContainer(
|
|
duration: const Duration(milliseconds: 200),
|
|
margin: const EdgeInsets.only(bottom: ShadcnTheme.spacing1),
|
|
child: InkWell(
|
|
onTap: () => onRouteChanged(route),
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: collapsed ? ShadcnTheme.spacing3 : ShadcnTheme.spacing3,
|
|
vertical: ShadcnTheme.spacing2 + 2,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: isActive
|
|
? ShadcnTheme.primaryLight
|
|
: Colors.transparent,
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
isActive ? _getFilledIcon(icon) : icon,
|
|
size: 20,
|
|
color: isActive
|
|
? ShadcnTheme.primary
|
|
: ShadcnTheme.foregroundSecondary,
|
|
),
|
|
if (!collapsed) ...[
|
|
const SizedBox(width: ShadcnTheme.spacing3),
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: ShadcnTheme.bodyMedium.copyWith(
|
|
color: isActive
|
|
? ShadcnTheme.primary
|
|
: ShadcnTheme.foreground,
|
|
fontWeight: isActive ? FontWeight.w600 : FontWeight.w400,
|
|
),
|
|
),
|
|
),
|
|
if (badge != null) ...[
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 6,
|
|
vertical: 2,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.orange,
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Text(
|
|
badge,
|
|
style: ShadcnTheme.caption.copyWith(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
if (collapsed && badge != null) ...[
|
|
Positioned(
|
|
right: 0,
|
|
top: 0,
|
|
child: Container(
|
|
width: 8,
|
|
height: 8,
|
|
decoration: BoxDecoration(
|
|
color: Colors.orange,
|
|
shape: BoxShape.circle,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (hasSubMenu && subMenuItems.isNotEmpty) ...subMenuItems,
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildSubMenuItem({
|
|
required String title,
|
|
required String route,
|
|
required bool isActive,
|
|
}) {
|
|
return AnimatedContainer(
|
|
duration: const Duration(milliseconds: 200),
|
|
margin: const EdgeInsets.only(left: 40, bottom: 4),
|
|
child: InkWell(
|
|
onTap: () => onRouteChanged(route),
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: ShadcnTheme.spacing3,
|
|
vertical: ShadcnTheme.spacing2,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: isActive
|
|
? ShadcnTheme.primaryLight.withValues(alpha: 0.5)
|
|
: Colors.transparent,
|
|
borderRadius: BorderRadius.circular(ShadcnTheme.radiusMd),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
width: 4,
|
|
height: 4,
|
|
decoration: BoxDecoration(
|
|
color: isActive
|
|
? ShadcnTheme.primary
|
|
: ShadcnTheme.foregroundSecondary,
|
|
shape: BoxShape.circle,
|
|
),
|
|
),
|
|
const SizedBox(width: ShadcnTheme.spacing3),
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: ShadcnTheme.bodySmall.copyWith(
|
|
color: isActive
|
|
? ShadcnTheme.primary
|
|
: ShadcnTheme.foreground,
|
|
fontWeight: isActive ? FontWeight.w600 : FontWeight.w400,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 활성화 상태일 때 채워진 아이콘 반환
|
|
IconData _getFilledIcon(IconData outlinedIcon) {
|
|
switch (outlinedIcon) {
|
|
case Icons.dashboard_outlined:
|
|
return Icons.dashboard;
|
|
case Icons.inventory_2_outlined:
|
|
return Icons.inventory_2;
|
|
case Icons.warehouse_outlined:
|
|
return Icons.warehouse;
|
|
case Icons.location_on_outlined:
|
|
return Icons.location_on;
|
|
case Icons.business_outlined:
|
|
return Icons.business;
|
|
case Icons.people_outlined:
|
|
return Icons.people;
|
|
case Icons.support_outlined:
|
|
return Icons.support;
|
|
case Icons.build_circle_outlined:
|
|
return Icons.build_circle;
|
|
case Icons.bug_report_outlined:
|
|
return Icons.bug_report;
|
|
case Icons.analytics_outlined:
|
|
return Icons.analytics;
|
|
case Icons.factory_outlined:
|
|
return Icons.factory;
|
|
case Icons.category_outlined:
|
|
return Icons.category;
|
|
case Icons.calendar_month_outlined:
|
|
return Icons.calendar_month;
|
|
default:
|
|
return outlinedIcon;
|
|
}
|
|
}
|
|
} |